changed move speed for some elements for older game engine versions
[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 GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebeNachbarNr(int, int);
1096 void AmoebeUmwandeln(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Feld[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Feld[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Feld[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Feld[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       if (options.debug)
1773       {
1774         printf("- player element %d activated", player->element_nr);
1775         printf(" (local player is %d and currently %s)\n",
1776                local_player->element_nr,
1777                local_player->active ? "active" : "not active");
1778       }
1779     }
1780 #endif
1781
1782     Feld[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788   if (!init_game)
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active && player->killed)
1794       player->reanimated = TRUE; // if player was just killed, reanimate him
1795   }
1796 }
1797
1798 static void InitField(int x, int y, boolean init_game)
1799 {
1800   int element = Feld[x][y];
1801
1802   switch (element)
1803   {
1804     case EL_SP_MURPHY:
1805     case EL_PLAYER_1:
1806     case EL_PLAYER_2:
1807     case EL_PLAYER_3:
1808     case EL_PLAYER_4:
1809       InitPlayerField(x, y, element, init_game);
1810       break;
1811
1812     case EL_SOKOBAN_FIELD_PLAYER:
1813       element = Feld[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817       InitField(x, y, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_EMPTY:
1821       IncrementSokobanFieldsNeeded();
1822       break;
1823
1824     case EL_SOKOBAN_OBJECT:
1825       IncrementSokobanObjectsNeeded();
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Feld[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_active)
1967         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_active)
1972         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Feld[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Feld[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Feld[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Feld[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return game_em.ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return game_sp.red_disk_count;
2132   else
2133     return stored_player[player_nr].inventory_size;
2134 }
2135
2136 static void InitGameControlValues(void)
2137 {
2138   int i;
2139
2140   for (i = 0; game_panel_controls[i].nr != -1; i++)
2141   {
2142     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144     struct TextPosInfo *pos = gpc->pos;
2145     int nr = gpc->nr;
2146     int type = gpc->type;
2147
2148     if (nr != i)
2149     {
2150       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2151       Error(ERR_EXIT, "this should not happen -- please debug");
2152     }
2153
2154     // force update of game controls after initialization
2155     gpc->value = gpc->last_value = -1;
2156     gpc->frame = gpc->last_frame = -1;
2157     gpc->gfx_frame = -1;
2158
2159     // determine panel value width for later calculation of alignment
2160     if (type == TYPE_INTEGER || type == TYPE_STRING)
2161     {
2162       pos->width = pos->size * getFontWidth(pos->font);
2163       pos->height = getFontHeight(pos->font);
2164     }
2165     else if (type == TYPE_ELEMENT)
2166     {
2167       pos->width = pos->size;
2168       pos->height = pos->size;
2169     }
2170
2171     // fill structure for game panel draw order
2172     gpo->nr = gpc->nr;
2173     gpo->sort_priority = pos->sort_priority;
2174   }
2175
2176   // sort game panel controls according to sort_priority and control number
2177   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2178         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2179 }
2180
2181 static void UpdatePlayfieldElementCount(void)
2182 {
2183   boolean use_element_count = FALSE;
2184   int i, j, x, y;
2185
2186   // first check if it is needed at all to calculate playfield element count
2187   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2188     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2189       use_element_count = TRUE;
2190
2191   if (!use_element_count)
2192     return;
2193
2194   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2195     element_info[i].element_count = 0;
2196
2197   SCAN_PLAYFIELD(x, y)
2198   {
2199     element_info[Feld[x][y]].element_count++;
2200   }
2201
2202   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2203     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2204       if (IS_IN_GROUP(j, i))
2205         element_info[EL_GROUP_START + i].element_count +=
2206           element_info[j].element_count;
2207 }
2208
2209 static void UpdateGameControlValues(void)
2210 {
2211   int i, k;
2212   int time = (game.LevelSolved ?
2213               game.LevelSolved_CountingTime :
2214               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2215               game_em.lev->time :
2216               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2217               game_sp.time_played :
2218               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219               game_mm.energy_left :
2220               game.no_time_limit ? TimePlayed : TimeLeft);
2221   int score = (game.LevelSolved ?
2222                game.LevelSolved_CountingScore :
2223                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2224                game_em.lev->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2226                game_sp.score :
2227                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2228                game_mm.score :
2229                game.score);
2230   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231               game_em.lev->gems_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2233               game_sp.infotrons_still_needed :
2234               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2235               game_mm.kettles_still_needed :
2236               game.gems_still_needed);
2237   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238                      game_em.lev->gems_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240                      game_sp.infotrons_still_needed > 0 :
2241                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2242                      game_mm.kettles_still_needed > 0 ||
2243                      game_mm.lights_still_needed > 0 :
2244                      game.gems_still_needed > 0 ||
2245                      game.sokoban_fields_still_needed > 0 ||
2246                      game.sokoban_objects_still_needed > 0 ||
2247                      game.lights_still_needed > 0);
2248   int health = (game.LevelSolved ?
2249                 game.LevelSolved_CountingHealth :
2250                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2251                 MM_HEALTH(game_mm.laser_overload_value) :
2252                 game.health);
2253
2254   UpdatePlayfieldElementCount();
2255
2256   // update game panel control values
2257
2258   // used instead of "level_nr" (for network games)
2259   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2260   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2261
2262   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2263   for (i = 0; i < MAX_NUM_KEYS; i++)
2264     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2265   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2266   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2267
2268   if (game.centered_player_nr == -1)
2269   {
2270     for (i = 0; i < MAX_PLAYERS; i++)
2271     {
2272       // only one player in Supaplex game engine
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2274         break;
2275
2276       for (k = 0; k < MAX_NUM_KEYS; k++)
2277       {
2278         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2279         {
2280           if (game_em.ply[i]->keys & (1 << k))
2281             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282               get_key_element_from_nr(k);
2283         }
2284         else if (stored_player[i].key[k])
2285           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286             get_key_element_from_nr(k);
2287       }
2288
2289       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290         getPlayerInventorySize(i);
2291
2292       if (stored_player[i].num_white_keys > 0)
2293         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2294           EL_DC_KEY_WHITE;
2295
2296       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2297         stored_player[i].num_white_keys;
2298     }
2299   }
2300   else
2301   {
2302     int player_nr = game.centered_player_nr;
2303
2304     for (k = 0; k < MAX_NUM_KEYS; k++)
2305     {
2306       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2307       {
2308         if (game_em.ply[player_nr]->keys & (1 << k))
2309           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2310             get_key_element_from_nr(k);
2311       }
2312       else if (stored_player[player_nr].key[k])
2313         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314           get_key_element_from_nr(k);
2315     }
2316
2317     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2318       getPlayerInventorySize(player_nr);
2319
2320     if (stored_player[player_nr].num_white_keys > 0)
2321       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2322
2323     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2324       stored_player[player_nr].num_white_keys;
2325   }
2326
2327   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, i);
2331     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2332       get_inventory_element_from_pos(local_player, -i - 1);
2333   }
2334
2335   game_panel_controls[GAME_PANEL_SCORE].value = score;
2336   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2337
2338   game_panel_controls[GAME_PANEL_TIME].value = time;
2339
2340   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2341   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2342   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2343
2344   if (level.time == 0)
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2346   else
2347     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2348
2349   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2350   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2351
2352   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2353
2354   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2355     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2356      EL_EMPTY);
2357   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2358     local_player->shield_normal_time_left;
2359   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2360     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2361      EL_EMPTY);
2362   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2363     local_player->shield_deadly_time_left;
2364
2365   game_panel_controls[GAME_PANEL_EXIT].value =
2366     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2367
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2369     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2370   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2371     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2372      EL_EMC_MAGIC_BALL_SWITCH);
2373
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2375     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2376   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2377     game.light_time_left;
2378
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2380     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2381   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2382     game.timegate_time_left;
2383
2384   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2385     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2386
2387   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2388     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2389   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2390     game.lenses_time_left;
2391
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2393     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2394   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2395     game.magnify_time_left;
2396
2397   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2398     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2399      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2400      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2401      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2402      EL_BALLOON_SWITCH_NONE);
2403
2404   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2405     local_player->dynabomb_count;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2407     local_player->dynabomb_size;
2408   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2409     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2410
2411   game_panel_controls[GAME_PANEL_PENGUINS].value =
2412     game.friends_still_needed;
2413
2414   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2415     game.sokoban_objects_still_needed;
2416   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2417     game.sokoban_fields_still_needed;
2418
2419   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2420     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2421
2422   for (i = 0; i < NUM_BELTS; i++)
2423   {
2424     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2425       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2426        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2427     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2428       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2429   }
2430
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2432     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2433   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2434     game.magic_wall_time_left;
2435
2436   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2437     local_player->gravity;
2438
2439   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2440     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2441
2442   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2443     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2444       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2445        game.panel.element[i].id : EL_UNDEFINED);
2446
2447   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2448     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2449       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2450        element_info[game.panel.element_count[i].id].element_count : 0);
2451
2452   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2453     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2454       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2455        element_info[game.panel.ce_score[i].id].collect_score : 0);
2456
2457   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2458     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2459       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2460        element_info[game.panel.ce_score_element[i].id].collect_score :
2461        EL_UNDEFINED);
2462
2463   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2464   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2465   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2466
2467   // update game panel control frames
2468
2469   for (i = 0; game_panel_controls[i].nr != -1; i++)
2470   {
2471     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2472
2473     if (gpc->type == TYPE_ELEMENT)
2474     {
2475       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2476       {
2477         int last_anim_random_frame = gfx.anim_random_frame;
2478         int element = gpc->value;
2479         int graphic = el2panelimg(element);
2480
2481         if (gpc->value != gpc->last_value)
2482         {
2483           gpc->gfx_frame = 0;
2484           gpc->gfx_random = INIT_GFX_RANDOM();
2485         }
2486         else
2487         {
2488           gpc->gfx_frame++;
2489
2490           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2491               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2492             gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494
2495         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496           gfx.anim_random_frame = gpc->gfx_random;
2497
2498         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2499           gpc->gfx_frame = element_info[element].collect_score;
2500
2501         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2502                                               gpc->gfx_frame);
2503
2504         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2505           gfx.anim_random_frame = last_anim_random_frame;
2506       }
2507     }
2508     else if (gpc->type == TYPE_GRAPHIC)
2509     {
2510       if (gpc->graphic != IMG_UNDEFINED)
2511       {
2512         int last_anim_random_frame = gfx.anim_random_frame;
2513         int graphic = gpc->graphic;
2514
2515         if (gpc->value != gpc->last_value)
2516         {
2517           gpc->gfx_frame = 0;
2518           gpc->gfx_random = INIT_GFX_RANDOM();
2519         }
2520         else
2521         {
2522           gpc->gfx_frame++;
2523
2524           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2525               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2526             gpc->gfx_random = INIT_GFX_RANDOM();
2527         }
2528
2529         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2530           gfx.anim_random_frame = gpc->gfx_random;
2531
2532         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2533
2534         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2535           gfx.anim_random_frame = last_anim_random_frame;
2536       }
2537     }
2538   }
2539 }
2540
2541 static void DisplayGameControlValues(void)
2542 {
2543   boolean redraw_panel = FALSE;
2544   int i;
2545
2546   for (i = 0; game_panel_controls[i].nr != -1; i++)
2547   {
2548     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2549
2550     if (PANEL_DEACTIVATED(gpc->pos))
2551       continue;
2552
2553     if (gpc->value == gpc->last_value &&
2554         gpc->frame == gpc->last_frame)
2555       continue;
2556
2557     redraw_panel = TRUE;
2558   }
2559
2560   if (!redraw_panel)
2561     return;
2562
2563   // copy default game door content to main double buffer
2564
2565   // !!! CHECK AGAIN !!!
2566   SetPanelBackground();
2567   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2568   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2569
2570   // redraw game control buttons
2571   RedrawGameButtons();
2572
2573   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2574
2575   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2576   {
2577     int nr = game_panel_order[i].nr;
2578     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2579     struct TextPosInfo *pos = gpc->pos;
2580     int type = gpc->type;
2581     int value = gpc->value;
2582     int frame = gpc->frame;
2583     int size = pos->size;
2584     int font = pos->font;
2585     boolean draw_masked = pos->draw_masked;
2586     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2587
2588     if (PANEL_DEACTIVATED(pos))
2589       continue;
2590
2591     gpc->last_value = value;
2592     gpc->last_frame = frame;
2593
2594     if (type == TYPE_INTEGER)
2595     {
2596       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2597           nr == GAME_PANEL_TIME)
2598       {
2599         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2600
2601         if (use_dynamic_size)           // use dynamic number of digits
2602         {
2603           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2604           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2605           int size2 = size1 + 1;
2606           int font1 = pos->font;
2607           int font2 = pos->font_alt;
2608
2609           size = (value < value_change ? size1 : size2);
2610           font = (value < value_change ? font1 : font2);
2611         }
2612       }
2613
2614       // correct text size if "digits" is zero or less
2615       if (size <= 0)
2616         size = strlen(int2str(value, size));
2617
2618       // dynamically correct text alignment
2619       pos->width = size * getFontWidth(font);
2620
2621       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2622                   int2str(value, size), font, mask_mode);
2623     }
2624     else if (type == TYPE_ELEMENT)
2625     {
2626       int element, graphic;
2627       Bitmap *src_bitmap;
2628       int src_x, src_y;
2629       int width, height;
2630       int dst_x = PANEL_XPOS(pos);
2631       int dst_y = PANEL_YPOS(pos);
2632
2633       if (value != EL_UNDEFINED && value != EL_EMPTY)
2634       {
2635         element = value;
2636         graphic = el2panelimg(value);
2637
2638         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2639
2640         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2641           size = TILESIZE;
2642
2643         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2644                               &src_x, &src_y);
2645
2646         width  = graphic_info[graphic].width  * size / TILESIZE;
2647         height = graphic_info[graphic].height * size / TILESIZE;
2648
2649         if (draw_masked)
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         else
2653           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2654                      dst_x, dst_y);
2655       }
2656     }
2657     else if (type == TYPE_GRAPHIC)
2658     {
2659       int graphic        = gpc->graphic;
2660       int graphic_active = gpc->graphic_active;
2661       Bitmap *src_bitmap;
2662       int src_x, src_y;
2663       int width, height;
2664       int dst_x = PANEL_XPOS(pos);
2665       int dst_y = PANEL_YPOS(pos);
2666       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2667                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2668
2669       if (graphic != IMG_UNDEFINED && !skip)
2670       {
2671         if (pos->style == STYLE_REVERSE)
2672           value = 100 - value;
2673
2674         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2675
2676         if (pos->direction & MV_HORIZONTAL)
2677         {
2678           width  = graphic_info[graphic_active].width * value / 100;
2679           height = graphic_info[graphic_active].height;
2680
2681           if (pos->direction == MV_LEFT)
2682           {
2683             src_x += graphic_info[graphic_active].width - width;
2684             dst_x += graphic_info[graphic_active].width - width;
2685           }
2686         }
2687         else
2688         {
2689           width  = graphic_info[graphic_active].width;
2690           height = graphic_info[graphic_active].height * value / 100;
2691
2692           if (pos->direction == MV_UP)
2693           {
2694             src_y += graphic_info[graphic_active].height - height;
2695             dst_y += graphic_info[graphic_active].height - height;
2696           }
2697         }
2698
2699         if (draw_masked)
2700           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2701                            dst_x, dst_y);
2702         else
2703           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2704                      dst_x, dst_y);
2705
2706         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2707
2708         if (pos->direction & MV_HORIZONTAL)
2709         {
2710           if (pos->direction == MV_RIGHT)
2711           {
2712             src_x += width;
2713             dst_x += width;
2714           }
2715           else
2716           {
2717             dst_x = PANEL_XPOS(pos);
2718           }
2719
2720           width = graphic_info[graphic].width - width;
2721         }
2722         else
2723         {
2724           if (pos->direction == MV_DOWN)
2725           {
2726             src_y += height;
2727             dst_y += height;
2728           }
2729           else
2730           {
2731             dst_y = PANEL_YPOS(pos);
2732           }
2733
2734           height = graphic_info[graphic].height - height;
2735         }
2736
2737         if (draw_masked)
2738           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2739                            dst_x, dst_y);
2740         else
2741           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2742                      dst_x, dst_y);
2743       }
2744     }
2745     else if (type == TYPE_STRING)
2746     {
2747       boolean active = (value != 0);
2748       char *state_normal = "off";
2749       char *state_active = "on";
2750       char *state = (active ? state_active : state_normal);
2751       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2752                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2753                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2754                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2755
2756       if (nr == GAME_PANEL_GRAVITY_STATE)
2757       {
2758         int font1 = pos->font;          // (used for normal state)
2759         int font2 = pos->font_alt;      // (used for active state)
2760
2761         font = (active ? font2 : font1);
2762       }
2763
2764       if (s != NULL)
2765       {
2766         char *s_cut;
2767
2768         if (size <= 0)
2769         {
2770           // don't truncate output if "chars" is zero or less
2771           size = strlen(s);
2772
2773           // dynamically correct text alignment
2774           pos->width = size * getFontWidth(font);
2775         }
2776
2777         s_cut = getStringCopyN(s, size);
2778
2779         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2780                     s_cut, font, mask_mode);
2781
2782         free(s_cut);
2783       }
2784     }
2785
2786     redraw_mask |= REDRAW_DOOR_1;
2787   }
2788
2789   SetGameStatus(GAME_MODE_PLAYING);
2790 }
2791
2792 void UpdateAndDisplayGameControlValues(void)
2793 {
2794   if (tape.deactivate_display)
2795     return;
2796
2797   UpdateGameControlValues();
2798   DisplayGameControlValues();
2799 }
2800
2801 #if 0
2802 static void UpdateGameDoorValues(void)
2803 {
2804   UpdateGameControlValues();
2805 }
2806 #endif
2807
2808 void DrawGameDoorValues(void)
2809 {
2810   DisplayGameControlValues();
2811 }
2812
2813
2814 // ============================================================================
2815 // InitGameEngine()
2816 // ----------------------------------------------------------------------------
2817 // initialize game engine due to level / tape version number
2818 // ============================================================================
2819
2820 static void InitGameEngine(void)
2821 {
2822   int i, j, k, l, x, y;
2823
2824   // set game engine from tape file when re-playing, else from level file
2825   game.engine_version = (tape.playing ? tape.engine_version :
2826                          level.game_version);
2827
2828   // set single or multi-player game mode (needed for re-playing tapes)
2829   game.team_mode = setup.team_mode;
2830
2831   if (tape.playing)
2832   {
2833     int num_players = 0;
2834
2835     for (i = 0; i < MAX_PLAYERS; i++)
2836       if (tape.player_participates[i])
2837         num_players++;
2838
2839     // multi-player tapes contain input data for more than one player
2840     game.team_mode = (num_players > 1);
2841   }
2842
2843   // --------------------------------------------------------------------------
2844   // set flags for bugs and changes according to active game engine version
2845   // --------------------------------------------------------------------------
2846
2847   /*
2848     Summary of bugfix/change:
2849     Fixed move speed of elements entering or leaving magic wall.
2850
2851     Fixed/changed in version:
2852     2.0.1
2853
2854     Description:
2855     Before 2.0.1, move speed of elements entering or leaving magic wall was
2856     twice as fast as it is now.
2857     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2858
2859     Affected levels/tapes:
2860     The first condition is generally needed for all levels/tapes before version
2861     2.0.1, which might use the old behaviour before it was changed; known tapes
2862     that are affected: Tape 014 from the level set "rnd_conor_mancone".
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 2.0.1 or
2865     above, but before it was known that this change would break tapes like the
2866     above and was fixed in 4.1.4.2, so that the changed behaviour was active
2867     although the engine version while recording maybe was before 2.0.1. There
2868     are a lot of tapes that are affected by this exception, like tape 006 from
2869     the level set "rnd_conor_mancone".
2870   */
2871
2872   boolean use_old_move_stepsize_for_magic_wall =
2873     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2874      !(tape.playing &&
2875        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2876        tape.game_version <  VERSION_IDENT(4,1,4,2)));
2877
2878   /*
2879     Summary of bugfix/change:
2880     Fixed handling for custom elements that change when pushed by the player.
2881
2882     Fixed/changed in version:
2883     3.1.0
2884
2885     Description:
2886     Before 3.1.0, custom elements that "change when pushing" changed directly
2887     after the player started pushing them (until then handled in "DigField()").
2888     Since 3.1.0, these custom elements are not changed until the "pushing"
2889     move of the element is finished (now handled in "ContinueMoving()").
2890
2891     Affected levels/tapes:
2892     The first condition is generally needed for all levels/tapes before version
2893     3.1.0, which might use the old behaviour before it was changed; known tapes
2894     that are affected are some tapes from the level set "Walpurgis Gardens" by
2895     Jamie Cullen.
2896     The second condition is an exception from the above case and is needed for
2897     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2898     above (including some development versions of 3.1.0), but before it was
2899     known that this change would break tapes like the above and was fixed in
2900     3.1.1, so that the changed behaviour was active although the engine version
2901     while recording maybe was before 3.1.0. There is at least one tape that is
2902     affected by this exception, which is the tape for the one-level set "Bug
2903     Machine" by Juergen Bonhagen.
2904   */
2905
2906   game.use_change_when_pushing_bug =
2907     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2908      !(tape.playing &&
2909        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2910        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2911
2912   /*
2913     Summary of bugfix/change:
2914     Fixed handling for blocking the field the player leaves when moving.
2915
2916     Fixed/changed in version:
2917     3.1.1
2918
2919     Description:
2920     Before 3.1.1, when "block last field when moving" was enabled, the field
2921     the player is leaving when moving was blocked for the time of the move,
2922     and was directly unblocked afterwards. This resulted in the last field
2923     being blocked for exactly one less than the number of frames of one player
2924     move. Additionally, even when blocking was disabled, the last field was
2925     blocked for exactly one frame.
2926     Since 3.1.1, due to changes in player movement handling, the last field
2927     is not blocked at all when blocking is disabled. When blocking is enabled,
2928     the last field is blocked for exactly the number of frames of one player
2929     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2930     last field is blocked for exactly one more than the number of frames of
2931     one player move.
2932
2933     Affected levels/tapes:
2934     (!!! yet to be determined -- probably many !!!)
2935   */
2936
2937   game.use_block_last_field_bug =
2938     (game.engine_version < VERSION_IDENT(3,1,1,0));
2939
2940   /* various special flags and settings for native Emerald Mine game engine */
2941
2942   game_em.use_single_button =
2943     (game.engine_version > VERSION_IDENT(4,0,0,2));
2944
2945   game_em.use_snap_key_bug =
2946     (game.engine_version < VERSION_IDENT(4,0,1,0));
2947
2948   game_em.use_old_explosions =
2949     (game.engine_version < VERSION_IDENT(4,1,4,2));
2950
2951   // --------------------------------------------------------------------------
2952
2953   // set maximal allowed number of custom element changes per game frame
2954   game.max_num_changes_per_frame = 1;
2955
2956   // default scan direction: scan playfield from top/left to bottom/right
2957   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2958
2959   // dynamically adjust element properties according to game engine version
2960   InitElementPropertiesEngine(game.engine_version);
2961
2962 #if 0
2963   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2964   printf("          tape version == %06d [%s] [file: %06d]\n",
2965          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2966          tape.file_version);
2967   printf("       => game.engine_version == %06d\n", game.engine_version);
2968 #endif
2969
2970   // ---------- initialize player's initial move delay ------------------------
2971
2972   // dynamically adjust player properties according to level information
2973   for (i = 0; i < MAX_PLAYERS; i++)
2974     game.initial_move_delay_value[i] =
2975       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2976
2977   // dynamically adjust player properties according to game engine version
2978   for (i = 0; i < MAX_PLAYERS; i++)
2979     game.initial_move_delay[i] =
2980       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2981        game.initial_move_delay_value[i] : 0);
2982
2983   // ---------- initialize player's initial push delay ------------------------
2984
2985   // dynamically adjust player properties according to game engine version
2986   game.initial_push_delay_value =
2987     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2988
2989   // ---------- initialize changing elements ----------------------------------
2990
2991   // initialize changing elements information
2992   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2993   {
2994     struct ElementInfo *ei = &element_info[i];
2995
2996     // this pointer might have been changed in the level editor
2997     ei->change = &ei->change_page[0];
2998
2999     if (!IS_CUSTOM_ELEMENT(i))
3000     {
3001       ei->change->target_element = EL_EMPTY_SPACE;
3002       ei->change->delay_fixed = 0;
3003       ei->change->delay_random = 0;
3004       ei->change->delay_frames = 1;
3005     }
3006
3007     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3008     {
3009       ei->has_change_event[j] = FALSE;
3010
3011       ei->event_page_nr[j] = 0;
3012       ei->event_page[j] = &ei->change_page[0];
3013     }
3014   }
3015
3016   // add changing elements from pre-defined list
3017   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3018   {
3019     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3020     struct ElementInfo *ei = &element_info[ch_delay->element];
3021
3022     ei->change->target_element       = ch_delay->target_element;
3023     ei->change->delay_fixed          = ch_delay->change_delay;
3024
3025     ei->change->pre_change_function  = ch_delay->pre_change_function;
3026     ei->change->change_function      = ch_delay->change_function;
3027     ei->change->post_change_function = ch_delay->post_change_function;
3028
3029     ei->change->can_change = TRUE;
3030     ei->change->can_change_or_has_action = TRUE;
3031
3032     ei->has_change_event[CE_DELAY] = TRUE;
3033
3034     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3035     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3036   }
3037
3038   // ---------- initialize internal run-time variables ------------------------
3039
3040   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3041   {
3042     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3043
3044     for (j = 0; j < ei->num_change_pages; j++)
3045     {
3046       ei->change_page[j].can_change_or_has_action =
3047         (ei->change_page[j].can_change |
3048          ei->change_page[j].has_action);
3049     }
3050   }
3051
3052   // add change events from custom element configuration
3053   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3054   {
3055     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3056
3057     for (j = 0; j < ei->num_change_pages; j++)
3058     {
3059       if (!ei->change_page[j].can_change_or_has_action)
3060         continue;
3061
3062       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3063       {
3064         // only add event page for the first page found with this event
3065         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3066         {
3067           ei->has_change_event[k] = TRUE;
3068
3069           ei->event_page_nr[k] = j;
3070           ei->event_page[k] = &ei->change_page[j];
3071         }
3072       }
3073     }
3074   }
3075
3076   // ---------- initialize reference elements in change conditions ------------
3077
3078   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3079   {
3080     int element = EL_CUSTOM_START + i;
3081     struct ElementInfo *ei = &element_info[element];
3082
3083     for (j = 0; j < ei->num_change_pages; j++)
3084     {
3085       int trigger_element = ei->change_page[j].initial_trigger_element;
3086
3087       if (trigger_element >= EL_PREV_CE_8 &&
3088           trigger_element <= EL_NEXT_CE_8)
3089         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3090
3091       ei->change_page[j].trigger_element = trigger_element;
3092     }
3093   }
3094
3095   // ---------- initialize run-time trigger player and element ----------------
3096
3097   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3098   {
3099     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3100
3101     for (j = 0; j < ei->num_change_pages; j++)
3102     {
3103       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3104       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3105       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3106       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3107       ei->change_page[j].actual_trigger_ce_value = 0;
3108       ei->change_page[j].actual_trigger_ce_score = 0;
3109     }
3110   }
3111
3112   // ---------- initialize trigger events -------------------------------------
3113
3114   // initialize trigger events information
3115   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3116     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3117       trigger_events[i][j] = FALSE;
3118
3119   // add trigger events from element change event properties
3120   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3121   {
3122     struct ElementInfo *ei = &element_info[i];
3123
3124     for (j = 0; j < ei->num_change_pages; j++)
3125     {
3126       if (!ei->change_page[j].can_change_or_has_action)
3127         continue;
3128
3129       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3130       {
3131         int trigger_element = ei->change_page[j].trigger_element;
3132
3133         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3134         {
3135           if (ei->change_page[j].has_event[k])
3136           {
3137             if (IS_GROUP_ELEMENT(trigger_element))
3138             {
3139               struct ElementGroupInfo *group =
3140                 element_info[trigger_element].group;
3141
3142               for (l = 0; l < group->num_elements_resolved; l++)
3143                 trigger_events[group->element_resolved[l]][k] = TRUE;
3144             }
3145             else if (trigger_element == EL_ANY_ELEMENT)
3146               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3147                 trigger_events[l][k] = TRUE;
3148             else
3149               trigger_events[trigger_element][k] = TRUE;
3150           }
3151         }
3152       }
3153     }
3154   }
3155
3156   // ---------- initialize push delay -----------------------------------------
3157
3158   // initialize push delay values to default
3159   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3160   {
3161     if (!IS_CUSTOM_ELEMENT(i))
3162     {
3163       // set default push delay values (corrected since version 3.0.7-1)
3164       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3165       {
3166         element_info[i].push_delay_fixed = 2;
3167         element_info[i].push_delay_random = 8;
3168       }
3169       else
3170       {
3171         element_info[i].push_delay_fixed = 8;
3172         element_info[i].push_delay_random = 8;
3173       }
3174     }
3175   }
3176
3177   // set push delay value for certain elements from pre-defined list
3178   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3179   {
3180     int e = push_delay_list[i].element;
3181
3182     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3183     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3184   }
3185
3186   // set push delay value for Supaplex elements for newer engine versions
3187   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3188   {
3189     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3190     {
3191       if (IS_SP_ELEMENT(i))
3192       {
3193         // set SP push delay to just enough to push under a falling zonk
3194         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3195
3196         element_info[i].push_delay_fixed  = delay;
3197         element_info[i].push_delay_random = 0;
3198       }
3199     }
3200   }
3201
3202   // ---------- initialize move stepsize --------------------------------------
3203
3204   // initialize move stepsize values to default
3205   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3206     if (!IS_CUSTOM_ELEMENT(i))
3207       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3208
3209   // set move stepsize value for certain elements from pre-defined list
3210   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3211   {
3212     int e = move_stepsize_list[i].element;
3213
3214     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3215
3216     // set move stepsize value for certain elements for older engine versions
3217     if (use_old_move_stepsize_for_magic_wall)
3218     {
3219       if (e == EL_MAGIC_WALL_FILLING ||
3220           e == EL_MAGIC_WALL_EMPTYING ||
3221           e == EL_BD_MAGIC_WALL_FILLING ||
3222           e == EL_BD_MAGIC_WALL_EMPTYING)
3223         element_info[e].move_stepsize *= 2;
3224     }
3225   }
3226
3227   // ---------- initialize collect score --------------------------------------
3228
3229   // initialize collect score values for custom elements from initial value
3230   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3231     if (IS_CUSTOM_ELEMENT(i))
3232       element_info[i].collect_score = element_info[i].collect_score_initial;
3233
3234   // ---------- initialize collect count --------------------------------------
3235
3236   // initialize collect count values for non-custom elements
3237   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3238     if (!IS_CUSTOM_ELEMENT(i))
3239       element_info[i].collect_count_initial = 0;
3240
3241   // add collect count values for all elements from pre-defined list
3242   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3243     element_info[collect_count_list[i].element].collect_count_initial =
3244       collect_count_list[i].count;
3245
3246   // ---------- initialize access direction -----------------------------------
3247
3248   // initialize access direction values to default (access from every side)
3249   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3250     if (!IS_CUSTOM_ELEMENT(i))
3251       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3252
3253   // set access direction value for certain elements from pre-defined list
3254   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3255     element_info[access_direction_list[i].element].access_direction =
3256       access_direction_list[i].direction;
3257
3258   // ---------- initialize explosion content ----------------------------------
3259   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3260   {
3261     if (IS_CUSTOM_ELEMENT(i))
3262       continue;
3263
3264     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3265     {
3266       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3267
3268       element_info[i].content.e[x][y] =
3269         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3270          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3271          i == EL_PLAYER_3 ? EL_EMERALD :
3272          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3273          i == EL_MOLE ? EL_EMERALD_RED :
3274          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3275          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3276          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3277          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3278          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3279          i == EL_WALL_EMERALD ? EL_EMERALD :
3280          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3281          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3282          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3283          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3284          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3285          i == EL_WALL_PEARL ? EL_PEARL :
3286          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3287          EL_EMPTY);
3288     }
3289   }
3290
3291   // ---------- initialize recursion detection --------------------------------
3292   recursion_loop_depth = 0;
3293   recursion_loop_detected = FALSE;
3294   recursion_loop_element = EL_UNDEFINED;
3295
3296   // ---------- initialize graphics engine ------------------------------------
3297   game.scroll_delay_value =
3298     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3299      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3300      !setup.forced_scroll_delay           ? 0 :
3301      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3302   game.scroll_delay_value =
3303     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3304
3305   // ---------- initialize game engine snapshots ------------------------------
3306   for (i = 0; i < MAX_PLAYERS; i++)
3307     game.snapshot.last_action[i] = 0;
3308   game.snapshot.changed_action = FALSE;
3309   game.snapshot.collected_item = FALSE;
3310   game.snapshot.mode =
3311     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3312      SNAPSHOT_MODE_EVERY_STEP :
3313      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3314      SNAPSHOT_MODE_EVERY_MOVE :
3315      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3316      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3317   game.snapshot.save_snapshot = FALSE;
3318
3319   // ---------- initialize level time for Supaplex engine ---------------------
3320   // Supaplex levels with time limit currently unsupported -- should be added
3321   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3322     level.time = 0;
3323
3324   // ---------- initialize flags for handling game actions --------------------
3325
3326   // set flags for game actions to default values
3327   game.use_key_actions = TRUE;
3328   game.use_mouse_actions = FALSE;
3329
3330   // when using Mirror Magic game engine, handle mouse events only
3331   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3332   {
3333     game.use_key_actions = FALSE;
3334     game.use_mouse_actions = TRUE;
3335   }
3336
3337   // check for custom elements with mouse click events
3338   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3339   {
3340     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3341     {
3342       int element = EL_CUSTOM_START + i;
3343
3344       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3345           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3346           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3347           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3348         game.use_mouse_actions = TRUE;
3349     }
3350   }
3351 }
3352
3353 static int get_num_special_action(int element, int action_first,
3354                                   int action_last)
3355 {
3356   int num_special_action = 0;
3357   int i, j;
3358
3359   for (i = action_first; i <= action_last; i++)
3360   {
3361     boolean found = FALSE;
3362
3363     for (j = 0; j < NUM_DIRECTIONS; j++)
3364       if (el_act_dir2img(element, i, j) !=
3365           el_act_dir2img(element, ACTION_DEFAULT, j))
3366         found = TRUE;
3367
3368     if (found)
3369       num_special_action++;
3370     else
3371       break;
3372   }
3373
3374   return num_special_action;
3375 }
3376
3377
3378 // ============================================================================
3379 // InitGame()
3380 // ----------------------------------------------------------------------------
3381 // initialize and start new game
3382 // ============================================================================
3383
3384 #if DEBUG_INIT_PLAYER
3385 static void DebugPrintPlayerStatus(char *message)
3386 {
3387   int i;
3388
3389   if (!options.debug)
3390     return;
3391
3392   printf("%s:\n", message);
3393
3394   for (i = 0; i < MAX_PLAYERS; i++)
3395   {
3396     struct PlayerInfo *player = &stored_player[i];
3397
3398     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3399            i + 1,
3400            player->present,
3401            player->connected,
3402            player->connected_locally,
3403            player->connected_network,
3404            player->active);
3405
3406     if (local_player == player)
3407       printf(" (local player)");
3408
3409     printf("\n");
3410   }
3411 }
3412 #endif
3413
3414 void InitGame(void)
3415 {
3416   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3417   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3418   int fade_mask = REDRAW_FIELD;
3419
3420   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3421   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3422   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3423   int initial_move_dir = MV_DOWN;
3424   int i, j, x, y;
3425
3426   // required here to update video display before fading (FIX THIS)
3427   DrawMaskedBorder(REDRAW_DOOR_2);
3428
3429   if (!game.restart_level)
3430     CloseDoor(DOOR_CLOSE_1);
3431
3432   SetGameStatus(GAME_MODE_PLAYING);
3433
3434   if (level_editor_test_game)
3435     FadeSkipNextFadeOut();
3436   else
3437     FadeSetEnterScreen();
3438
3439   if (CheckFadeAll())
3440     fade_mask = REDRAW_ALL;
3441
3442   FadeLevelSoundsAndMusic();
3443
3444   ExpireSoundLoops(TRUE);
3445
3446   FadeOut(fade_mask);
3447
3448   if (level_editor_test_game)
3449     FadeSkipNextFadeIn();
3450
3451   // needed if different viewport properties defined for playing
3452   ChangeViewportPropertiesIfNeeded();
3453
3454   ClearField();
3455
3456   DrawCompleteVideoDisplay();
3457
3458   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3459
3460   InitGameEngine();
3461   InitGameControlValues();
3462
3463   // initialize tape actions from game when recording tape
3464   if (tape.recording)
3465   {
3466     tape.use_key_actions   = game.use_key_actions;
3467     tape.use_mouse_actions = game.use_mouse_actions;
3468   }
3469
3470   // don't play tapes over network
3471   network_playing = (network.enabled && !tape.playing);
3472
3473   for (i = 0; i < MAX_PLAYERS; i++)
3474   {
3475     struct PlayerInfo *player = &stored_player[i];
3476
3477     player->index_nr = i;
3478     player->index_bit = (1 << i);
3479     player->element_nr = EL_PLAYER_1 + i;
3480
3481     player->present = FALSE;
3482     player->active = FALSE;
3483     player->mapped = FALSE;
3484
3485     player->killed = FALSE;
3486     player->reanimated = FALSE;
3487     player->buried = FALSE;
3488
3489     player->action = 0;
3490     player->effective_action = 0;
3491     player->programmed_action = 0;
3492     player->snap_action = 0;
3493
3494     player->mouse_action.lx = 0;
3495     player->mouse_action.ly = 0;
3496     player->mouse_action.button = 0;
3497     player->mouse_action.button_hint = 0;
3498
3499     player->effective_mouse_action.lx = 0;
3500     player->effective_mouse_action.ly = 0;
3501     player->effective_mouse_action.button = 0;
3502     player->effective_mouse_action.button_hint = 0;
3503
3504     for (j = 0; j < MAX_NUM_KEYS; j++)
3505       player->key[j] = FALSE;
3506
3507     player->num_white_keys = 0;
3508
3509     player->dynabomb_count = 0;
3510     player->dynabomb_size = 1;
3511     player->dynabombs_left = 0;
3512     player->dynabomb_xl = FALSE;
3513
3514     player->MovDir = initial_move_dir;
3515     player->MovPos = 0;
3516     player->GfxPos = 0;
3517     player->GfxDir = initial_move_dir;
3518     player->GfxAction = ACTION_DEFAULT;
3519     player->Frame = 0;
3520     player->StepFrame = 0;
3521
3522     player->initial_element = player->element_nr;
3523     player->artwork_element =
3524       (level.use_artwork_element[i] ? level.artwork_element[i] :
3525        player->element_nr);
3526     player->use_murphy = FALSE;
3527
3528     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3529     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3530
3531     player->gravity = level.initial_player_gravity[i];
3532
3533     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3534
3535     player->actual_frame_counter = 0;
3536
3537     player->step_counter = 0;
3538
3539     player->last_move_dir = initial_move_dir;
3540
3541     player->is_active = FALSE;
3542
3543     player->is_waiting = FALSE;
3544     player->is_moving = FALSE;
3545     player->is_auto_moving = FALSE;
3546     player->is_digging = FALSE;
3547     player->is_snapping = FALSE;
3548     player->is_collecting = FALSE;
3549     player->is_pushing = FALSE;
3550     player->is_switching = FALSE;
3551     player->is_dropping = FALSE;
3552     player->is_dropping_pressed = FALSE;
3553
3554     player->is_bored = FALSE;
3555     player->is_sleeping = FALSE;
3556
3557     player->was_waiting = TRUE;
3558     player->was_moving = FALSE;
3559     player->was_snapping = FALSE;
3560     player->was_dropping = FALSE;
3561
3562     player->force_dropping = FALSE;
3563
3564     player->frame_counter_bored = -1;
3565     player->frame_counter_sleeping = -1;
3566
3567     player->anim_delay_counter = 0;
3568     player->post_delay_counter = 0;
3569
3570     player->dir_waiting = initial_move_dir;
3571     player->action_waiting = ACTION_DEFAULT;
3572     player->last_action_waiting = ACTION_DEFAULT;
3573     player->special_action_bored = ACTION_DEFAULT;
3574     player->special_action_sleeping = ACTION_DEFAULT;
3575
3576     player->switch_x = -1;
3577     player->switch_y = -1;
3578
3579     player->drop_x = -1;
3580     player->drop_y = -1;
3581
3582     player->show_envelope = 0;
3583
3584     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3585
3586     player->push_delay       = -1;      // initialized when pushing starts
3587     player->push_delay_value = game.initial_push_delay_value;
3588
3589     player->drop_delay = 0;
3590     player->drop_pressed_delay = 0;
3591
3592     player->last_jx = -1;
3593     player->last_jy = -1;
3594     player->jx = -1;
3595     player->jy = -1;
3596
3597     player->shield_normal_time_left = 0;
3598     player->shield_deadly_time_left = 0;
3599
3600     player->inventory_infinite_element = EL_UNDEFINED;
3601     player->inventory_size = 0;
3602
3603     if (level.use_initial_inventory[i])
3604     {
3605       for (j = 0; j < level.initial_inventory_size[i]; j++)
3606       {
3607         int element = level.initial_inventory_content[i][j];
3608         int collect_count = element_info[element].collect_count_initial;
3609         int k;
3610
3611         if (!IS_CUSTOM_ELEMENT(element))
3612           collect_count = 1;
3613
3614         if (collect_count == 0)
3615           player->inventory_infinite_element = element;
3616         else
3617           for (k = 0; k < collect_count; k++)
3618             if (player->inventory_size < MAX_INVENTORY_SIZE)
3619               player->inventory_element[player->inventory_size++] = element;
3620       }
3621     }
3622
3623     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3624     SnapField(player, 0, 0);
3625
3626     map_player_action[i] = i;
3627   }
3628
3629   network_player_action_received = FALSE;
3630
3631   // initial null action
3632   if (network_playing)
3633     SendToServer_MovePlayer(MV_NONE);
3634
3635   FrameCounter = 0;
3636   TimeFrames = 0;
3637   TimePlayed = 0;
3638   TimeLeft = level.time;
3639   TapeTime = 0;
3640
3641   ScreenMovDir = MV_NONE;
3642   ScreenMovPos = 0;
3643   ScreenGfxPos = 0;
3644
3645   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3646
3647   game.robot_wheel_x = -1;
3648   game.robot_wheel_y = -1;
3649
3650   game.exit_x = -1;
3651   game.exit_y = -1;
3652
3653   game.all_players_gone = FALSE;
3654
3655   game.LevelSolved = FALSE;
3656   game.GameOver = FALSE;
3657
3658   game.GamePlayed = !tape.playing;
3659
3660   game.LevelSolved_GameWon = FALSE;
3661   game.LevelSolved_GameEnd = FALSE;
3662   game.LevelSolved_SaveTape = FALSE;
3663   game.LevelSolved_SaveScore = FALSE;
3664
3665   game.LevelSolved_CountingTime = 0;
3666   game.LevelSolved_CountingScore = 0;
3667   game.LevelSolved_CountingHealth = 0;
3668
3669   game.panel.active = TRUE;
3670
3671   game.no_time_limit = (level.time == 0);
3672
3673   game.yamyam_content_nr = 0;
3674   game.robot_wheel_active = FALSE;
3675   game.magic_wall_active = FALSE;
3676   game.magic_wall_time_left = 0;
3677   game.light_time_left = 0;
3678   game.timegate_time_left = 0;
3679   game.switchgate_pos = 0;
3680   game.wind_direction = level.wind_direction_initial;
3681
3682   game.score = 0;
3683   game.score_final = 0;
3684
3685   game.health = MAX_HEALTH;
3686   game.health_final = MAX_HEALTH;
3687
3688   game.gems_still_needed = level.gems_needed;
3689   game.sokoban_fields_still_needed = 0;
3690   game.sokoban_objects_still_needed = 0;
3691   game.lights_still_needed = 0;
3692   game.players_still_needed = 0;
3693   game.friends_still_needed = 0;
3694
3695   game.lenses_time_left = 0;
3696   game.magnify_time_left = 0;
3697
3698   game.ball_active = level.ball_active_initial;
3699   game.ball_content_nr = 0;
3700
3701   game.explosions_delayed = TRUE;
3702
3703   game.envelope_active = FALSE;
3704
3705   for (i = 0; i < NUM_BELTS; i++)
3706   {
3707     game.belt_dir[i] = MV_NONE;
3708     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3709   }
3710
3711   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3712     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3713
3714 #if DEBUG_INIT_PLAYER
3715   DebugPrintPlayerStatus("Player status at level initialization");
3716 #endif
3717
3718   SCAN_PLAYFIELD(x, y)
3719   {
3720     Feld[x][y] = Last[x][y] = level.field[x][y];
3721     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3722     ChangeDelay[x][y] = 0;
3723     ChangePage[x][y] = -1;
3724     CustomValue[x][y] = 0;              // initialized in InitField()
3725     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3726     AmoebaNr[x][y] = 0;
3727     WasJustMoving[x][y] = 0;
3728     WasJustFalling[x][y] = 0;
3729     CheckCollision[x][y] = 0;
3730     CheckImpact[x][y] = 0;
3731     Stop[x][y] = FALSE;
3732     Pushed[x][y] = FALSE;
3733
3734     ChangeCount[x][y] = 0;
3735     ChangeEvent[x][y] = -1;
3736
3737     ExplodePhase[x][y] = 0;
3738     ExplodeDelay[x][y] = 0;
3739     ExplodeField[x][y] = EX_TYPE_NONE;
3740
3741     RunnerVisit[x][y] = 0;
3742     PlayerVisit[x][y] = 0;
3743
3744     GfxFrame[x][y] = 0;
3745     GfxRandom[x][y] = INIT_GFX_RANDOM();
3746     GfxElement[x][y] = EL_UNDEFINED;
3747     GfxAction[x][y] = ACTION_DEFAULT;
3748     GfxDir[x][y] = MV_NONE;
3749     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3750   }
3751
3752   SCAN_PLAYFIELD(x, y)
3753   {
3754     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3755       emulate_bd = FALSE;
3756     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3757       emulate_sb = FALSE;
3758     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3759       emulate_sp = FALSE;
3760
3761     InitField(x, y, TRUE);
3762
3763     ResetGfxAnimation(x, y);
3764   }
3765
3766   InitBeltMovement();
3767
3768   for (i = 0; i < MAX_PLAYERS; i++)
3769   {
3770     struct PlayerInfo *player = &stored_player[i];
3771
3772     // set number of special actions for bored and sleeping animation
3773     player->num_special_action_bored =
3774       get_num_special_action(player->artwork_element,
3775                              ACTION_BORING_1, ACTION_BORING_LAST);
3776     player->num_special_action_sleeping =
3777       get_num_special_action(player->artwork_element,
3778                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3779   }
3780
3781   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3782                     emulate_sb ? EMU_SOKOBAN :
3783                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3784
3785   // initialize type of slippery elements
3786   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3787   {
3788     if (!IS_CUSTOM_ELEMENT(i))
3789     {
3790       // default: elements slip down either to the left or right randomly
3791       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3792
3793       // SP style elements prefer to slip down on the left side
3794       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3795         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3796
3797       // BD style elements prefer to slip down on the left side
3798       if (game.emulation == EMU_BOULDERDASH)
3799         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3800     }
3801   }
3802
3803   // initialize explosion and ignition delay
3804   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3805   {
3806     if (!IS_CUSTOM_ELEMENT(i))
3807     {
3808       int num_phase = 8;
3809       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3810                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3811                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3812       int last_phase = (num_phase + 1) * delay;
3813       int half_phase = (num_phase / 2) * delay;
3814
3815       element_info[i].explosion_delay = last_phase - 1;
3816       element_info[i].ignition_delay = half_phase;
3817
3818       if (i == EL_BLACK_ORB)
3819         element_info[i].ignition_delay = 1;
3820     }
3821   }
3822
3823   // correct non-moving belts to start moving left
3824   for (i = 0; i < NUM_BELTS; i++)
3825     if (game.belt_dir[i] == MV_NONE)
3826       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3827
3828 #if USE_NEW_PLAYER_ASSIGNMENTS
3829   // use preferred player also in local single-player mode
3830   if (!network.enabled && !game.team_mode)
3831   {
3832     int new_index_nr = setup.network_player_nr;
3833
3834     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3835     {
3836       for (i = 0; i < MAX_PLAYERS; i++)
3837         stored_player[i].connected_locally = FALSE;
3838
3839       stored_player[new_index_nr].connected_locally = TRUE;
3840     }
3841   }
3842
3843   for (i = 0; i < MAX_PLAYERS; i++)
3844   {
3845     stored_player[i].connected = FALSE;
3846
3847     // in network game mode, the local player might not be the first player
3848     if (stored_player[i].connected_locally)
3849       local_player = &stored_player[i];
3850   }
3851
3852   if (!network.enabled)
3853     local_player->connected = TRUE;
3854
3855   if (tape.playing)
3856   {
3857     for (i = 0; i < MAX_PLAYERS; i++)
3858       stored_player[i].connected = tape.player_participates[i];
3859   }
3860   else if (network.enabled)
3861   {
3862     // add team mode players connected over the network (needed for correct
3863     // assignment of player figures from level to locally playing players)
3864
3865     for (i = 0; i < MAX_PLAYERS; i++)
3866       if (stored_player[i].connected_network)
3867         stored_player[i].connected = TRUE;
3868   }
3869   else if (game.team_mode)
3870   {
3871     // try to guess locally connected team mode players (needed for correct
3872     // assignment of player figures from level to locally playing players)
3873
3874     for (i = 0; i < MAX_PLAYERS; i++)
3875       if (setup.input[i].use_joystick ||
3876           setup.input[i].key.left != KSYM_UNDEFINED)
3877         stored_player[i].connected = TRUE;
3878   }
3879
3880 #if DEBUG_INIT_PLAYER
3881   DebugPrintPlayerStatus("Player status after level initialization");
3882 #endif
3883
3884 #if DEBUG_INIT_PLAYER
3885   if (options.debug)
3886     printf("Reassigning players ...\n");
3887 #endif
3888
3889   // check if any connected player was not found in playfield
3890   for (i = 0; i < MAX_PLAYERS; i++)
3891   {
3892     struct PlayerInfo *player = &stored_player[i];
3893
3894     if (player->connected && !player->present)
3895     {
3896       struct PlayerInfo *field_player = NULL;
3897
3898 #if DEBUG_INIT_PLAYER
3899       if (options.debug)
3900         printf("- looking for field player for player %d ...\n", i + 1);
3901 #endif
3902
3903       // assign first free player found that is present in the playfield
3904
3905       // first try: look for unmapped playfield player that is not connected
3906       for (j = 0; j < MAX_PLAYERS; j++)
3907         if (field_player == NULL &&
3908             stored_player[j].present &&
3909             !stored_player[j].mapped &&
3910             !stored_player[j].connected)
3911           field_player = &stored_player[j];
3912
3913       // second try: look for *any* unmapped playfield player
3914       for (j = 0; j < MAX_PLAYERS; j++)
3915         if (field_player == NULL &&
3916             stored_player[j].present &&
3917             !stored_player[j].mapped)
3918           field_player = &stored_player[j];
3919
3920       if (field_player != NULL)
3921       {
3922         int jx = field_player->jx, jy = field_player->jy;
3923
3924 #if DEBUG_INIT_PLAYER
3925         if (options.debug)
3926           printf("- found player %d\n", field_player->index_nr + 1);
3927 #endif
3928
3929         player->present = FALSE;
3930         player->active = FALSE;
3931
3932         field_player->present = TRUE;
3933         field_player->active = TRUE;
3934
3935         /*
3936         player->initial_element = field_player->initial_element;
3937         player->artwork_element = field_player->artwork_element;
3938
3939         player->block_last_field       = field_player->block_last_field;
3940         player->block_delay_adjustment = field_player->block_delay_adjustment;
3941         */
3942
3943         StorePlayer[jx][jy] = field_player->element_nr;
3944
3945         field_player->jx = field_player->last_jx = jx;
3946         field_player->jy = field_player->last_jy = jy;
3947
3948         if (local_player == player)
3949           local_player = field_player;
3950
3951         map_player_action[field_player->index_nr] = i;
3952
3953         field_player->mapped = TRUE;
3954
3955 #if DEBUG_INIT_PLAYER
3956         if (options.debug)
3957           printf("- map_player_action[%d] == %d\n",
3958                  field_player->index_nr + 1, i + 1);
3959 #endif
3960       }
3961     }
3962
3963     if (player->connected && player->present)
3964       player->mapped = TRUE;
3965   }
3966
3967 #if DEBUG_INIT_PLAYER
3968   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3969 #endif
3970
3971 #else
3972
3973   // check if any connected player was not found in playfield
3974   for (i = 0; i < MAX_PLAYERS; i++)
3975   {
3976     struct PlayerInfo *player = &stored_player[i];
3977
3978     if (player->connected && !player->present)
3979     {
3980       for (j = 0; j < MAX_PLAYERS; j++)
3981       {
3982         struct PlayerInfo *field_player = &stored_player[j];
3983         int jx = field_player->jx, jy = field_player->jy;
3984
3985         // assign first free player found that is present in the playfield
3986         if (field_player->present && !field_player->connected)
3987         {
3988           player->present = TRUE;
3989           player->active = TRUE;
3990
3991           field_player->present = FALSE;
3992           field_player->active = FALSE;
3993
3994           player->initial_element = field_player->initial_element;
3995           player->artwork_element = field_player->artwork_element;
3996
3997           player->block_last_field       = field_player->block_last_field;
3998           player->block_delay_adjustment = field_player->block_delay_adjustment;
3999
4000           StorePlayer[jx][jy] = player->element_nr;
4001
4002           player->jx = player->last_jx = jx;
4003           player->jy = player->last_jy = jy;
4004
4005           break;
4006         }
4007       }
4008     }
4009   }
4010 #endif
4011
4012 #if 0
4013   printf("::: local_player->present == %d\n", local_player->present);
4014 #endif
4015
4016   // set focus to local player for network games, else to all players
4017   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4018   game.centered_player_nr_next = game.centered_player_nr;
4019   game.set_centered_player = FALSE;
4020   game.set_centered_player_wrap = FALSE;
4021
4022   if (network_playing && tape.recording)
4023   {
4024     // store client dependent player focus when recording network games
4025     tape.centered_player_nr_next = game.centered_player_nr_next;
4026     tape.set_centered_player = TRUE;
4027   }
4028
4029   if (tape.playing)
4030   {
4031     // when playing a tape, eliminate all players who do not participate
4032
4033 #if USE_NEW_PLAYER_ASSIGNMENTS
4034
4035     if (!game.team_mode)
4036     {
4037       for (i = 0; i < MAX_PLAYERS; i++)
4038       {
4039         if (stored_player[i].active &&
4040             !tape.player_participates[map_player_action[i]])
4041         {
4042           struct PlayerInfo *player = &stored_player[i];
4043           int jx = player->jx, jy = player->jy;
4044
4045 #if DEBUG_INIT_PLAYER
4046           if (options.debug)
4047             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4048 #endif
4049
4050           player->active = FALSE;
4051           StorePlayer[jx][jy] = 0;
4052           Feld[jx][jy] = EL_EMPTY;
4053         }
4054       }
4055     }
4056
4057 #else
4058
4059     for (i = 0; i < MAX_PLAYERS; i++)
4060     {
4061       if (stored_player[i].active &&
4062           !tape.player_participates[i])
4063       {
4064         struct PlayerInfo *player = &stored_player[i];
4065         int jx = player->jx, jy = player->jy;
4066
4067         player->active = FALSE;
4068         StorePlayer[jx][jy] = 0;
4069         Feld[jx][jy] = EL_EMPTY;
4070       }
4071     }
4072 #endif
4073   }
4074   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4075   {
4076     // when in single player mode, eliminate all but the local player
4077
4078     for (i = 0; i < MAX_PLAYERS; i++)
4079     {
4080       struct PlayerInfo *player = &stored_player[i];
4081
4082       if (player->active && player != local_player)
4083       {
4084         int jx = player->jx, jy = player->jy;
4085
4086         player->active = FALSE;
4087         player->present = FALSE;
4088
4089         StorePlayer[jx][jy] = 0;
4090         Feld[jx][jy] = EL_EMPTY;
4091       }
4092     }
4093   }
4094
4095   for (i = 0; i < MAX_PLAYERS; i++)
4096     if (stored_player[i].active)
4097       game.players_still_needed++;
4098
4099   if (level.solved_by_one_player)
4100     game.players_still_needed = 1;
4101
4102   // when recording the game, store which players take part in the game
4103   if (tape.recording)
4104   {
4105 #if USE_NEW_PLAYER_ASSIGNMENTS
4106     for (i = 0; i < MAX_PLAYERS; i++)
4107       if (stored_player[i].connected)
4108         tape.player_participates[i] = TRUE;
4109 #else
4110     for (i = 0; i < MAX_PLAYERS; i++)
4111       if (stored_player[i].active)
4112         tape.player_participates[i] = TRUE;
4113 #endif
4114   }
4115
4116 #if DEBUG_INIT_PLAYER
4117   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4118 #endif
4119
4120   if (BorderElement == EL_EMPTY)
4121   {
4122     SBX_Left = 0;
4123     SBX_Right = lev_fieldx - SCR_FIELDX;
4124     SBY_Upper = 0;
4125     SBY_Lower = lev_fieldy - SCR_FIELDY;
4126   }
4127   else
4128   {
4129     SBX_Left = -1;
4130     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4131     SBY_Upper = -1;
4132     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4133   }
4134
4135   if (full_lev_fieldx <= SCR_FIELDX)
4136     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4137   if (full_lev_fieldy <= SCR_FIELDY)
4138     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4139
4140   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4141     SBX_Left--;
4142   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4143     SBY_Upper--;
4144
4145   // if local player not found, look for custom element that might create
4146   // the player (make some assumptions about the right custom element)
4147   if (!local_player->present)
4148   {
4149     int start_x = 0, start_y = 0;
4150     int found_rating = 0;
4151     int found_element = EL_UNDEFINED;
4152     int player_nr = local_player->index_nr;
4153
4154     SCAN_PLAYFIELD(x, y)
4155     {
4156       int element = Feld[x][y];
4157       int content;
4158       int xx, yy;
4159       boolean is_player;
4160
4161       if (level.use_start_element[player_nr] &&
4162           level.start_element[player_nr] == element &&
4163           found_rating < 4)
4164       {
4165         start_x = x;
4166         start_y = y;
4167
4168         found_rating = 4;
4169         found_element = element;
4170       }
4171
4172       if (!IS_CUSTOM_ELEMENT(element))
4173         continue;
4174
4175       if (CAN_CHANGE(element))
4176       {
4177         for (i = 0; i < element_info[element].num_change_pages; i++)
4178         {
4179           // check for player created from custom element as single target
4180           content = element_info[element].change_page[i].target_element;
4181           is_player = ELEM_IS_PLAYER(content);
4182
4183           if (is_player && (found_rating < 3 ||
4184                             (found_rating == 3 && element < found_element)))
4185           {
4186             start_x = x;
4187             start_y = y;
4188
4189             found_rating = 3;
4190             found_element = element;
4191           }
4192         }
4193       }
4194
4195       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4196       {
4197         // check for player created from custom element as explosion content
4198         content = element_info[element].content.e[xx][yy];
4199         is_player = ELEM_IS_PLAYER(content);
4200
4201         if (is_player && (found_rating < 2 ||
4202                           (found_rating == 2 && element < found_element)))
4203         {
4204           start_x = x + xx - 1;
4205           start_y = y + yy - 1;
4206
4207           found_rating = 2;
4208           found_element = element;
4209         }
4210
4211         if (!CAN_CHANGE(element))
4212           continue;
4213
4214         for (i = 0; i < element_info[element].num_change_pages; i++)
4215         {
4216           // check for player created from custom element as extended target
4217           content =
4218             element_info[element].change_page[i].target_content.e[xx][yy];
4219
4220           is_player = ELEM_IS_PLAYER(content);
4221
4222           if (is_player && (found_rating < 1 ||
4223                             (found_rating == 1 && element < found_element)))
4224           {
4225             start_x = x + xx - 1;
4226             start_y = y + yy - 1;
4227
4228             found_rating = 1;
4229             found_element = element;
4230           }
4231         }
4232       }
4233     }
4234
4235     scroll_x = SCROLL_POSITION_X(start_x);
4236     scroll_y = SCROLL_POSITION_Y(start_y);
4237   }
4238   else
4239   {
4240     scroll_x = SCROLL_POSITION_X(local_player->jx);
4241     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4242   }
4243
4244   // !!! FIX THIS (START) !!!
4245   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4246   {
4247     InitGameEngine_EM();
4248   }
4249   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4250   {
4251     InitGameEngine_SP();
4252   }
4253   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4254   {
4255     InitGameEngine_MM();
4256   }
4257   else
4258   {
4259     DrawLevel(REDRAW_FIELD);
4260     DrawAllPlayers();
4261
4262     // after drawing the level, correct some elements
4263     if (game.timegate_time_left == 0)
4264       CloseAllOpenTimegates();
4265   }
4266
4267   // blit playfield from scroll buffer to normal back buffer for fading in
4268   BlitScreenToBitmap(backbuffer);
4269   // !!! FIX THIS (END) !!!
4270
4271   DrawMaskedBorder(fade_mask);
4272
4273   FadeIn(fade_mask);
4274
4275 #if 1
4276   // full screen redraw is required at this point in the following cases:
4277   // - special editor door undrawn when game was started from level editor
4278   // - drawing area (playfield) was changed and has to be removed completely
4279   redraw_mask = REDRAW_ALL;
4280   BackToFront();
4281 #endif
4282
4283   if (!game.restart_level)
4284   {
4285     // copy default game door content to main double buffer
4286
4287     // !!! CHECK AGAIN !!!
4288     SetPanelBackground();
4289     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4290     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4291   }
4292
4293   SetPanelBackground();
4294   SetDrawBackgroundMask(REDRAW_DOOR_1);
4295
4296   UpdateAndDisplayGameControlValues();
4297
4298   if (!game.restart_level)
4299   {
4300     UnmapGameButtons();
4301     UnmapTapeButtons();
4302
4303     FreeGameButtons();
4304     CreateGameButtons();
4305
4306     MapGameButtons();
4307     MapTapeButtons();
4308
4309     // copy actual game door content to door double buffer for OpenDoor()
4310     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4311
4312     OpenDoor(DOOR_OPEN_ALL);
4313
4314     KeyboardAutoRepeatOffUnlessAutoplay();
4315
4316 #if DEBUG_INIT_PLAYER
4317     DebugPrintPlayerStatus("Player status (final)");
4318 #endif
4319   }
4320
4321   UnmapAllGadgets();
4322
4323   MapGameButtons();
4324   MapTapeButtons();
4325
4326   if (!game.restart_level && !tape.playing)
4327   {
4328     LevelStats_incPlayed(level_nr);
4329
4330     SaveLevelSetup_SeriesInfo();
4331   }
4332
4333   game.restart_level = FALSE;
4334   game.restart_game_message = NULL;
4335   game.request_active = FALSE;
4336
4337   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4338     InitGameActions_MM();
4339
4340   SaveEngineSnapshotToListInitial();
4341
4342   if (!game.restart_level)
4343   {
4344     PlaySound(SND_GAME_STARTING);
4345
4346     if (setup.sound_music)
4347       PlayLevelMusic();
4348   }
4349 }
4350
4351 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4352                         int actual_player_x, int actual_player_y)
4353 {
4354   // this is used for non-R'n'D game engines to update certain engine values
4355
4356   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4357   {
4358     actual_player_x = correctLevelPosX_EM(actual_player_x);
4359     actual_player_y = correctLevelPosY_EM(actual_player_y);
4360   }
4361
4362   // needed to determine if sounds are played within the visible screen area
4363   scroll_x = actual_scroll_x;
4364   scroll_y = actual_scroll_y;
4365
4366   // needed to get player position for "follow finger" playing input method
4367   local_player->jx = actual_player_x;
4368   local_player->jy = actual_player_y;
4369 }
4370
4371 void InitMovDir(int x, int y)
4372 {
4373   int i, element = Feld[x][y];
4374   static int xy[4][2] =
4375   {
4376     {  0, +1 },
4377     { +1,  0 },
4378     {  0, -1 },
4379     { -1,  0 }
4380   };
4381   static int direction[3][4] =
4382   {
4383     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4384     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4385     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4386   };
4387
4388   switch (element)
4389   {
4390     case EL_BUG_RIGHT:
4391     case EL_BUG_UP:
4392     case EL_BUG_LEFT:
4393     case EL_BUG_DOWN:
4394       Feld[x][y] = EL_BUG;
4395       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4396       break;
4397
4398     case EL_SPACESHIP_RIGHT:
4399     case EL_SPACESHIP_UP:
4400     case EL_SPACESHIP_LEFT:
4401     case EL_SPACESHIP_DOWN:
4402       Feld[x][y] = EL_SPACESHIP;
4403       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4404       break;
4405
4406     case EL_BD_BUTTERFLY_RIGHT:
4407     case EL_BD_BUTTERFLY_UP:
4408     case EL_BD_BUTTERFLY_LEFT:
4409     case EL_BD_BUTTERFLY_DOWN:
4410       Feld[x][y] = EL_BD_BUTTERFLY;
4411       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4412       break;
4413
4414     case EL_BD_FIREFLY_RIGHT:
4415     case EL_BD_FIREFLY_UP:
4416     case EL_BD_FIREFLY_LEFT:
4417     case EL_BD_FIREFLY_DOWN:
4418       Feld[x][y] = EL_BD_FIREFLY;
4419       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4420       break;
4421
4422     case EL_PACMAN_RIGHT:
4423     case EL_PACMAN_UP:
4424     case EL_PACMAN_LEFT:
4425     case EL_PACMAN_DOWN:
4426       Feld[x][y] = EL_PACMAN;
4427       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4428       break;
4429
4430     case EL_YAMYAM_LEFT:
4431     case EL_YAMYAM_RIGHT:
4432     case EL_YAMYAM_UP:
4433     case EL_YAMYAM_DOWN:
4434       Feld[x][y] = EL_YAMYAM;
4435       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4436       break;
4437
4438     case EL_SP_SNIKSNAK:
4439       MovDir[x][y] = MV_UP;
4440       break;
4441
4442     case EL_SP_ELECTRON:
4443       MovDir[x][y] = MV_LEFT;
4444       break;
4445
4446     case EL_MOLE_LEFT:
4447     case EL_MOLE_RIGHT:
4448     case EL_MOLE_UP:
4449     case EL_MOLE_DOWN:
4450       Feld[x][y] = EL_MOLE;
4451       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4452       break;
4453
4454     default:
4455       if (IS_CUSTOM_ELEMENT(element))
4456       {
4457         struct ElementInfo *ei = &element_info[element];
4458         int move_direction_initial = ei->move_direction_initial;
4459         int move_pattern = ei->move_pattern;
4460
4461         if (move_direction_initial == MV_START_PREVIOUS)
4462         {
4463           if (MovDir[x][y] != MV_NONE)
4464             return;
4465
4466           move_direction_initial = MV_START_AUTOMATIC;
4467         }
4468
4469         if (move_direction_initial == MV_START_RANDOM)
4470           MovDir[x][y] = 1 << RND(4);
4471         else if (move_direction_initial & MV_ANY_DIRECTION)
4472           MovDir[x][y] = move_direction_initial;
4473         else if (move_pattern == MV_ALL_DIRECTIONS ||
4474                  move_pattern == MV_TURNING_LEFT ||
4475                  move_pattern == MV_TURNING_RIGHT ||
4476                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4477                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4478                  move_pattern == MV_TURNING_RANDOM)
4479           MovDir[x][y] = 1 << RND(4);
4480         else if (move_pattern == MV_HORIZONTAL)
4481           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4482         else if (move_pattern == MV_VERTICAL)
4483           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4484         else if (move_pattern & MV_ANY_DIRECTION)
4485           MovDir[x][y] = element_info[element].move_pattern;
4486         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4487                  move_pattern == MV_ALONG_RIGHT_SIDE)
4488         {
4489           // use random direction as default start direction
4490           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4491             MovDir[x][y] = 1 << RND(4);
4492
4493           for (i = 0; i < NUM_DIRECTIONS; i++)
4494           {
4495             int x1 = x + xy[i][0];
4496             int y1 = y + xy[i][1];
4497
4498             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4499             {
4500               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4501                 MovDir[x][y] = direction[0][i];
4502               else
4503                 MovDir[x][y] = direction[1][i];
4504
4505               break;
4506             }
4507           }
4508         }                
4509       }
4510       else
4511       {
4512         MovDir[x][y] = 1 << RND(4);
4513
4514         if (element != EL_BUG &&
4515             element != EL_SPACESHIP &&
4516             element != EL_BD_BUTTERFLY &&
4517             element != EL_BD_FIREFLY)
4518           break;
4519
4520         for (i = 0; i < NUM_DIRECTIONS; i++)
4521         {
4522           int x1 = x + xy[i][0];
4523           int y1 = y + xy[i][1];
4524
4525           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4526           {
4527             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4528             {
4529               MovDir[x][y] = direction[0][i];
4530               break;
4531             }
4532             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4533                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4534             {
4535               MovDir[x][y] = direction[1][i];
4536               break;
4537             }
4538           }
4539         }
4540       }
4541       break;
4542   }
4543
4544   GfxDir[x][y] = MovDir[x][y];
4545 }
4546
4547 void InitAmoebaNr(int x, int y)
4548 {
4549   int i;
4550   int group_nr = AmoebeNachbarNr(x, y);
4551
4552   if (group_nr == 0)
4553   {
4554     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4555     {
4556       if (AmoebaCnt[i] == 0)
4557       {
4558         group_nr = i;
4559         break;
4560       }
4561     }
4562   }
4563
4564   AmoebaNr[x][y] = group_nr;
4565   AmoebaCnt[group_nr]++;
4566   AmoebaCnt2[group_nr]++;
4567 }
4568
4569 static void LevelSolved(void)
4570 {
4571   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4572       game.players_still_needed > 0)
4573     return;
4574
4575   game.LevelSolved = TRUE;
4576   game.GameOver = TRUE;
4577
4578   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4579                       game_em.lev->score :
4580                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4581                       game_mm.score :
4582                       game.score);
4583   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4584                        MM_HEALTH(game_mm.laser_overload_value) :
4585                        game.health);
4586
4587   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4588   game.LevelSolved_CountingScore = game.score_final;
4589   game.LevelSolved_CountingHealth = game.health_final;
4590 }
4591
4592 void GameWon(void)
4593 {
4594   static int time_count_steps;
4595   static int time, time_final;
4596   static int score, score_final;
4597   static int health, health_final;
4598   static int game_over_delay_1 = 0;
4599   static int game_over_delay_2 = 0;
4600   static int game_over_delay_3 = 0;
4601   int game_over_delay_value_1 = 50;
4602   int game_over_delay_value_2 = 25;
4603   int game_over_delay_value_3 = 50;
4604
4605   if (!game.LevelSolved_GameWon)
4606   {
4607     int i;
4608
4609     // do not start end game actions before the player stops moving (to exit)
4610     if (local_player->active && local_player->MovPos)
4611       return;
4612
4613     game.LevelSolved_GameWon = TRUE;
4614     game.LevelSolved_SaveTape = tape.recording;
4615     game.LevelSolved_SaveScore = !tape.playing;
4616
4617     if (!tape.playing)
4618     {
4619       LevelStats_incSolved(level_nr);
4620
4621       SaveLevelSetup_SeriesInfo();
4622     }
4623
4624     if (tape.auto_play)         // tape might already be stopped here
4625       tape.auto_play_level_solved = TRUE;
4626
4627     TapeStop();
4628
4629     game_over_delay_1 = 0;
4630     game_over_delay_2 = 0;
4631     game_over_delay_3 = game_over_delay_value_3;
4632
4633     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4634     score = score_final = game.score_final;
4635     health = health_final = game.health_final;
4636
4637     if (level.score[SC_TIME_BONUS] > 0)
4638     {
4639       if (TimeLeft > 0)
4640       {
4641         time_final = 0;
4642         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4643       }
4644       else if (game.no_time_limit && TimePlayed < 999)
4645       {
4646         time_final = 999;
4647         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4648       }
4649
4650       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4651
4652       game_over_delay_1 = game_over_delay_value_1;
4653
4654       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4655       {
4656         health_final = 0;
4657         score_final += health * level.score[SC_TIME_BONUS];
4658
4659         game_over_delay_2 = game_over_delay_value_2;
4660       }
4661
4662       game.score_final = score_final;
4663       game.health_final = health_final;
4664     }
4665
4666     if (level_editor_test_game)
4667     {
4668       time = time_final;
4669       score = score_final;
4670
4671       game.LevelSolved_CountingTime = time;
4672       game.LevelSolved_CountingScore = score;
4673
4674       game_panel_controls[GAME_PANEL_TIME].value = time;
4675       game_panel_controls[GAME_PANEL_SCORE].value = score;
4676
4677       DisplayGameControlValues();
4678     }
4679
4680     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4681     {
4682       // check if last player has left the level
4683       if (game.exit_x >= 0 &&
4684           game.exit_y >= 0)
4685       {
4686         int x = game.exit_x;
4687         int y = game.exit_y;
4688         int element = Feld[x][y];
4689
4690         // close exit door after last player
4691         if ((game.all_players_gone &&
4692              (element == EL_EXIT_OPEN ||
4693               element == EL_SP_EXIT_OPEN ||
4694               element == EL_STEEL_EXIT_OPEN)) ||
4695             element == EL_EM_EXIT_OPEN ||
4696             element == EL_EM_STEEL_EXIT_OPEN)
4697         {
4698
4699           Feld[x][y] =
4700             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4701              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4702              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4703              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4704              EL_EM_STEEL_EXIT_CLOSING);
4705
4706           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4707         }
4708
4709         // player disappears
4710         DrawLevelField(x, y);
4711       }
4712
4713       for (i = 0; i < MAX_PLAYERS; i++)
4714       {
4715         struct PlayerInfo *player = &stored_player[i];
4716
4717         if (player->present)
4718         {
4719           RemovePlayer(player);
4720
4721           // player disappears
4722           DrawLevelField(player->jx, player->jy);
4723         }
4724       }
4725     }
4726
4727     PlaySound(SND_GAME_WINNING);
4728   }
4729
4730   if (game_over_delay_1 > 0)
4731   {
4732     game_over_delay_1--;
4733
4734     return;
4735   }
4736
4737   if (time != time_final)
4738   {
4739     int time_to_go = ABS(time_final - time);
4740     int time_count_dir = (time < time_final ? +1 : -1);
4741
4742     if (time_to_go < time_count_steps)
4743       time_count_steps = 1;
4744
4745     time  += time_count_steps * time_count_dir;
4746     score += time_count_steps * level.score[SC_TIME_BONUS];
4747
4748     game.LevelSolved_CountingTime = time;
4749     game.LevelSolved_CountingScore = score;
4750
4751     game_panel_controls[GAME_PANEL_TIME].value = time;
4752     game_panel_controls[GAME_PANEL_SCORE].value = score;
4753
4754     DisplayGameControlValues();
4755
4756     if (time == time_final)
4757       StopSound(SND_GAME_LEVELTIME_BONUS);
4758     else if (setup.sound_loops)
4759       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4760     else
4761       PlaySound(SND_GAME_LEVELTIME_BONUS);
4762
4763     return;
4764   }
4765
4766   if (game_over_delay_2 > 0)
4767   {
4768     game_over_delay_2--;
4769
4770     return;
4771   }
4772
4773   if (health != health_final)
4774   {
4775     int health_count_dir = (health < health_final ? +1 : -1);
4776
4777     health += health_count_dir;
4778     score  += level.score[SC_TIME_BONUS];
4779
4780     game.LevelSolved_CountingHealth = health;
4781     game.LevelSolved_CountingScore = score;
4782
4783     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4784     game_panel_controls[GAME_PANEL_SCORE].value = score;
4785
4786     DisplayGameControlValues();
4787
4788     if (health == health_final)
4789       StopSound(SND_GAME_LEVELTIME_BONUS);
4790     else if (setup.sound_loops)
4791       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4792     else
4793       PlaySound(SND_GAME_LEVELTIME_BONUS);
4794
4795     return;
4796   }
4797
4798   game.panel.active = FALSE;
4799
4800   if (game_over_delay_3 > 0)
4801   {
4802     game_over_delay_3--;
4803
4804     return;
4805   }
4806
4807   GameEnd();
4808 }
4809
4810 void GameEnd(void)
4811 {
4812   // used instead of "level_nr" (needed for network games)
4813   int last_level_nr = levelset.level_nr;
4814   int hi_pos;
4815
4816   game.LevelSolved_GameEnd = TRUE;
4817
4818   if (game.LevelSolved_SaveTape)
4819   {
4820     // make sure that request dialog to save tape does not open door again
4821     if (!global.use_envelope_request)
4822       CloseDoor(DOOR_CLOSE_1);
4823
4824     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4825   }
4826
4827   // if no tape is to be saved, close both doors simultaneously
4828   CloseDoor(DOOR_CLOSE_ALL);
4829
4830   if (level_editor_test_game)
4831   {
4832     SetGameStatus(GAME_MODE_MAIN);
4833
4834     DrawMainMenu();
4835
4836     return;
4837   }
4838
4839   if (!game.LevelSolved_SaveScore)
4840   {
4841     SetGameStatus(GAME_MODE_MAIN);
4842
4843     DrawMainMenu();
4844
4845     return;
4846   }
4847
4848   if (level_nr == leveldir_current->handicap_level)
4849   {
4850     leveldir_current->handicap_level++;
4851
4852     SaveLevelSetup_SeriesInfo();
4853   }
4854
4855   if (setup.increment_levels &&
4856       level_nr < leveldir_current->last_level &&
4857       !network_playing)
4858   {
4859     level_nr++;         // advance to next level
4860     TapeErase();        // start with empty tape
4861
4862     if (setup.auto_play_next_level)
4863     {
4864       LoadLevel(level_nr);
4865
4866       SaveLevelSetup_SeriesInfo();
4867     }
4868   }
4869
4870   hi_pos = NewHiScore(last_level_nr);
4871
4872   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4873   {
4874     SetGameStatus(GAME_MODE_SCORES);
4875
4876     DrawHallOfFame(last_level_nr, hi_pos);
4877   }
4878   else if (setup.auto_play_next_level && setup.increment_levels &&
4879            last_level_nr < leveldir_current->last_level &&
4880            !network_playing)
4881   {
4882     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4883   }
4884   else
4885   {
4886     SetGameStatus(GAME_MODE_MAIN);
4887
4888     DrawMainMenu();
4889   }
4890 }
4891
4892 int NewHiScore(int level_nr)
4893 {
4894   int k, l;
4895   int position = -1;
4896   boolean one_score_entry_per_name = !program.many_scores_per_name;
4897
4898   LoadScore(level_nr);
4899
4900   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4901       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4902     return -1;
4903
4904   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4905   {
4906     if (game.score_final > highscore[k].Score)
4907     {
4908       // player has made it to the hall of fame
4909
4910       if (k < MAX_SCORE_ENTRIES - 1)
4911       {
4912         int m = MAX_SCORE_ENTRIES - 1;
4913
4914         if (one_score_entry_per_name)
4915         {
4916           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4917             if (strEqual(setup.player_name, highscore[l].Name))
4918               m = l;
4919
4920           if (m == k)   // player's new highscore overwrites his old one
4921             goto put_into_list;
4922         }
4923
4924         for (l = m; l > k; l--)
4925         {
4926           strcpy(highscore[l].Name, highscore[l - 1].Name);
4927           highscore[l].Score = highscore[l - 1].Score;
4928         }
4929       }
4930
4931       put_into_list:
4932
4933       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4934       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4935       highscore[k].Score = game.score_final;
4936       position = k;
4937
4938       break;
4939     }
4940     else if (one_score_entry_per_name &&
4941              !strncmp(setup.player_name, highscore[k].Name,
4942                       MAX_PLAYER_NAME_LEN))
4943       break;    // player already there with a higher score
4944   }
4945
4946   if (position >= 0) 
4947     SaveScore(level_nr);
4948
4949   return position;
4950 }
4951
4952 static int getElementMoveStepsizeExt(int x, int y, int direction)
4953 {
4954   int element = Feld[x][y];
4955   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4956   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4957   int horiz_move = (dx != 0);
4958   int sign = (horiz_move ? dx : dy);
4959   int step = sign * element_info[element].move_stepsize;
4960
4961   // special values for move stepsize for spring and things on conveyor belt
4962   if (horiz_move)
4963   {
4964     if (CAN_FALL(element) &&
4965         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4966       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4967     else if (element == EL_SPRING)
4968       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4969   }
4970
4971   return step;
4972 }
4973
4974 static int getElementMoveStepsize(int x, int y)
4975 {
4976   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4977 }
4978
4979 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4980 {
4981   if (player->GfxAction != action || player->GfxDir != dir)
4982   {
4983     player->GfxAction = action;
4984     player->GfxDir = dir;
4985     player->Frame = 0;
4986     player->StepFrame = 0;
4987   }
4988 }
4989
4990 static void ResetGfxFrame(int x, int y)
4991 {
4992   // profiling showed that "autotest" spends 10~20% of its time in this function
4993   if (DrawingDeactivatedField())
4994     return;
4995
4996   int element = Feld[x][y];
4997   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4998
4999   if (graphic_info[graphic].anim_global_sync)
5000     GfxFrame[x][y] = FrameCounter;
5001   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5002     GfxFrame[x][y] = CustomValue[x][y];
5003   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5004     GfxFrame[x][y] = element_info[element].collect_score;
5005   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5006     GfxFrame[x][y] = ChangeDelay[x][y];
5007 }
5008
5009 static void ResetGfxAnimation(int x, int y)
5010 {
5011   GfxAction[x][y] = ACTION_DEFAULT;
5012   GfxDir[x][y] = MovDir[x][y];
5013   GfxFrame[x][y] = 0;
5014
5015   ResetGfxFrame(x, y);
5016 }
5017
5018 static void ResetRandomAnimationValue(int x, int y)
5019 {
5020   GfxRandom[x][y] = INIT_GFX_RANDOM();
5021 }
5022
5023 static void InitMovingField(int x, int y, int direction)
5024 {
5025   int element = Feld[x][y];
5026   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5027   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5028   int newx = x + dx;
5029   int newy = y + dy;
5030   boolean is_moving_before, is_moving_after;
5031
5032   // check if element was/is moving or being moved before/after mode change
5033   is_moving_before = (WasJustMoving[x][y] != 0);
5034   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5035
5036   // reset animation only for moving elements which change direction of moving
5037   // or which just started or stopped moving
5038   // (else CEs with property "can move" / "not moving" are reset each frame)
5039   if (is_moving_before != is_moving_after ||
5040       direction != MovDir[x][y])
5041     ResetGfxAnimation(x, y);
5042
5043   MovDir[x][y] = direction;
5044   GfxDir[x][y] = direction;
5045
5046   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5047                      direction == MV_DOWN && CAN_FALL(element) ?
5048                      ACTION_FALLING : ACTION_MOVING);
5049
5050   // this is needed for CEs with property "can move" / "not moving"
5051
5052   if (is_moving_after)
5053   {
5054     if (Feld[newx][newy] == EL_EMPTY)
5055       Feld[newx][newy] = EL_BLOCKED;
5056
5057     MovDir[newx][newy] = MovDir[x][y];
5058
5059     CustomValue[newx][newy] = CustomValue[x][y];
5060
5061     GfxFrame[newx][newy] = GfxFrame[x][y];
5062     GfxRandom[newx][newy] = GfxRandom[x][y];
5063     GfxAction[newx][newy] = GfxAction[x][y];
5064     GfxDir[newx][newy] = GfxDir[x][y];
5065   }
5066 }
5067
5068 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5069 {
5070   int direction = MovDir[x][y];
5071   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5072   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5073
5074   *goes_to_x = newx;
5075   *goes_to_y = newy;
5076 }
5077
5078 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5079 {
5080   int oldx = x, oldy = y;
5081   int direction = MovDir[x][y];
5082
5083   if (direction == MV_LEFT)
5084     oldx++;
5085   else if (direction == MV_RIGHT)
5086     oldx--;
5087   else if (direction == MV_UP)
5088     oldy++;
5089   else if (direction == MV_DOWN)
5090     oldy--;
5091
5092   *comes_from_x = oldx;
5093   *comes_from_y = oldy;
5094 }
5095
5096 static int MovingOrBlocked2Element(int x, int y)
5097 {
5098   int element = Feld[x][y];
5099
5100   if (element == EL_BLOCKED)
5101   {
5102     int oldx, oldy;
5103
5104     Blocked2Moving(x, y, &oldx, &oldy);
5105     return Feld[oldx][oldy];
5106   }
5107   else
5108     return element;
5109 }
5110
5111 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5112 {
5113   // like MovingOrBlocked2Element(), but if element is moving
5114   // and (x,y) is the field the moving element is just leaving,
5115   // return EL_BLOCKED instead of the element value
5116   int element = Feld[x][y];
5117
5118   if (IS_MOVING(x, y))
5119   {
5120     if (element == EL_BLOCKED)
5121     {
5122       int oldx, oldy;
5123
5124       Blocked2Moving(x, y, &oldx, &oldy);
5125       return Feld[oldx][oldy];
5126     }
5127     else
5128       return EL_BLOCKED;
5129   }
5130   else
5131     return element;
5132 }
5133
5134 static void RemoveField(int x, int y)
5135 {
5136   Feld[x][y] = EL_EMPTY;
5137
5138   MovPos[x][y] = 0;
5139   MovDir[x][y] = 0;
5140   MovDelay[x][y] = 0;
5141
5142   CustomValue[x][y] = 0;
5143
5144   AmoebaNr[x][y] = 0;
5145   ChangeDelay[x][y] = 0;
5146   ChangePage[x][y] = -1;
5147   Pushed[x][y] = FALSE;
5148
5149   GfxElement[x][y] = EL_UNDEFINED;
5150   GfxAction[x][y] = ACTION_DEFAULT;
5151   GfxDir[x][y] = MV_NONE;
5152 }
5153
5154 static void RemoveMovingField(int x, int y)
5155 {
5156   int oldx = x, oldy = y, newx = x, newy = y;
5157   int element = Feld[x][y];
5158   int next_element = EL_UNDEFINED;
5159
5160   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5161     return;
5162
5163   if (IS_MOVING(x, y))
5164   {
5165     Moving2Blocked(x, y, &newx, &newy);
5166
5167     if (Feld[newx][newy] != EL_BLOCKED)
5168     {
5169       // element is moving, but target field is not free (blocked), but
5170       // already occupied by something different (example: acid pool);
5171       // in this case, only remove the moving field, but not the target
5172
5173       RemoveField(oldx, oldy);
5174
5175       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5176
5177       TEST_DrawLevelField(oldx, oldy);
5178
5179       return;
5180     }
5181   }
5182   else if (element == EL_BLOCKED)
5183   {
5184     Blocked2Moving(x, y, &oldx, &oldy);
5185     if (!IS_MOVING(oldx, oldy))
5186       return;
5187   }
5188
5189   if (element == EL_BLOCKED &&
5190       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5191        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5192        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5193        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5194        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5195        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5196     next_element = get_next_element(Feld[oldx][oldy]);
5197
5198   RemoveField(oldx, oldy);
5199   RemoveField(newx, newy);
5200
5201   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5202
5203   if (next_element != EL_UNDEFINED)
5204     Feld[oldx][oldy] = next_element;
5205
5206   TEST_DrawLevelField(oldx, oldy);
5207   TEST_DrawLevelField(newx, newy);
5208 }
5209
5210 void DrawDynamite(int x, int y)
5211 {
5212   int sx = SCREENX(x), sy = SCREENY(y);
5213   int graphic = el2img(Feld[x][y]);
5214   int frame;
5215
5216   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5217     return;
5218
5219   if (IS_WALKABLE_INSIDE(Back[x][y]))
5220     return;
5221
5222   if (Back[x][y])
5223     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5224   else if (Store[x][y])
5225     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5226
5227   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5228
5229   if (Back[x][y] || Store[x][y])
5230     DrawGraphicThruMask(sx, sy, graphic, frame);
5231   else
5232     DrawGraphic(sx, sy, graphic, frame);
5233 }
5234
5235 static void CheckDynamite(int x, int y)
5236 {
5237   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5238   {
5239     MovDelay[x][y]--;
5240
5241     if (MovDelay[x][y] != 0)
5242     {
5243       DrawDynamite(x, y);
5244       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5245
5246       return;
5247     }
5248   }
5249
5250   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5251
5252   Bang(x, y);
5253 }
5254
5255 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5256 {
5257   boolean num_checked_players = 0;
5258   int i;
5259
5260   for (i = 0; i < MAX_PLAYERS; i++)
5261   {
5262     if (stored_player[i].active)
5263     {
5264       int sx = stored_player[i].jx;
5265       int sy = stored_player[i].jy;
5266
5267       if (num_checked_players == 0)
5268       {
5269         *sx1 = *sx2 = sx;
5270         *sy1 = *sy2 = sy;
5271       }
5272       else
5273       {
5274         *sx1 = MIN(*sx1, sx);
5275         *sy1 = MIN(*sy1, sy);
5276         *sx2 = MAX(*sx2, sx);
5277         *sy2 = MAX(*sy2, sy);
5278       }
5279
5280       num_checked_players++;
5281     }
5282   }
5283 }
5284
5285 static boolean checkIfAllPlayersFitToScreen_RND(void)
5286 {
5287   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5288
5289   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5290
5291   return (sx2 - sx1 < SCR_FIELDX &&
5292           sy2 - sy1 < SCR_FIELDY);
5293 }
5294
5295 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5296 {
5297   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5298
5299   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5300
5301   *sx = (sx1 + sx2) / 2;
5302   *sy = (sy1 + sy2) / 2;
5303 }
5304
5305 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5306                                boolean center_screen, boolean quick_relocation)
5307 {
5308   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5309   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5310   boolean no_delay = (tape.warp_forward);
5311   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5312   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5313   int new_scroll_x, new_scroll_y;
5314
5315   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5316   {
5317     // case 1: quick relocation inside visible screen (without scrolling)
5318
5319     RedrawPlayfield();
5320
5321     return;
5322   }
5323
5324   if (!level.shifted_relocation || center_screen)
5325   {
5326     // relocation _with_ centering of screen
5327
5328     new_scroll_x = SCROLL_POSITION_X(x);
5329     new_scroll_y = SCROLL_POSITION_Y(y);
5330   }
5331   else
5332   {
5333     // relocation _without_ centering of screen
5334
5335     int center_scroll_x = SCROLL_POSITION_X(old_x);
5336     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5337     int offset_x = x + (scroll_x - center_scroll_x);
5338     int offset_y = y + (scroll_y - center_scroll_y);
5339
5340     // for new screen position, apply previous offset to center position
5341     new_scroll_x = SCROLL_POSITION_X(offset_x);
5342     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5343   }
5344
5345   if (quick_relocation)
5346   {
5347     // case 2: quick relocation (redraw without visible scrolling)
5348
5349     scroll_x = new_scroll_x;
5350     scroll_y = new_scroll_y;
5351
5352     RedrawPlayfield();
5353
5354     return;
5355   }
5356
5357   // case 3: visible relocation (with scrolling to new position)
5358
5359   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5360
5361   SetVideoFrameDelay(wait_delay_value);
5362
5363   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5364   {
5365     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5366     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5367
5368     if (dx == 0 && dy == 0)             // no scrolling needed at all
5369       break;
5370
5371     scroll_x -= dx;
5372     scroll_y -= dy;
5373
5374     // set values for horizontal/vertical screen scrolling (half tile size)
5375     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5376     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5377     int pos_x = dx * TILEX / 2;
5378     int pos_y = dy * TILEY / 2;
5379     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5380     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5381
5382     ScrollLevel(dx, dy);
5383     DrawAllPlayers();
5384
5385     // scroll in two steps of half tile size to make things smoother
5386     BlitScreenToBitmapExt_RND(window, fx, fy);
5387
5388     // scroll second step to align at full tile size
5389     BlitScreenToBitmap(window);
5390   }
5391
5392   DrawAllPlayers();
5393   BackToFront();
5394
5395   SetVideoFrameDelay(frame_delay_value_old);
5396 }
5397
5398 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5399 {
5400   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5401   int player_nr = GET_PLAYER_NR(el_player);
5402   struct PlayerInfo *player = &stored_player[player_nr];
5403   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5404   boolean no_delay = (tape.warp_forward);
5405   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5406   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5407   int old_jx = player->jx;
5408   int old_jy = player->jy;
5409   int old_element = Feld[old_jx][old_jy];
5410   int element = Feld[jx][jy];
5411   boolean player_relocated = (old_jx != jx || old_jy != jy);
5412
5413   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5414   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5415   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5416   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5417   int leave_side_horiz = move_dir_horiz;
5418   int leave_side_vert  = move_dir_vert;
5419   int enter_side = enter_side_horiz | enter_side_vert;
5420   int leave_side = leave_side_horiz | leave_side_vert;
5421
5422   if (player->buried)           // do not reanimate dead player
5423     return;
5424
5425   if (!player_relocated)        // no need to relocate the player
5426     return;
5427
5428   if (IS_PLAYER(jx, jy))        // player already placed at new position
5429   {
5430     RemoveField(jx, jy);        // temporarily remove newly placed player
5431     DrawLevelField(jx, jy);
5432   }
5433
5434   if (player->present)
5435   {
5436     while (player->MovPos)
5437     {
5438       ScrollPlayer(player, SCROLL_GO_ON);
5439       ScrollScreen(NULL, SCROLL_GO_ON);
5440
5441       AdvanceFrameAndPlayerCounters(player->index_nr);
5442
5443       DrawPlayer(player);
5444
5445       BackToFront_WithFrameDelay(wait_delay_value);
5446     }
5447
5448     DrawPlayer(player);         // needed here only to cleanup last field
5449     DrawLevelField(player->jx, player->jy);     // remove player graphic
5450
5451     player->is_moving = FALSE;
5452   }
5453
5454   if (IS_CUSTOM_ELEMENT(old_element))
5455     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5456                                CE_LEFT_BY_PLAYER,
5457                                player->index_bit, leave_side);
5458
5459   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5460                                       CE_PLAYER_LEAVES_X,
5461                                       player->index_bit, leave_side);
5462
5463   Feld[jx][jy] = el_player;
5464   InitPlayerField(jx, jy, el_player, TRUE);
5465
5466   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5467      possible that the relocation target field did not contain a player element,
5468      but a walkable element, to which the new player was relocated -- in this
5469      case, restore that (already initialized!) element on the player field */
5470   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5471   {
5472     Feld[jx][jy] = element;     // restore previously existing element
5473   }
5474
5475   // only visually relocate centered player
5476   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5477                      FALSE, level.instant_relocation);
5478
5479   TestIfPlayerTouchesBadThing(jx, jy);
5480   TestIfPlayerTouchesCustomElement(jx, jy);
5481
5482   if (IS_CUSTOM_ELEMENT(element))
5483     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5484                                player->index_bit, enter_side);
5485
5486   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5487                                       player->index_bit, enter_side);
5488
5489   if (player->is_switching)
5490   {
5491     /* ensure that relocation while still switching an element does not cause
5492        a new element to be treated as also switched directly after relocation
5493        (this is important for teleporter switches that teleport the player to
5494        a place where another teleporter switch is in the same direction, which
5495        would then incorrectly be treated as immediately switched before the
5496        direction key that caused the switch was released) */
5497
5498     player->switch_x += jx - old_jx;
5499     player->switch_y += jy - old_jy;
5500   }
5501 }
5502
5503 static void Explode(int ex, int ey, int phase, int mode)
5504 {
5505   int x, y;
5506   int last_phase;
5507   int border_element;
5508
5509   // !!! eliminate this variable !!!
5510   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5511
5512   if (game.explosions_delayed)
5513   {
5514     ExplodeField[ex][ey] = mode;
5515     return;
5516   }
5517
5518   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5519   {
5520     int center_element = Feld[ex][ey];
5521     int artwork_element, explosion_element;     // set these values later
5522
5523     // remove things displayed in background while burning dynamite
5524     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5525       Back[ex][ey] = 0;
5526
5527     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5528     {
5529       // put moving element to center field (and let it explode there)
5530       center_element = MovingOrBlocked2Element(ex, ey);
5531       RemoveMovingField(ex, ey);
5532       Feld[ex][ey] = center_element;
5533     }
5534
5535     // now "center_element" is finally determined -- set related values now
5536     artwork_element = center_element;           // for custom player artwork
5537     explosion_element = center_element;         // for custom player artwork
5538
5539     if (IS_PLAYER(ex, ey))
5540     {
5541       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5542
5543       artwork_element = stored_player[player_nr].artwork_element;
5544
5545       if (level.use_explosion_element[player_nr])
5546       {
5547         explosion_element = level.explosion_element[player_nr];
5548         artwork_element = explosion_element;
5549       }
5550     }
5551
5552     if (mode == EX_TYPE_NORMAL ||
5553         mode == EX_TYPE_CENTER ||
5554         mode == EX_TYPE_CROSS)
5555       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5556
5557     last_phase = element_info[explosion_element].explosion_delay + 1;
5558
5559     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5560     {
5561       int xx = x - ex + 1;
5562       int yy = y - ey + 1;
5563       int element;
5564
5565       if (!IN_LEV_FIELD(x, y) ||
5566           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5567           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5568         continue;
5569
5570       element = Feld[x][y];
5571
5572       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5573       {
5574         element = MovingOrBlocked2Element(x, y);
5575
5576         if (!IS_EXPLOSION_PROOF(element))
5577           RemoveMovingField(x, y);
5578       }
5579
5580       // indestructible elements can only explode in center (but not flames)
5581       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5582                                            mode == EX_TYPE_BORDER)) ||
5583           element == EL_FLAMES)
5584         continue;
5585
5586       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5587          behaviour, for example when touching a yamyam that explodes to rocks
5588          with active deadly shield, a rock is created under the player !!! */
5589       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5590 #if 0
5591       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5592           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5593            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5594 #else
5595       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5596 #endif
5597       {
5598         if (IS_ACTIVE_BOMB(element))
5599         {
5600           // re-activate things under the bomb like gate or penguin
5601           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5602           Back[x][y] = 0;
5603         }
5604
5605         continue;
5606       }
5607
5608       // save walkable background elements while explosion on same tile
5609       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5610           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5611         Back[x][y] = element;
5612
5613       // ignite explodable elements reached by other explosion
5614       if (element == EL_EXPLOSION)
5615         element = Store2[x][y];
5616
5617       if (AmoebaNr[x][y] &&
5618           (element == EL_AMOEBA_FULL ||
5619            element == EL_BD_AMOEBA ||
5620            element == EL_AMOEBA_GROWING))
5621       {
5622         AmoebaCnt[AmoebaNr[x][y]]--;
5623         AmoebaCnt2[AmoebaNr[x][y]]--;
5624       }
5625
5626       RemoveField(x, y);
5627
5628       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5629       {
5630         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5631
5632         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5633
5634         if (PLAYERINFO(ex, ey)->use_murphy)
5635           Store[x][y] = EL_EMPTY;
5636       }
5637
5638       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5639       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5640       else if (ELEM_IS_PLAYER(center_element))
5641         Store[x][y] = EL_EMPTY;
5642       else if (center_element == EL_YAMYAM)
5643         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5644       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5645         Store[x][y] = element_info[center_element].content.e[xx][yy];
5646 #if 1
5647       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5648       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5649       // otherwise) -- FIX THIS !!!
5650       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5651         Store[x][y] = element_info[element].content.e[1][1];
5652 #else
5653       else if (!CAN_EXPLODE(element))
5654         Store[x][y] = element_info[element].content.e[1][1];
5655 #endif
5656       else
5657         Store[x][y] = EL_EMPTY;
5658
5659       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5660           center_element == EL_AMOEBA_TO_DIAMOND)
5661         Store2[x][y] = element;
5662
5663       Feld[x][y] = EL_EXPLOSION;
5664       GfxElement[x][y] = artwork_element;
5665
5666       ExplodePhase[x][y] = 1;
5667       ExplodeDelay[x][y] = last_phase;
5668
5669       Stop[x][y] = TRUE;
5670     }
5671
5672     if (center_element == EL_YAMYAM)
5673       game.yamyam_content_nr =
5674         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5675
5676     return;
5677   }
5678
5679   if (Stop[ex][ey])
5680     return;
5681
5682   x = ex;
5683   y = ey;
5684
5685   if (phase == 1)
5686     GfxFrame[x][y] = 0;         // restart explosion animation
5687
5688   last_phase = ExplodeDelay[x][y];
5689
5690   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5691
5692   // this can happen if the player leaves an explosion just in time
5693   if (GfxElement[x][y] == EL_UNDEFINED)
5694     GfxElement[x][y] = EL_EMPTY;
5695
5696   border_element = Store2[x][y];
5697   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5698     border_element = StorePlayer[x][y];
5699
5700   if (phase == element_info[border_element].ignition_delay ||
5701       phase == last_phase)
5702   {
5703     boolean border_explosion = FALSE;
5704
5705     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5706         !PLAYER_EXPLOSION_PROTECTED(x, y))
5707     {
5708       KillPlayerUnlessExplosionProtected(x, y);
5709       border_explosion = TRUE;
5710     }
5711     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5712     {
5713       Feld[x][y] = Store2[x][y];
5714       Store2[x][y] = 0;
5715       Bang(x, y);
5716       border_explosion = TRUE;
5717     }
5718     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5719     {
5720       AmoebeUmwandeln(x, y);
5721       Store2[x][y] = 0;
5722       border_explosion = TRUE;
5723     }
5724
5725     // if an element just explodes due to another explosion (chain-reaction),
5726     // do not immediately end the new explosion when it was the last frame of
5727     // the explosion (as it would be done in the following "if"-statement!)
5728     if (border_explosion && phase == last_phase)
5729       return;
5730   }
5731
5732   if (phase == last_phase)
5733   {
5734     int element;
5735
5736     element = Feld[x][y] = Store[x][y];
5737     Store[x][y] = Store2[x][y] = 0;
5738     GfxElement[x][y] = EL_UNDEFINED;
5739
5740     // player can escape from explosions and might therefore be still alive
5741     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5742         element <= EL_PLAYER_IS_EXPLODING_4)
5743     {
5744       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5745       int explosion_element = EL_PLAYER_1 + player_nr;
5746       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5747       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5748
5749       if (level.use_explosion_element[player_nr])
5750         explosion_element = level.explosion_element[player_nr];
5751
5752       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5753                     element_info[explosion_element].content.e[xx][yy]);
5754     }
5755
5756     // restore probably existing indestructible background element
5757     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5758       element = Feld[x][y] = Back[x][y];
5759     Back[x][y] = 0;
5760
5761     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5762     GfxDir[x][y] = MV_NONE;
5763     ChangeDelay[x][y] = 0;
5764     ChangePage[x][y] = -1;
5765
5766     CustomValue[x][y] = 0;
5767
5768     InitField_WithBug2(x, y, FALSE);
5769
5770     TEST_DrawLevelField(x, y);
5771
5772     TestIfElementTouchesCustomElement(x, y);
5773
5774     if (GFX_CRUMBLED(element))
5775       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5776
5777     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5778       StorePlayer[x][y] = 0;
5779
5780     if (ELEM_IS_PLAYER(element))
5781       RelocatePlayer(x, y, element);
5782   }
5783   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5784   {
5785     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5786     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5787
5788     if (phase == delay)
5789       TEST_DrawLevelFieldCrumbled(x, y);
5790
5791     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5792     {
5793       DrawLevelElement(x, y, Back[x][y]);
5794       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5795     }
5796     else if (IS_WALKABLE_UNDER(Back[x][y]))
5797     {
5798       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5799       DrawLevelElementThruMask(x, y, Back[x][y]);
5800     }
5801     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5802       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5803   }
5804 }
5805
5806 static void DynaExplode(int ex, int ey)
5807 {
5808   int i, j;
5809   int dynabomb_element = Feld[ex][ey];
5810   int dynabomb_size = 1;
5811   boolean dynabomb_xl = FALSE;
5812   struct PlayerInfo *player;
5813   static int xy[4][2] =
5814   {
5815     { 0, -1 },
5816     { -1, 0 },
5817     { +1, 0 },
5818     { 0, +1 }
5819   };
5820
5821   if (IS_ACTIVE_BOMB(dynabomb_element))
5822   {
5823     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5824     dynabomb_size = player->dynabomb_size;
5825     dynabomb_xl = player->dynabomb_xl;
5826     player->dynabombs_left++;
5827   }
5828
5829   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5830
5831   for (i = 0; i < NUM_DIRECTIONS; i++)
5832   {
5833     for (j = 1; j <= dynabomb_size; j++)
5834     {
5835       int x = ex + j * xy[i][0];
5836       int y = ey + j * xy[i][1];
5837       int element;
5838
5839       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5840         break;
5841
5842       element = Feld[x][y];
5843
5844       // do not restart explosions of fields with active bombs
5845       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5846         continue;
5847
5848       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5849
5850       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5851           !IS_DIGGABLE(element) && !dynabomb_xl)
5852         break;
5853     }
5854   }
5855 }
5856
5857 void Bang(int x, int y)
5858 {
5859   int element = MovingOrBlocked2Element(x, y);
5860   int explosion_type = EX_TYPE_NORMAL;
5861
5862   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5863   {
5864     struct PlayerInfo *player = PLAYERINFO(x, y);
5865
5866     element = Feld[x][y] = player->initial_element;
5867
5868     if (level.use_explosion_element[player->index_nr])
5869     {
5870       int explosion_element = level.explosion_element[player->index_nr];
5871
5872       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5873         explosion_type = EX_TYPE_CROSS;
5874       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5875         explosion_type = EX_TYPE_CENTER;
5876     }
5877   }
5878
5879   switch (element)
5880   {
5881     case EL_BUG:
5882     case EL_SPACESHIP:
5883     case EL_BD_BUTTERFLY:
5884     case EL_BD_FIREFLY:
5885     case EL_YAMYAM:
5886     case EL_DARK_YAMYAM:
5887     case EL_ROBOT:
5888     case EL_PACMAN:
5889     case EL_MOLE:
5890       RaiseScoreElement(element);
5891       break;
5892
5893     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5894     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5895     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5896     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5897     case EL_DYNABOMB_INCREASE_NUMBER:
5898     case EL_DYNABOMB_INCREASE_SIZE:
5899     case EL_DYNABOMB_INCREASE_POWER:
5900       explosion_type = EX_TYPE_DYNA;
5901       break;
5902
5903     case EL_DC_LANDMINE:
5904       explosion_type = EX_TYPE_CENTER;
5905       break;
5906
5907     case EL_PENGUIN:
5908     case EL_LAMP:
5909     case EL_LAMP_ACTIVE:
5910     case EL_AMOEBA_TO_DIAMOND:
5911       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5912         explosion_type = EX_TYPE_CENTER;
5913       break;
5914
5915     default:
5916       if (element_info[element].explosion_type == EXPLODES_CROSS)
5917         explosion_type = EX_TYPE_CROSS;
5918       else if (element_info[element].explosion_type == EXPLODES_1X1)
5919         explosion_type = EX_TYPE_CENTER;
5920       break;
5921   }
5922
5923   if (explosion_type == EX_TYPE_DYNA)
5924     DynaExplode(x, y);
5925   else
5926     Explode(x, y, EX_PHASE_START, explosion_type);
5927
5928   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5929 }
5930
5931 static void SplashAcid(int x, int y)
5932 {
5933   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5934       (!IN_LEV_FIELD(x - 1, y - 2) ||
5935        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5936     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5937
5938   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5939       (!IN_LEV_FIELD(x + 1, y - 2) ||
5940        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5941     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5942
5943   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5944 }
5945
5946 static void InitBeltMovement(void)
5947 {
5948   static int belt_base_element[4] =
5949   {
5950     EL_CONVEYOR_BELT_1_LEFT,
5951     EL_CONVEYOR_BELT_2_LEFT,
5952     EL_CONVEYOR_BELT_3_LEFT,
5953     EL_CONVEYOR_BELT_4_LEFT
5954   };
5955   static int belt_base_active_element[4] =
5956   {
5957     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5958     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5959     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5960     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5961   };
5962
5963   int x, y, i, j;
5964
5965   // set frame order for belt animation graphic according to belt direction
5966   for (i = 0; i < NUM_BELTS; i++)
5967   {
5968     int belt_nr = i;
5969
5970     for (j = 0; j < NUM_BELT_PARTS; j++)
5971     {
5972       int element = belt_base_active_element[belt_nr] + j;
5973       int graphic_1 = el2img(element);
5974       int graphic_2 = el2panelimg(element);
5975
5976       if (game.belt_dir[i] == MV_LEFT)
5977       {
5978         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5979         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5980       }
5981       else
5982       {
5983         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5984         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5985       }
5986     }
5987   }
5988
5989   SCAN_PLAYFIELD(x, y)
5990   {
5991     int element = Feld[x][y];
5992
5993     for (i = 0; i < NUM_BELTS; i++)
5994     {
5995       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5996       {
5997         int e_belt_nr = getBeltNrFromBeltElement(element);
5998         int belt_nr = i;
5999
6000         if (e_belt_nr == belt_nr)
6001         {
6002           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6003
6004           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6005         }
6006       }
6007     }
6008   }
6009 }
6010
6011 static void ToggleBeltSwitch(int x, int y)
6012 {
6013   static int belt_base_element[4] =
6014   {
6015     EL_CONVEYOR_BELT_1_LEFT,
6016     EL_CONVEYOR_BELT_2_LEFT,
6017     EL_CONVEYOR_BELT_3_LEFT,
6018     EL_CONVEYOR_BELT_4_LEFT
6019   };
6020   static int belt_base_active_element[4] =
6021   {
6022     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6023     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6024     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6025     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6026   };
6027   static int belt_base_switch_element[4] =
6028   {
6029     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6030     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6031     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6032     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6033   };
6034   static int belt_move_dir[4] =
6035   {
6036     MV_LEFT,
6037     MV_NONE,
6038     MV_RIGHT,
6039     MV_NONE,
6040   };
6041
6042   int element = Feld[x][y];
6043   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6044   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6045   int belt_dir = belt_move_dir[belt_dir_nr];
6046   int xx, yy, i;
6047
6048   if (!IS_BELT_SWITCH(element))
6049     return;
6050
6051   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6052   game.belt_dir[belt_nr] = belt_dir;
6053
6054   if (belt_dir_nr == 3)
6055     belt_dir_nr = 1;
6056
6057   // set frame order for belt animation graphic according to belt direction
6058   for (i = 0; i < NUM_BELT_PARTS; i++)
6059   {
6060     int element = belt_base_active_element[belt_nr] + i;
6061     int graphic_1 = el2img(element);
6062     int graphic_2 = el2panelimg(element);
6063
6064     if (belt_dir == MV_LEFT)
6065     {
6066       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6067       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6068     }
6069     else
6070     {
6071       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6072       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6073     }
6074   }
6075
6076   SCAN_PLAYFIELD(xx, yy)
6077   {
6078     int element = Feld[xx][yy];
6079
6080     if (IS_BELT_SWITCH(element))
6081     {
6082       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6083
6084       if (e_belt_nr == belt_nr)
6085       {
6086         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6087         TEST_DrawLevelField(xx, yy);
6088       }
6089     }
6090     else if (IS_BELT(element) && belt_dir != MV_NONE)
6091     {
6092       int e_belt_nr = getBeltNrFromBeltElement(element);
6093
6094       if (e_belt_nr == belt_nr)
6095       {
6096         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6097
6098         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6099         TEST_DrawLevelField(xx, yy);
6100       }
6101     }
6102     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6103     {
6104       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6105
6106       if (e_belt_nr == belt_nr)
6107       {
6108         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6109
6110         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6111         TEST_DrawLevelField(xx, yy);
6112       }
6113     }
6114   }
6115 }
6116
6117 static void ToggleSwitchgateSwitch(int x, int y)
6118 {
6119   int xx, yy;
6120
6121   game.switchgate_pos = !game.switchgate_pos;
6122
6123   SCAN_PLAYFIELD(xx, yy)
6124   {
6125     int element = Feld[xx][yy];
6126
6127     if (element == EL_SWITCHGATE_SWITCH_UP)
6128     {
6129       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6130       TEST_DrawLevelField(xx, yy);
6131     }
6132     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6133     {
6134       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6135       TEST_DrawLevelField(xx, yy);
6136     }
6137     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6138     {
6139       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6140       TEST_DrawLevelField(xx, yy);
6141     }
6142     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6143     {
6144       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6145       TEST_DrawLevelField(xx, yy);
6146     }
6147     else if (element == EL_SWITCHGATE_OPEN ||
6148              element == EL_SWITCHGATE_OPENING)
6149     {
6150       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6151
6152       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6153     }
6154     else if (element == EL_SWITCHGATE_CLOSED ||
6155              element == EL_SWITCHGATE_CLOSING)
6156     {
6157       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6158
6159       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6160     }
6161   }
6162 }
6163
6164 static int getInvisibleActiveFromInvisibleElement(int element)
6165 {
6166   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6167           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6168           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6169           element);
6170 }
6171
6172 static int getInvisibleFromInvisibleActiveElement(int element)
6173 {
6174   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6175           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6176           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6177           element);
6178 }
6179
6180 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6181 {
6182   int x, y;
6183
6184   SCAN_PLAYFIELD(x, y)
6185   {
6186     int element = Feld[x][y];
6187
6188     if (element == EL_LIGHT_SWITCH &&
6189         game.light_time_left > 0)
6190     {
6191       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6192       TEST_DrawLevelField(x, y);
6193     }
6194     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6195              game.light_time_left == 0)
6196     {
6197       Feld[x][y] = EL_LIGHT_SWITCH;
6198       TEST_DrawLevelField(x, y);
6199     }
6200     else if (element == EL_EMC_DRIPPER &&
6201              game.light_time_left > 0)
6202     {
6203       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6204       TEST_DrawLevelField(x, y);
6205     }
6206     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6207              game.light_time_left == 0)
6208     {
6209       Feld[x][y] = EL_EMC_DRIPPER;
6210       TEST_DrawLevelField(x, y);
6211     }
6212     else if (element == EL_INVISIBLE_STEELWALL ||
6213              element == EL_INVISIBLE_WALL ||
6214              element == EL_INVISIBLE_SAND)
6215     {
6216       if (game.light_time_left > 0)
6217         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6218
6219       TEST_DrawLevelField(x, y);
6220
6221       // uncrumble neighbour fields, if needed
6222       if (element == EL_INVISIBLE_SAND)
6223         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6224     }
6225     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6226              element == EL_INVISIBLE_WALL_ACTIVE ||
6227              element == EL_INVISIBLE_SAND_ACTIVE)
6228     {
6229       if (game.light_time_left == 0)
6230         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6231
6232       TEST_DrawLevelField(x, y);
6233
6234       // re-crumble neighbour fields, if needed
6235       if (element == EL_INVISIBLE_SAND)
6236         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6237     }
6238   }
6239 }
6240
6241 static void RedrawAllInvisibleElementsForLenses(void)
6242 {
6243   int x, y;
6244
6245   SCAN_PLAYFIELD(x, y)
6246   {
6247     int element = Feld[x][y];
6248
6249     if (element == EL_EMC_DRIPPER &&
6250         game.lenses_time_left > 0)
6251     {
6252       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6253       TEST_DrawLevelField(x, y);
6254     }
6255     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6256              game.lenses_time_left == 0)
6257     {
6258       Feld[x][y] = EL_EMC_DRIPPER;
6259       TEST_DrawLevelField(x, y);
6260     }
6261     else if (element == EL_INVISIBLE_STEELWALL ||
6262              element == EL_INVISIBLE_WALL ||
6263              element == EL_INVISIBLE_SAND)
6264     {
6265       if (game.lenses_time_left > 0)
6266         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6267
6268       TEST_DrawLevelField(x, y);
6269
6270       // uncrumble neighbour fields, if needed
6271       if (element == EL_INVISIBLE_SAND)
6272         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6273     }
6274     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6275              element == EL_INVISIBLE_WALL_ACTIVE ||
6276              element == EL_INVISIBLE_SAND_ACTIVE)
6277     {
6278       if (game.lenses_time_left == 0)
6279         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6280
6281       TEST_DrawLevelField(x, y);
6282
6283       // re-crumble neighbour fields, if needed
6284       if (element == EL_INVISIBLE_SAND)
6285         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6286     }
6287   }
6288 }
6289
6290 static void RedrawAllInvisibleElementsForMagnifier(void)
6291 {
6292   int x, y;
6293
6294   SCAN_PLAYFIELD(x, y)
6295   {
6296     int element = Feld[x][y];
6297
6298     if (element == EL_EMC_FAKE_GRASS &&
6299         game.magnify_time_left > 0)
6300     {
6301       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6302       TEST_DrawLevelField(x, y);
6303     }
6304     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6305              game.magnify_time_left == 0)
6306     {
6307       Feld[x][y] = EL_EMC_FAKE_GRASS;
6308       TEST_DrawLevelField(x, y);
6309     }
6310     else if (IS_GATE_GRAY(element) &&
6311              game.magnify_time_left > 0)
6312     {
6313       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6314                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6315                     IS_EM_GATE_GRAY(element) ?
6316                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6317                     IS_EMC_GATE_GRAY(element) ?
6318                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6319                     IS_DC_GATE_GRAY(element) ?
6320                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6321                     element);
6322       TEST_DrawLevelField(x, y);
6323     }
6324     else if (IS_GATE_GRAY_ACTIVE(element) &&
6325              game.magnify_time_left == 0)
6326     {
6327       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6328                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6329                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6330                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6331                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6332                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6333                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6334                     EL_DC_GATE_WHITE_GRAY :
6335                     element);
6336       TEST_DrawLevelField(x, y);
6337     }
6338   }
6339 }
6340
6341 static void ToggleLightSwitch(int x, int y)
6342 {
6343   int element = Feld[x][y];
6344
6345   game.light_time_left =
6346     (element == EL_LIGHT_SWITCH ?
6347      level.time_light * FRAMES_PER_SECOND : 0);
6348
6349   RedrawAllLightSwitchesAndInvisibleElements();
6350 }
6351
6352 static void ActivateTimegateSwitch(int x, int y)
6353 {
6354   int xx, yy;
6355
6356   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6357
6358   SCAN_PLAYFIELD(xx, yy)
6359   {
6360     int element = Feld[xx][yy];
6361
6362     if (element == EL_TIMEGATE_CLOSED ||
6363         element == EL_TIMEGATE_CLOSING)
6364     {
6365       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6366       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6367     }
6368
6369     /*
6370     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6371     {
6372       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6373       TEST_DrawLevelField(xx, yy);
6374     }
6375     */
6376
6377   }
6378
6379   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6380                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6381 }
6382
6383 static void Impact(int x, int y)
6384 {
6385   boolean last_line = (y == lev_fieldy - 1);
6386   boolean object_hit = FALSE;
6387   boolean impact = (last_line || object_hit);
6388   int element = Feld[x][y];
6389   int smashed = EL_STEELWALL;
6390
6391   if (!last_line)       // check if element below was hit
6392   {
6393     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6394       return;
6395
6396     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6397                                          MovDir[x][y + 1] != MV_DOWN ||
6398                                          MovPos[x][y + 1] <= TILEY / 2));
6399
6400     // do not smash moving elements that left the smashed field in time
6401     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6402         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6403       object_hit = FALSE;
6404
6405 #if USE_QUICKSAND_IMPACT_BUGFIX
6406     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6407     {
6408       RemoveMovingField(x, y + 1);
6409       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6410       Feld[x][y + 2] = EL_ROCK;
6411       TEST_DrawLevelField(x, y + 2);
6412
6413       object_hit = TRUE;
6414     }
6415
6416     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6417     {
6418       RemoveMovingField(x, y + 1);
6419       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6420       Feld[x][y + 2] = EL_ROCK;
6421       TEST_DrawLevelField(x, y + 2);
6422
6423       object_hit = TRUE;
6424     }
6425 #endif
6426
6427     if (object_hit)
6428       smashed = MovingOrBlocked2Element(x, y + 1);
6429
6430     impact = (last_line || object_hit);
6431   }
6432
6433   if (!last_line && smashed == EL_ACID) // element falls into acid
6434   {
6435     SplashAcid(x, y + 1);
6436     return;
6437   }
6438
6439   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6440   // only reset graphic animation if graphic really changes after impact
6441   if (impact &&
6442       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6443   {
6444     ResetGfxAnimation(x, y);
6445     TEST_DrawLevelField(x, y);
6446   }
6447
6448   if (impact && CAN_EXPLODE_IMPACT(element))
6449   {
6450     Bang(x, y);
6451     return;
6452   }
6453   else if (impact && element == EL_PEARL &&
6454            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6455   {
6456     ResetGfxAnimation(x, y);
6457
6458     Feld[x][y] = EL_PEARL_BREAKING;
6459     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6460     return;
6461   }
6462   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6463   {
6464     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6465
6466     return;
6467   }
6468
6469   if (impact && element == EL_AMOEBA_DROP)
6470   {
6471     if (object_hit && IS_PLAYER(x, y + 1))
6472       KillPlayerUnlessEnemyProtected(x, y + 1);
6473     else if (object_hit && smashed == EL_PENGUIN)
6474       Bang(x, y + 1);
6475     else
6476     {
6477       Feld[x][y] = EL_AMOEBA_GROWING;
6478       Store[x][y] = EL_AMOEBA_WET;
6479
6480       ResetRandomAnimationValue(x, y);
6481     }
6482     return;
6483   }
6484
6485   if (object_hit)               // check which object was hit
6486   {
6487     if ((CAN_PASS_MAGIC_WALL(element) && 
6488          (smashed == EL_MAGIC_WALL ||
6489           smashed == EL_BD_MAGIC_WALL)) ||
6490         (CAN_PASS_DC_MAGIC_WALL(element) &&
6491          smashed == EL_DC_MAGIC_WALL))
6492     {
6493       int xx, yy;
6494       int activated_magic_wall =
6495         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6496          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6497          EL_DC_MAGIC_WALL_ACTIVE);
6498
6499       // activate magic wall / mill
6500       SCAN_PLAYFIELD(xx, yy)
6501       {
6502         if (Feld[xx][yy] == smashed)
6503           Feld[xx][yy] = activated_magic_wall;
6504       }
6505
6506       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6507       game.magic_wall_active = TRUE;
6508
6509       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6510                             SND_MAGIC_WALL_ACTIVATING :
6511                             smashed == EL_BD_MAGIC_WALL ?
6512                             SND_BD_MAGIC_WALL_ACTIVATING :
6513                             SND_DC_MAGIC_WALL_ACTIVATING));
6514     }
6515
6516     if (IS_PLAYER(x, y + 1))
6517     {
6518       if (CAN_SMASH_PLAYER(element))
6519       {
6520         KillPlayerUnlessEnemyProtected(x, y + 1);
6521         return;
6522       }
6523     }
6524     else if (smashed == EL_PENGUIN)
6525     {
6526       if (CAN_SMASH_PLAYER(element))
6527       {
6528         Bang(x, y + 1);
6529         return;
6530       }
6531     }
6532     else if (element == EL_BD_DIAMOND)
6533     {
6534       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6535       {
6536         Bang(x, y + 1);
6537         return;
6538       }
6539     }
6540     else if (((element == EL_SP_INFOTRON ||
6541                element == EL_SP_ZONK) &&
6542               (smashed == EL_SP_SNIKSNAK ||
6543                smashed == EL_SP_ELECTRON ||
6544                smashed == EL_SP_DISK_ORANGE)) ||
6545              (element == EL_SP_INFOTRON &&
6546               smashed == EL_SP_DISK_YELLOW))
6547     {
6548       Bang(x, y + 1);
6549       return;
6550     }
6551     else if (CAN_SMASH_EVERYTHING(element))
6552     {
6553       if (IS_CLASSIC_ENEMY(smashed) ||
6554           CAN_EXPLODE_SMASHED(smashed))
6555       {
6556         Bang(x, y + 1);
6557         return;
6558       }
6559       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6560       {
6561         if (smashed == EL_LAMP ||
6562             smashed == EL_LAMP_ACTIVE)
6563         {
6564           Bang(x, y + 1);
6565           return;
6566         }
6567         else if (smashed == EL_NUT)
6568         {
6569           Feld[x][y + 1] = EL_NUT_BREAKING;
6570           PlayLevelSound(x, y, SND_NUT_BREAKING);
6571           RaiseScoreElement(EL_NUT);
6572           return;
6573         }
6574         else if (smashed == EL_PEARL)
6575         {
6576           ResetGfxAnimation(x, y);
6577
6578           Feld[x][y + 1] = EL_PEARL_BREAKING;
6579           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6580           return;
6581         }
6582         else if (smashed == EL_DIAMOND)
6583         {
6584           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6585           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6586           return;
6587         }
6588         else if (IS_BELT_SWITCH(smashed))
6589         {
6590           ToggleBeltSwitch(x, y + 1);
6591         }
6592         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6593                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6594                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6595                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6596         {
6597           ToggleSwitchgateSwitch(x, y + 1);
6598         }
6599         else if (smashed == EL_LIGHT_SWITCH ||
6600                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6601         {
6602           ToggleLightSwitch(x, y + 1);
6603         }
6604         else
6605         {
6606           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6607
6608           CheckElementChangeBySide(x, y + 1, smashed, element,
6609                                    CE_SWITCHED, CH_SIDE_TOP);
6610           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6611                                             CH_SIDE_TOP);
6612         }
6613       }
6614       else
6615       {
6616         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6617       }
6618     }
6619   }
6620
6621   // play sound of magic wall / mill
6622   if (!last_line &&
6623       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6624        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6625        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6626   {
6627     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6628       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6629     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6630       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6631     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6632       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6633
6634     return;
6635   }
6636
6637   // play sound of object that hits the ground
6638   if (last_line || object_hit)
6639     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6640 }
6641
6642 static void TurnRoundExt(int x, int y)
6643 {
6644   static struct
6645   {
6646     int dx, dy;
6647   } move_xy[] =
6648   {
6649     {  0,  0 },
6650     { -1,  0 },
6651     { +1,  0 },
6652     {  0,  0 },
6653     {  0, -1 },
6654     {  0,  0 }, { 0, 0 }, { 0, 0 },
6655     {  0, +1 }
6656   };
6657   static struct
6658   {
6659     int left, right, back;
6660   } turn[] =
6661   {
6662     { 0,        0,              0        },
6663     { MV_DOWN,  MV_UP,          MV_RIGHT },
6664     { MV_UP,    MV_DOWN,        MV_LEFT  },
6665     { 0,        0,              0        },
6666     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6667     { 0,        0,              0        },
6668     { 0,        0,              0        },
6669     { 0,        0,              0        },
6670     { MV_RIGHT, MV_LEFT,        MV_UP    }
6671   };
6672
6673   int element = Feld[x][y];
6674   int move_pattern = element_info[element].move_pattern;
6675
6676   int old_move_dir = MovDir[x][y];
6677   int left_dir  = turn[old_move_dir].left;
6678   int right_dir = turn[old_move_dir].right;
6679   int back_dir  = turn[old_move_dir].back;
6680
6681   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6682   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6683   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6684   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6685
6686   int left_x  = x + left_dx,  left_y  = y + left_dy;
6687   int right_x = x + right_dx, right_y = y + right_dy;
6688   int move_x  = x + move_dx,  move_y  = y + move_dy;
6689
6690   int xx, yy;
6691
6692   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6693   {
6694     TestIfBadThingTouchesOtherBadThing(x, y);
6695
6696     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6697       MovDir[x][y] = right_dir;
6698     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6699       MovDir[x][y] = left_dir;
6700
6701     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6702       MovDelay[x][y] = 9;
6703     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6704       MovDelay[x][y] = 1;
6705   }
6706   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6707   {
6708     TestIfBadThingTouchesOtherBadThing(x, y);
6709
6710     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6711       MovDir[x][y] = left_dir;
6712     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6713       MovDir[x][y] = right_dir;
6714
6715     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6716       MovDelay[x][y] = 9;
6717     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6718       MovDelay[x][y] = 1;
6719   }
6720   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6721   {
6722     TestIfBadThingTouchesOtherBadThing(x, y);
6723
6724     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6725       MovDir[x][y] = left_dir;
6726     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6727       MovDir[x][y] = right_dir;
6728
6729     if (MovDir[x][y] != old_move_dir)
6730       MovDelay[x][y] = 9;
6731   }
6732   else if (element == EL_YAMYAM)
6733   {
6734     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6735     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6736
6737     if (can_turn_left && can_turn_right)
6738       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6739     else if (can_turn_left)
6740       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6741     else if (can_turn_right)
6742       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6743     else
6744       MovDir[x][y] = back_dir;
6745
6746     MovDelay[x][y] = 16 + 16 * RND(3);
6747   }
6748   else if (element == EL_DARK_YAMYAM)
6749   {
6750     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6751                                                          left_x, left_y);
6752     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6753                                                          right_x, right_y);
6754
6755     if (can_turn_left && can_turn_right)
6756       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6757     else if (can_turn_left)
6758       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6759     else if (can_turn_right)
6760       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6761     else
6762       MovDir[x][y] = back_dir;
6763
6764     MovDelay[x][y] = 16 + 16 * RND(3);
6765   }
6766   else if (element == EL_PACMAN)
6767   {
6768     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6769     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6770
6771     if (can_turn_left && can_turn_right)
6772       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6773     else if (can_turn_left)
6774       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6775     else if (can_turn_right)
6776       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6777     else
6778       MovDir[x][y] = back_dir;
6779
6780     MovDelay[x][y] = 6 + RND(40);
6781   }
6782   else if (element == EL_PIG)
6783   {
6784     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6785     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6786     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6787     boolean should_turn_left, should_turn_right, should_move_on;
6788     int rnd_value = 24;
6789     int rnd = RND(rnd_value);
6790
6791     should_turn_left = (can_turn_left &&
6792                         (!can_move_on ||
6793                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6794                                                    y + back_dy + left_dy)));
6795     should_turn_right = (can_turn_right &&
6796                          (!can_move_on ||
6797                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6798                                                     y + back_dy + right_dy)));
6799     should_move_on = (can_move_on &&
6800                       (!can_turn_left ||
6801                        !can_turn_right ||
6802                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6803                                                  y + move_dy + left_dy) ||
6804                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6805                                                  y + move_dy + right_dy)));
6806
6807     if (should_turn_left || should_turn_right || should_move_on)
6808     {
6809       if (should_turn_left && should_turn_right && should_move_on)
6810         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6811                         rnd < 2 * rnd_value / 3 ? right_dir :
6812                         old_move_dir);
6813       else if (should_turn_left && should_turn_right)
6814         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6815       else if (should_turn_left && should_move_on)
6816         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6817       else if (should_turn_right && should_move_on)
6818         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6819       else if (should_turn_left)
6820         MovDir[x][y] = left_dir;
6821       else if (should_turn_right)
6822         MovDir[x][y] = right_dir;
6823       else if (should_move_on)
6824         MovDir[x][y] = old_move_dir;
6825     }
6826     else if (can_move_on && rnd > rnd_value / 8)
6827       MovDir[x][y] = old_move_dir;
6828     else if (can_turn_left && can_turn_right)
6829       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6830     else if (can_turn_left && rnd > rnd_value / 8)
6831       MovDir[x][y] = left_dir;
6832     else if (can_turn_right && rnd > rnd_value/8)
6833       MovDir[x][y] = right_dir;
6834     else
6835       MovDir[x][y] = back_dir;
6836
6837     xx = x + move_xy[MovDir[x][y]].dx;
6838     yy = y + move_xy[MovDir[x][y]].dy;
6839
6840     if (!IN_LEV_FIELD(xx, yy) ||
6841         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6842       MovDir[x][y] = old_move_dir;
6843
6844     MovDelay[x][y] = 0;
6845   }
6846   else if (element == EL_DRAGON)
6847   {
6848     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6849     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6850     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6851     int rnd_value = 24;
6852     int rnd = RND(rnd_value);
6853
6854     if (can_move_on && rnd > rnd_value / 8)
6855       MovDir[x][y] = old_move_dir;
6856     else if (can_turn_left && can_turn_right)
6857       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6858     else if (can_turn_left && rnd > rnd_value / 8)
6859       MovDir[x][y] = left_dir;
6860     else if (can_turn_right && rnd > rnd_value / 8)
6861       MovDir[x][y] = right_dir;
6862     else
6863       MovDir[x][y] = back_dir;
6864
6865     xx = x + move_xy[MovDir[x][y]].dx;
6866     yy = y + move_xy[MovDir[x][y]].dy;
6867
6868     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6869       MovDir[x][y] = old_move_dir;
6870
6871     MovDelay[x][y] = 0;
6872   }
6873   else if (element == EL_MOLE)
6874   {
6875     boolean can_move_on =
6876       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6877                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6878                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6879     if (!can_move_on)
6880     {
6881       boolean can_turn_left =
6882         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6883                               IS_AMOEBOID(Feld[left_x][left_y])));
6884
6885       boolean can_turn_right =
6886         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6887                               IS_AMOEBOID(Feld[right_x][right_y])));
6888
6889       if (can_turn_left && can_turn_right)
6890         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6891       else if (can_turn_left)
6892         MovDir[x][y] = left_dir;
6893       else
6894         MovDir[x][y] = right_dir;
6895     }
6896
6897     if (MovDir[x][y] != old_move_dir)
6898       MovDelay[x][y] = 9;
6899   }
6900   else if (element == EL_BALLOON)
6901   {
6902     MovDir[x][y] = game.wind_direction;
6903     MovDelay[x][y] = 0;
6904   }
6905   else if (element == EL_SPRING)
6906   {
6907     if (MovDir[x][y] & MV_HORIZONTAL)
6908     {
6909       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6910           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6911       {
6912         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6913         ResetGfxAnimation(move_x, move_y);
6914         TEST_DrawLevelField(move_x, move_y);
6915
6916         MovDir[x][y] = back_dir;
6917       }
6918       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6919                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6920         MovDir[x][y] = MV_NONE;
6921     }
6922
6923     MovDelay[x][y] = 0;
6924   }
6925   else if (element == EL_ROBOT ||
6926            element == EL_SATELLITE ||
6927            element == EL_PENGUIN ||
6928            element == EL_EMC_ANDROID)
6929   {
6930     int attr_x = -1, attr_y = -1;
6931
6932     if (game.all_players_gone)
6933     {
6934       attr_x = game.exit_x;
6935       attr_y = game.exit_y;
6936     }
6937     else
6938     {
6939       int i;
6940
6941       for (i = 0; i < MAX_PLAYERS; i++)
6942       {
6943         struct PlayerInfo *player = &stored_player[i];
6944         int jx = player->jx, jy = player->jy;
6945
6946         if (!player->active)
6947           continue;
6948
6949         if (attr_x == -1 ||
6950             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6951         {
6952           attr_x = jx;
6953           attr_y = jy;
6954         }
6955       }
6956     }
6957
6958     if (element == EL_ROBOT &&
6959         game.robot_wheel_x >= 0 &&
6960         game.robot_wheel_y >= 0 &&
6961         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
6962          game.engine_version < VERSION_IDENT(3,1,0,0)))
6963     {
6964       attr_x = game.robot_wheel_x;
6965       attr_y = game.robot_wheel_y;
6966     }
6967
6968     if (element == EL_PENGUIN)
6969     {
6970       int i;
6971       static int xy[4][2] =
6972       {
6973         { 0, -1 },
6974         { -1, 0 },
6975         { +1, 0 },
6976         { 0, +1 }
6977       };
6978
6979       for (i = 0; i < NUM_DIRECTIONS; i++)
6980       {
6981         int ex = x + xy[i][0];
6982         int ey = y + xy[i][1];
6983
6984         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6985                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6986                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6987                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6988         {
6989           attr_x = ex;
6990           attr_y = ey;
6991           break;
6992         }
6993       }
6994     }
6995
6996     MovDir[x][y] = MV_NONE;
6997     if (attr_x < x)
6998       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
6999     else if (attr_x > x)
7000       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7001     if (attr_y < y)
7002       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7003     else if (attr_y > y)
7004       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7005
7006     if (element == EL_ROBOT)
7007     {
7008       int newx, newy;
7009
7010       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7011         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7012       Moving2Blocked(x, y, &newx, &newy);
7013
7014       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7015         MovDelay[x][y] = 8 + 8 * !RND(3);
7016       else
7017         MovDelay[x][y] = 16;
7018     }
7019     else if (element == EL_PENGUIN)
7020     {
7021       int newx, newy;
7022
7023       MovDelay[x][y] = 1;
7024
7025       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7026       {
7027         boolean first_horiz = RND(2);
7028         int new_move_dir = MovDir[x][y];
7029
7030         MovDir[x][y] =
7031           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7032         Moving2Blocked(x, y, &newx, &newy);
7033
7034         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7035           return;
7036
7037         MovDir[x][y] =
7038           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7039         Moving2Blocked(x, y, &newx, &newy);
7040
7041         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7042           return;
7043
7044         MovDir[x][y] = old_move_dir;
7045         return;
7046       }
7047     }
7048     else if (element == EL_SATELLITE)
7049     {
7050       int newx, newy;
7051
7052       MovDelay[x][y] = 1;
7053
7054       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7055       {
7056         boolean first_horiz = RND(2);
7057         int new_move_dir = MovDir[x][y];
7058
7059         MovDir[x][y] =
7060           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7061         Moving2Blocked(x, y, &newx, &newy);
7062
7063         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7064           return;
7065
7066         MovDir[x][y] =
7067           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7068         Moving2Blocked(x, y, &newx, &newy);
7069
7070         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7071           return;
7072
7073         MovDir[x][y] = old_move_dir;
7074         return;
7075       }
7076     }
7077     else if (element == EL_EMC_ANDROID)
7078     {
7079       static int check_pos[16] =
7080       {
7081         -1,             //  0 => (invalid)
7082         7,              //  1 => MV_LEFT
7083         3,              //  2 => MV_RIGHT
7084         -1,             //  3 => (invalid)
7085         1,              //  4 =>            MV_UP
7086         0,              //  5 => MV_LEFT  | MV_UP
7087         2,              //  6 => MV_RIGHT | MV_UP
7088         -1,             //  7 => (invalid)
7089         5,              //  8 =>            MV_DOWN
7090         6,              //  9 => MV_LEFT  | MV_DOWN
7091         4,              // 10 => MV_RIGHT | MV_DOWN
7092         -1,             // 11 => (invalid)
7093         -1,             // 12 => (invalid)
7094         -1,             // 13 => (invalid)
7095         -1,             // 14 => (invalid)
7096         -1,             // 15 => (invalid)
7097       };
7098       static struct
7099       {
7100         int dx, dy;
7101         int dir;
7102       } check_xy[8] =
7103       {
7104         { -1, -1,       MV_LEFT  | MV_UP   },
7105         {  0, -1,                  MV_UP   },
7106         { +1, -1,       MV_RIGHT | MV_UP   },
7107         { +1,  0,       MV_RIGHT           },
7108         { +1, +1,       MV_RIGHT | MV_DOWN },
7109         {  0, +1,                  MV_DOWN },
7110         { -1, +1,       MV_LEFT  | MV_DOWN },
7111         { -1,  0,       MV_LEFT            },
7112       };
7113       int start_pos, check_order;
7114       boolean can_clone = FALSE;
7115       int i;
7116
7117       // check if there is any free field around current position
7118       for (i = 0; i < 8; i++)
7119       {
7120         int newx = x + check_xy[i].dx;
7121         int newy = y + check_xy[i].dy;
7122
7123         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7124         {
7125           can_clone = TRUE;
7126
7127           break;
7128         }
7129       }
7130
7131       if (can_clone)            // randomly find an element to clone
7132       {
7133         can_clone = FALSE;
7134
7135         start_pos = check_pos[RND(8)];
7136         check_order = (RND(2) ? -1 : +1);
7137
7138         for (i = 0; i < 8; i++)
7139         {
7140           int pos_raw = start_pos + i * check_order;
7141           int pos = (pos_raw + 8) % 8;
7142           int newx = x + check_xy[pos].dx;
7143           int newy = y + check_xy[pos].dy;
7144
7145           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7146           {
7147             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7148             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7149
7150             Store[x][y] = Feld[newx][newy];
7151
7152             can_clone = TRUE;
7153
7154             break;
7155           }
7156         }
7157       }
7158
7159       if (can_clone)            // randomly find a direction to move
7160       {
7161         can_clone = FALSE;
7162
7163         start_pos = check_pos[RND(8)];
7164         check_order = (RND(2) ? -1 : +1);
7165
7166         for (i = 0; i < 8; i++)
7167         {
7168           int pos_raw = start_pos + i * check_order;
7169           int pos = (pos_raw + 8) % 8;
7170           int newx = x + check_xy[pos].dx;
7171           int newy = y + check_xy[pos].dy;
7172           int new_move_dir = check_xy[pos].dir;
7173
7174           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7175           {
7176             MovDir[x][y] = new_move_dir;
7177             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7178
7179             can_clone = TRUE;
7180
7181             break;
7182           }
7183         }
7184       }
7185
7186       if (can_clone)            // cloning and moving successful
7187         return;
7188
7189       // cannot clone -- try to move towards player
7190
7191       start_pos = check_pos[MovDir[x][y] & 0x0f];
7192       check_order = (RND(2) ? -1 : +1);
7193
7194       for (i = 0; i < 3; i++)
7195       {
7196         // first check start_pos, then previous/next or (next/previous) pos
7197         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7198         int pos = (pos_raw + 8) % 8;
7199         int newx = x + check_xy[pos].dx;
7200         int newy = y + check_xy[pos].dy;
7201         int new_move_dir = check_xy[pos].dir;
7202
7203         if (IS_PLAYER(newx, newy))
7204           break;
7205
7206         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7207         {
7208           MovDir[x][y] = new_move_dir;
7209           MovDelay[x][y] = level.android_move_time * 8 + 1;
7210
7211           break;
7212         }
7213       }
7214     }
7215   }
7216   else if (move_pattern == MV_TURNING_LEFT ||
7217            move_pattern == MV_TURNING_RIGHT ||
7218            move_pattern == MV_TURNING_LEFT_RIGHT ||
7219            move_pattern == MV_TURNING_RIGHT_LEFT ||
7220            move_pattern == MV_TURNING_RANDOM ||
7221            move_pattern == MV_ALL_DIRECTIONS)
7222   {
7223     boolean can_turn_left =
7224       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7225     boolean can_turn_right =
7226       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7227
7228     if (element_info[element].move_stepsize == 0)       // "not moving"
7229       return;
7230
7231     if (move_pattern == MV_TURNING_LEFT)
7232       MovDir[x][y] = left_dir;
7233     else if (move_pattern == MV_TURNING_RIGHT)
7234       MovDir[x][y] = right_dir;
7235     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7236       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7237     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7238       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7239     else if (move_pattern == MV_TURNING_RANDOM)
7240       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7241                       can_turn_right && !can_turn_left ? right_dir :
7242                       RND(2) ? left_dir : right_dir);
7243     else if (can_turn_left && can_turn_right)
7244       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7245     else if (can_turn_left)
7246       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7247     else if (can_turn_right)
7248       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7249     else
7250       MovDir[x][y] = back_dir;
7251
7252     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7253   }
7254   else if (move_pattern == MV_HORIZONTAL ||
7255            move_pattern == MV_VERTICAL)
7256   {
7257     if (move_pattern & old_move_dir)
7258       MovDir[x][y] = back_dir;
7259     else if (move_pattern == MV_HORIZONTAL)
7260       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7261     else if (move_pattern == MV_VERTICAL)
7262       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7263
7264     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7265   }
7266   else if (move_pattern & MV_ANY_DIRECTION)
7267   {
7268     MovDir[x][y] = move_pattern;
7269     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7270   }
7271   else if (move_pattern & MV_WIND_DIRECTION)
7272   {
7273     MovDir[x][y] = game.wind_direction;
7274     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7275   }
7276   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7277   {
7278     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7279       MovDir[x][y] = left_dir;
7280     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7281       MovDir[x][y] = right_dir;
7282
7283     if (MovDir[x][y] != old_move_dir)
7284       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7285   }
7286   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7287   {
7288     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7289       MovDir[x][y] = right_dir;
7290     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7291       MovDir[x][y] = left_dir;
7292
7293     if (MovDir[x][y] != old_move_dir)
7294       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7295   }
7296   else if (move_pattern == MV_TOWARDS_PLAYER ||
7297            move_pattern == MV_AWAY_FROM_PLAYER)
7298   {
7299     int attr_x = -1, attr_y = -1;
7300     int newx, newy;
7301     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7302
7303     if (game.all_players_gone)
7304     {
7305       attr_x = game.exit_x;
7306       attr_y = game.exit_y;
7307     }
7308     else
7309     {
7310       int i;
7311
7312       for (i = 0; i < MAX_PLAYERS; i++)
7313       {
7314         struct PlayerInfo *player = &stored_player[i];
7315         int jx = player->jx, jy = player->jy;
7316
7317         if (!player->active)
7318           continue;
7319
7320         if (attr_x == -1 ||
7321             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7322         {
7323           attr_x = jx;
7324           attr_y = jy;
7325         }
7326       }
7327     }
7328
7329     MovDir[x][y] = MV_NONE;
7330     if (attr_x < x)
7331       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7332     else if (attr_x > x)
7333       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7334     if (attr_y < y)
7335       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7336     else if (attr_y > y)
7337       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7338
7339     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7340
7341     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7342     {
7343       boolean first_horiz = RND(2);
7344       int new_move_dir = MovDir[x][y];
7345
7346       if (element_info[element].move_stepsize == 0)     // "not moving"
7347       {
7348         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7349         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7350
7351         return;
7352       }
7353
7354       MovDir[x][y] =
7355         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7356       Moving2Blocked(x, y, &newx, &newy);
7357
7358       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7359         return;
7360
7361       MovDir[x][y] =
7362         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7363       Moving2Blocked(x, y, &newx, &newy);
7364
7365       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7366         return;
7367
7368       MovDir[x][y] = old_move_dir;
7369     }
7370   }
7371   else if (move_pattern == MV_WHEN_PUSHED ||
7372            move_pattern == MV_WHEN_DROPPED)
7373   {
7374     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7375       MovDir[x][y] = MV_NONE;
7376
7377     MovDelay[x][y] = 0;
7378   }
7379   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7380   {
7381     static int test_xy[7][2] =
7382     {
7383       { 0, -1 },
7384       { -1, 0 },
7385       { +1, 0 },
7386       { 0, +1 },
7387       { 0, -1 },
7388       { -1, 0 },
7389       { +1, 0 },
7390     };
7391     static int test_dir[7] =
7392     {
7393       MV_UP,
7394       MV_LEFT,
7395       MV_RIGHT,
7396       MV_DOWN,
7397       MV_UP,
7398       MV_LEFT,
7399       MV_RIGHT,
7400     };
7401     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7402     int move_preference = -1000000;     // start with very low preference
7403     int new_move_dir = MV_NONE;
7404     int start_test = RND(4);
7405     int i;
7406
7407     for (i = 0; i < NUM_DIRECTIONS; i++)
7408     {
7409       int move_dir = test_dir[start_test + i];
7410       int move_dir_preference;
7411
7412       xx = x + test_xy[start_test + i][0];
7413       yy = y + test_xy[start_test + i][1];
7414
7415       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7416           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7417       {
7418         new_move_dir = move_dir;
7419
7420         break;
7421       }
7422
7423       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7424         continue;
7425
7426       move_dir_preference = -1 * RunnerVisit[xx][yy];
7427       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7428         move_dir_preference = PlayerVisit[xx][yy];
7429
7430       if (move_dir_preference > move_preference)
7431       {
7432         // prefer field that has not been visited for the longest time
7433         move_preference = move_dir_preference;
7434         new_move_dir = move_dir;
7435       }
7436       else if (move_dir_preference == move_preference &&
7437                move_dir == old_move_dir)
7438       {
7439         // prefer last direction when all directions are preferred equally
7440         move_preference = move_dir_preference;
7441         new_move_dir = move_dir;
7442       }
7443     }
7444
7445     MovDir[x][y] = new_move_dir;
7446     if (old_move_dir != new_move_dir)
7447       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7448   }
7449 }
7450
7451 static void TurnRound(int x, int y)
7452 {
7453   int direction = MovDir[x][y];
7454
7455   TurnRoundExt(x, y);
7456
7457   GfxDir[x][y] = MovDir[x][y];
7458
7459   if (direction != MovDir[x][y])
7460     GfxFrame[x][y] = 0;
7461
7462   if (MovDelay[x][y])
7463     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7464
7465   ResetGfxFrame(x, y);
7466 }
7467
7468 static boolean JustBeingPushed(int x, int y)
7469 {
7470   int i;
7471
7472   for (i = 0; i < MAX_PLAYERS; i++)
7473   {
7474     struct PlayerInfo *player = &stored_player[i];
7475
7476     if (player->active && player->is_pushing && player->MovPos)
7477     {
7478       int next_jx = player->jx + (player->jx - player->last_jx);
7479       int next_jy = player->jy + (player->jy - player->last_jy);
7480
7481       if (x == next_jx && y == next_jy)
7482         return TRUE;
7483     }
7484   }
7485
7486   return FALSE;
7487 }
7488
7489 static void StartMoving(int x, int y)
7490 {
7491   boolean started_moving = FALSE;       // some elements can fall _and_ move
7492   int element = Feld[x][y];
7493
7494   if (Stop[x][y])
7495     return;
7496
7497   if (MovDelay[x][y] == 0)
7498     GfxAction[x][y] = ACTION_DEFAULT;
7499
7500   if (CAN_FALL(element) && y < lev_fieldy - 1)
7501   {
7502     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7503         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7504       if (JustBeingPushed(x, y))
7505         return;
7506
7507     if (element == EL_QUICKSAND_FULL)
7508     {
7509       if (IS_FREE(x, y + 1))
7510       {
7511         InitMovingField(x, y, MV_DOWN);
7512         started_moving = TRUE;
7513
7514         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7515 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7516         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7517           Store[x][y] = EL_ROCK;
7518 #else
7519         Store[x][y] = EL_ROCK;
7520 #endif
7521
7522         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7523       }
7524       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7525       {
7526         if (!MovDelay[x][y])
7527         {
7528           MovDelay[x][y] = TILEY + 1;
7529
7530           ResetGfxAnimation(x, y);
7531           ResetGfxAnimation(x, y + 1);
7532         }
7533
7534         if (MovDelay[x][y])
7535         {
7536           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7537           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7538
7539           MovDelay[x][y]--;
7540           if (MovDelay[x][y])
7541             return;
7542         }
7543
7544         Feld[x][y] = EL_QUICKSAND_EMPTY;
7545         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7546         Store[x][y + 1] = Store[x][y];
7547         Store[x][y] = 0;
7548
7549         PlayLevelSoundAction(x, y, ACTION_FILLING);
7550       }
7551       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7552       {
7553         if (!MovDelay[x][y])
7554         {
7555           MovDelay[x][y] = TILEY + 1;
7556
7557           ResetGfxAnimation(x, y);
7558           ResetGfxAnimation(x, y + 1);
7559         }
7560
7561         if (MovDelay[x][y])
7562         {
7563           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7564           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7565
7566           MovDelay[x][y]--;
7567           if (MovDelay[x][y])
7568             return;
7569         }
7570
7571         Feld[x][y] = EL_QUICKSAND_EMPTY;
7572         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7573         Store[x][y + 1] = Store[x][y];
7574         Store[x][y] = 0;
7575
7576         PlayLevelSoundAction(x, y, ACTION_FILLING);
7577       }
7578     }
7579     else if (element == EL_QUICKSAND_FAST_FULL)
7580     {
7581       if (IS_FREE(x, y + 1))
7582       {
7583         InitMovingField(x, y, MV_DOWN);
7584         started_moving = TRUE;
7585
7586         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7587 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7588         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7589           Store[x][y] = EL_ROCK;
7590 #else
7591         Store[x][y] = EL_ROCK;
7592 #endif
7593
7594         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7595       }
7596       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7597       {
7598         if (!MovDelay[x][y])
7599         {
7600           MovDelay[x][y] = TILEY + 1;
7601
7602           ResetGfxAnimation(x, y);
7603           ResetGfxAnimation(x, y + 1);
7604         }
7605
7606         if (MovDelay[x][y])
7607         {
7608           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7609           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7610
7611           MovDelay[x][y]--;
7612           if (MovDelay[x][y])
7613             return;
7614         }
7615
7616         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7617         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7618         Store[x][y + 1] = Store[x][y];
7619         Store[x][y] = 0;
7620
7621         PlayLevelSoundAction(x, y, ACTION_FILLING);
7622       }
7623       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7624       {
7625         if (!MovDelay[x][y])
7626         {
7627           MovDelay[x][y] = TILEY + 1;
7628
7629           ResetGfxAnimation(x, y);
7630           ResetGfxAnimation(x, y + 1);
7631         }
7632
7633         if (MovDelay[x][y])
7634         {
7635           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7636           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7637
7638           MovDelay[x][y]--;
7639           if (MovDelay[x][y])
7640             return;
7641         }
7642
7643         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7644         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7645         Store[x][y + 1] = Store[x][y];
7646         Store[x][y] = 0;
7647
7648         PlayLevelSoundAction(x, y, ACTION_FILLING);
7649       }
7650     }
7651     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7652              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7653     {
7654       InitMovingField(x, y, MV_DOWN);
7655       started_moving = TRUE;
7656
7657       Feld[x][y] = EL_QUICKSAND_FILLING;
7658       Store[x][y] = element;
7659
7660       PlayLevelSoundAction(x, y, ACTION_FILLING);
7661     }
7662     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7663              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7664     {
7665       InitMovingField(x, y, MV_DOWN);
7666       started_moving = TRUE;
7667
7668       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7669       Store[x][y] = element;
7670
7671       PlayLevelSoundAction(x, y, ACTION_FILLING);
7672     }
7673     else if (element == EL_MAGIC_WALL_FULL)
7674     {
7675       if (IS_FREE(x, y + 1))
7676       {
7677         InitMovingField(x, y, MV_DOWN);
7678         started_moving = TRUE;
7679
7680         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7681         Store[x][y] = EL_CHANGED(Store[x][y]);
7682       }
7683       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7684       {
7685         if (!MovDelay[x][y])
7686           MovDelay[x][y] = TILEY / 4 + 1;
7687
7688         if (MovDelay[x][y])
7689         {
7690           MovDelay[x][y]--;
7691           if (MovDelay[x][y])
7692             return;
7693         }
7694
7695         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7696         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7697         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7698         Store[x][y] = 0;
7699       }
7700     }
7701     else if (element == EL_BD_MAGIC_WALL_FULL)
7702     {
7703       if (IS_FREE(x, y + 1))
7704       {
7705         InitMovingField(x, y, MV_DOWN);
7706         started_moving = TRUE;
7707
7708         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7709         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7710       }
7711       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7712       {
7713         if (!MovDelay[x][y])
7714           MovDelay[x][y] = TILEY / 4 + 1;
7715
7716         if (MovDelay[x][y])
7717         {
7718           MovDelay[x][y]--;
7719           if (MovDelay[x][y])
7720             return;
7721         }
7722
7723         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7724         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7725         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7726         Store[x][y] = 0;
7727       }
7728     }
7729     else if (element == EL_DC_MAGIC_WALL_FULL)
7730     {
7731       if (IS_FREE(x, y + 1))
7732       {
7733         InitMovingField(x, y, MV_DOWN);
7734         started_moving = TRUE;
7735
7736         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7737         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7738       }
7739       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7740       {
7741         if (!MovDelay[x][y])
7742           MovDelay[x][y] = TILEY / 4 + 1;
7743
7744         if (MovDelay[x][y])
7745         {
7746           MovDelay[x][y]--;
7747           if (MovDelay[x][y])
7748             return;
7749         }
7750
7751         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7752         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7753         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7754         Store[x][y] = 0;
7755       }
7756     }
7757     else if ((CAN_PASS_MAGIC_WALL(element) &&
7758               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7759                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7760              (CAN_PASS_DC_MAGIC_WALL(element) &&
7761               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7762
7763     {
7764       InitMovingField(x, y, MV_DOWN);
7765       started_moving = TRUE;
7766
7767       Feld[x][y] =
7768         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7769          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7770          EL_DC_MAGIC_WALL_FILLING);
7771       Store[x][y] = element;
7772     }
7773     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7774     {
7775       SplashAcid(x, y + 1);
7776
7777       InitMovingField(x, y, MV_DOWN);
7778       started_moving = TRUE;
7779
7780       Store[x][y] = EL_ACID;
7781     }
7782     else if (
7783              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7784               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7785              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7786               CAN_FALL(element) && WasJustFalling[x][y] &&
7787               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7788
7789              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7790               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7791               (Feld[x][y + 1] == EL_BLOCKED)))
7792     {
7793       /* this is needed for a special case not covered by calling "Impact()"
7794          from "ContinueMoving()": if an element moves to a tile directly below
7795          another element which was just falling on that tile (which was empty
7796          in the previous frame), the falling element above would just stop
7797          instead of smashing the element below (in previous version, the above
7798          element was just checked for "moving" instead of "falling", resulting
7799          in incorrect smashes caused by horizontal movement of the above
7800          element; also, the case of the player being the element to smash was
7801          simply not covered here... :-/ ) */
7802
7803       CheckCollision[x][y] = 0;
7804       CheckImpact[x][y] = 0;
7805
7806       Impact(x, y);
7807     }
7808     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7809     {
7810       if (MovDir[x][y] == MV_NONE)
7811       {
7812         InitMovingField(x, y, MV_DOWN);
7813         started_moving = TRUE;
7814       }
7815     }
7816     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7817     {
7818       if (WasJustFalling[x][y]) // prevent animation from being restarted
7819         MovDir[x][y] = MV_DOWN;
7820
7821       InitMovingField(x, y, MV_DOWN);
7822       started_moving = TRUE;
7823     }
7824     else if (element == EL_AMOEBA_DROP)
7825     {
7826       Feld[x][y] = EL_AMOEBA_GROWING;
7827       Store[x][y] = EL_AMOEBA_WET;
7828     }
7829     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7830               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7831              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7832              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7833     {
7834       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7835                                 (IS_FREE(x - 1, y + 1) ||
7836                                  Feld[x - 1][y + 1] == EL_ACID));
7837       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7838                                 (IS_FREE(x + 1, y + 1) ||
7839                                  Feld[x + 1][y + 1] == EL_ACID));
7840       boolean can_fall_any  = (can_fall_left || can_fall_right);
7841       boolean can_fall_both = (can_fall_left && can_fall_right);
7842       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7843
7844       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7845       {
7846         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7847           can_fall_right = FALSE;
7848         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7849           can_fall_left = FALSE;
7850         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7851           can_fall_right = FALSE;
7852         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7853           can_fall_left = FALSE;
7854
7855         can_fall_any  = (can_fall_left || can_fall_right);
7856         can_fall_both = FALSE;
7857       }
7858
7859       if (can_fall_both)
7860       {
7861         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7862           can_fall_right = FALSE;       // slip down on left side
7863         else
7864           can_fall_left = !(can_fall_right = RND(2));
7865
7866         can_fall_both = FALSE;
7867       }
7868
7869       if (can_fall_any)
7870       {
7871         // if not determined otherwise, prefer left side for slipping down
7872         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7873         started_moving = TRUE;
7874       }
7875     }
7876     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7877     {
7878       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7879       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7880       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7881       int belt_dir = game.belt_dir[belt_nr];
7882
7883       if ((belt_dir == MV_LEFT  && left_is_free) ||
7884           (belt_dir == MV_RIGHT && right_is_free))
7885       {
7886         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7887
7888         InitMovingField(x, y, belt_dir);
7889         started_moving = TRUE;
7890
7891         Pushed[x][y] = TRUE;
7892         Pushed[nextx][y] = TRUE;
7893
7894         GfxAction[x][y] = ACTION_DEFAULT;
7895       }
7896       else
7897       {
7898         MovDir[x][y] = 0;       // if element was moving, stop it
7899       }
7900     }
7901   }
7902
7903   // not "else if" because of elements that can fall and move (EL_SPRING)
7904   if (CAN_MOVE(element) && !started_moving)
7905   {
7906     int move_pattern = element_info[element].move_pattern;
7907     int newx, newy;
7908
7909     Moving2Blocked(x, y, &newx, &newy);
7910
7911     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7912       return;
7913
7914     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7915         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7916     {
7917       WasJustMoving[x][y] = 0;
7918       CheckCollision[x][y] = 0;
7919
7920       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7921
7922       if (Feld[x][y] != element)        // element has changed
7923         return;
7924     }
7925
7926     if (!MovDelay[x][y])        // start new movement phase
7927     {
7928       // all objects that can change their move direction after each step
7929       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7930
7931       if (element != EL_YAMYAM &&
7932           element != EL_DARK_YAMYAM &&
7933           element != EL_PACMAN &&
7934           !(move_pattern & MV_ANY_DIRECTION) &&
7935           move_pattern != MV_TURNING_LEFT &&
7936           move_pattern != MV_TURNING_RIGHT &&
7937           move_pattern != MV_TURNING_LEFT_RIGHT &&
7938           move_pattern != MV_TURNING_RIGHT_LEFT &&
7939           move_pattern != MV_TURNING_RANDOM)
7940       {
7941         TurnRound(x, y);
7942
7943         if (MovDelay[x][y] && (element == EL_BUG ||
7944                                element == EL_SPACESHIP ||
7945                                element == EL_SP_SNIKSNAK ||
7946                                element == EL_SP_ELECTRON ||
7947                                element == EL_MOLE))
7948           TEST_DrawLevelField(x, y);
7949       }
7950     }
7951
7952     if (MovDelay[x][y])         // wait some time before next movement
7953     {
7954       MovDelay[x][y]--;
7955
7956       if (element == EL_ROBOT ||
7957           element == EL_YAMYAM ||
7958           element == EL_DARK_YAMYAM)
7959       {
7960         DrawLevelElementAnimationIfNeeded(x, y, element);
7961         PlayLevelSoundAction(x, y, ACTION_WAITING);
7962       }
7963       else if (element == EL_SP_ELECTRON)
7964         DrawLevelElementAnimationIfNeeded(x, y, element);
7965       else if (element == EL_DRAGON)
7966       {
7967         int i;
7968         int dir = MovDir[x][y];
7969         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7970         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7971         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7972                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7973                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7974                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7975         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7976
7977         GfxAction[x][y] = ACTION_ATTACKING;
7978
7979         if (IS_PLAYER(x, y))
7980           DrawPlayerField(x, y);
7981         else
7982           TEST_DrawLevelField(x, y);
7983
7984         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7985
7986         for (i = 1; i <= 3; i++)
7987         {
7988           int xx = x + i * dx;
7989           int yy = y + i * dy;
7990           int sx = SCREENX(xx);
7991           int sy = SCREENY(yy);
7992           int flame_graphic = graphic + (i - 1);
7993
7994           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7995             break;
7996
7997           if (MovDelay[x][y])
7998           {
7999             int flamed = MovingOrBlocked2Element(xx, yy);
8000
8001             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8002               Bang(xx, yy);
8003             else
8004               RemoveMovingField(xx, yy);
8005
8006             ChangeDelay[xx][yy] = 0;
8007
8008             Feld[xx][yy] = EL_FLAMES;
8009
8010             if (IN_SCR_FIELD(sx, sy))
8011             {
8012               TEST_DrawLevelFieldCrumbled(xx, yy);
8013               DrawGraphic(sx, sy, flame_graphic, frame);
8014             }
8015           }
8016           else
8017           {
8018             if (Feld[xx][yy] == EL_FLAMES)
8019               Feld[xx][yy] = EL_EMPTY;
8020             TEST_DrawLevelField(xx, yy);
8021           }
8022         }
8023       }
8024
8025       if (MovDelay[x][y])       // element still has to wait some time
8026       {
8027         PlayLevelSoundAction(x, y, ACTION_WAITING);
8028
8029         return;
8030       }
8031     }
8032
8033     // now make next step
8034
8035     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8036
8037     if (DONT_COLLIDE_WITH(element) &&
8038         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8039         !PLAYER_ENEMY_PROTECTED(newx, newy))
8040     {
8041       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8042
8043       return;
8044     }
8045
8046     else if (CAN_MOVE_INTO_ACID(element) &&
8047              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8048              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8049              (MovDir[x][y] == MV_DOWN ||
8050               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8051     {
8052       SplashAcid(newx, newy);
8053       Store[x][y] = EL_ACID;
8054     }
8055     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8056     {
8057       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8058           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8059           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8060           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8061       {
8062         RemoveField(x, y);
8063         TEST_DrawLevelField(x, y);
8064
8065         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8066         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8067           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8068
8069         game.friends_still_needed--;
8070         if (!game.friends_still_needed &&
8071             !game.GameOver &&
8072             game.all_players_gone)
8073           LevelSolved();
8074
8075         return;
8076       }
8077       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8078       {
8079         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8080           TEST_DrawLevelField(newx, newy);
8081         else
8082           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8083       }
8084       else if (!IS_FREE(newx, newy))
8085       {
8086         GfxAction[x][y] = ACTION_WAITING;
8087
8088         if (IS_PLAYER(x, y))
8089           DrawPlayerField(x, y);
8090         else
8091           TEST_DrawLevelField(x, y);
8092
8093         return;
8094       }
8095     }
8096     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8097     {
8098       if (IS_FOOD_PIG(Feld[newx][newy]))
8099       {
8100         if (IS_MOVING(newx, newy))
8101           RemoveMovingField(newx, newy);
8102         else
8103         {
8104           Feld[newx][newy] = EL_EMPTY;
8105           TEST_DrawLevelField(newx, newy);
8106         }
8107
8108         PlayLevelSound(x, y, SND_PIG_DIGGING);
8109       }
8110       else if (!IS_FREE(newx, newy))
8111       {
8112         if (IS_PLAYER(x, y))
8113           DrawPlayerField(x, y);
8114         else
8115           TEST_DrawLevelField(x, y);
8116
8117         return;
8118       }
8119     }
8120     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8121     {
8122       if (Store[x][y] != EL_EMPTY)
8123       {
8124         boolean can_clone = FALSE;
8125         int xx, yy;
8126
8127         // check if element to clone is still there
8128         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8129         {
8130           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8131           {
8132             can_clone = TRUE;
8133
8134             break;
8135           }
8136         }
8137
8138         // cannot clone or target field not free anymore -- do not clone
8139         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8140           Store[x][y] = EL_EMPTY;
8141       }
8142
8143       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8144       {
8145         if (IS_MV_DIAGONAL(MovDir[x][y]))
8146         {
8147           int diagonal_move_dir = MovDir[x][y];
8148           int stored = Store[x][y];
8149           int change_delay = 8;
8150           int graphic;
8151
8152           // android is moving diagonally
8153
8154           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8155
8156           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8157           GfxElement[x][y] = EL_EMC_ANDROID;
8158           GfxAction[x][y] = ACTION_SHRINKING;
8159           GfxDir[x][y] = diagonal_move_dir;
8160           ChangeDelay[x][y] = change_delay;
8161
8162           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8163                                    GfxDir[x][y]);
8164
8165           DrawLevelGraphicAnimation(x, y, graphic);
8166           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8167
8168           if (Feld[newx][newy] == EL_ACID)
8169           {
8170             SplashAcid(newx, newy);
8171
8172             return;
8173           }
8174
8175           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8176
8177           Store[newx][newy] = EL_EMC_ANDROID;
8178           GfxElement[newx][newy] = EL_EMC_ANDROID;
8179           GfxAction[newx][newy] = ACTION_GROWING;
8180           GfxDir[newx][newy] = diagonal_move_dir;
8181           ChangeDelay[newx][newy] = change_delay;
8182
8183           graphic = el_act_dir2img(GfxElement[newx][newy],
8184                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8185
8186           DrawLevelGraphicAnimation(newx, newy, graphic);
8187           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8188
8189           return;
8190         }
8191         else
8192         {
8193           Feld[newx][newy] = EL_EMPTY;
8194           TEST_DrawLevelField(newx, newy);
8195
8196           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8197         }
8198       }
8199       else if (!IS_FREE(newx, newy))
8200       {
8201         return;
8202       }
8203     }
8204     else if (IS_CUSTOM_ELEMENT(element) &&
8205              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8206     {
8207       if (!DigFieldByCE(newx, newy, element))
8208         return;
8209
8210       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8211       {
8212         RunnerVisit[x][y] = FrameCounter;
8213         PlayerVisit[x][y] /= 8;         // expire player visit path
8214       }
8215     }
8216     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8217     {
8218       if (!IS_FREE(newx, newy))
8219       {
8220         if (IS_PLAYER(x, y))
8221           DrawPlayerField(x, y);
8222         else
8223           TEST_DrawLevelField(x, y);
8224
8225         return;
8226       }
8227       else
8228       {
8229         boolean wanna_flame = !RND(10);
8230         int dx = newx - x, dy = newy - y;
8231         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8232         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8233         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8234                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8235         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8236                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8237
8238         if ((wanna_flame ||
8239              IS_CLASSIC_ENEMY(element1) ||
8240              IS_CLASSIC_ENEMY(element2)) &&
8241             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8242             element1 != EL_FLAMES && element2 != EL_FLAMES)
8243         {
8244           ResetGfxAnimation(x, y);
8245           GfxAction[x][y] = ACTION_ATTACKING;
8246
8247           if (IS_PLAYER(x, y))
8248             DrawPlayerField(x, y);
8249           else
8250             TEST_DrawLevelField(x, y);
8251
8252           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8253
8254           MovDelay[x][y] = 50;
8255
8256           Feld[newx][newy] = EL_FLAMES;
8257           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8258             Feld[newx1][newy1] = EL_FLAMES;
8259           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8260             Feld[newx2][newy2] = EL_FLAMES;
8261
8262           return;
8263         }
8264       }
8265     }
8266     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8267              Feld[newx][newy] == EL_DIAMOND)
8268     {
8269       if (IS_MOVING(newx, newy))
8270         RemoveMovingField(newx, newy);
8271       else
8272       {
8273         Feld[newx][newy] = EL_EMPTY;
8274         TEST_DrawLevelField(newx, newy);
8275       }
8276
8277       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8278     }
8279     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8280              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8281     {
8282       if (AmoebaNr[newx][newy])
8283       {
8284         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8285         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8286             Feld[newx][newy] == EL_BD_AMOEBA)
8287           AmoebaCnt[AmoebaNr[newx][newy]]--;
8288       }
8289
8290       if (IS_MOVING(newx, newy))
8291       {
8292         RemoveMovingField(newx, newy);
8293       }
8294       else
8295       {
8296         Feld[newx][newy] = EL_EMPTY;
8297         TEST_DrawLevelField(newx, newy);
8298       }
8299
8300       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8301     }
8302     else if ((element == EL_PACMAN || element == EL_MOLE)
8303              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8304     {
8305       if (AmoebaNr[newx][newy])
8306       {
8307         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8308         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8309             Feld[newx][newy] == EL_BD_AMOEBA)
8310           AmoebaCnt[AmoebaNr[newx][newy]]--;
8311       }
8312
8313       if (element == EL_MOLE)
8314       {
8315         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8316         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8317
8318         ResetGfxAnimation(x, y);
8319         GfxAction[x][y] = ACTION_DIGGING;
8320         TEST_DrawLevelField(x, y);
8321
8322         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8323
8324         return;                         // wait for shrinking amoeba
8325       }
8326       else      // element == EL_PACMAN
8327       {
8328         Feld[newx][newy] = EL_EMPTY;
8329         TEST_DrawLevelField(newx, newy);
8330         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8331       }
8332     }
8333     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8334              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8335               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8336     {
8337       // wait for shrinking amoeba to completely disappear
8338       return;
8339     }
8340     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8341     {
8342       // object was running against a wall
8343
8344       TurnRound(x, y);
8345
8346       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8347         DrawLevelElementAnimation(x, y, element);
8348
8349       if (DONT_TOUCH(element))
8350         TestIfBadThingTouchesPlayer(x, y);
8351
8352       return;
8353     }
8354
8355     InitMovingField(x, y, MovDir[x][y]);
8356
8357     PlayLevelSoundAction(x, y, ACTION_MOVING);
8358   }
8359
8360   if (MovDir[x][y])
8361     ContinueMoving(x, y);
8362 }
8363
8364 void ContinueMoving(int x, int y)
8365 {
8366   int element = Feld[x][y];
8367   struct ElementInfo *ei = &element_info[element];
8368   int direction = MovDir[x][y];
8369   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8370   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8371   int newx = x + dx, newy = y + dy;
8372   int stored = Store[x][y];
8373   int stored_new = Store[newx][newy];
8374   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8375   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8376   boolean last_line = (newy == lev_fieldy - 1);
8377
8378   MovPos[x][y] += getElementMoveStepsize(x, y);
8379
8380   if (pushed_by_player) // special case: moving object pushed by player
8381     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8382
8383   if (ABS(MovPos[x][y]) < TILEX)
8384   {
8385     TEST_DrawLevelField(x, y);
8386
8387     return;     // element is still moving
8388   }
8389
8390   // element reached destination field
8391
8392   Feld[x][y] = EL_EMPTY;
8393   Feld[newx][newy] = element;
8394   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8395
8396   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8397   {
8398     element = Feld[newx][newy] = EL_ACID;
8399   }
8400   else if (element == EL_MOLE)
8401   {
8402     Feld[x][y] = EL_SAND;
8403
8404     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8405   }
8406   else if (element == EL_QUICKSAND_FILLING)
8407   {
8408     element = Feld[newx][newy] = get_next_element(element);
8409     Store[newx][newy] = Store[x][y];
8410   }
8411   else if (element == EL_QUICKSAND_EMPTYING)
8412   {
8413     Feld[x][y] = get_next_element(element);
8414     element = Feld[newx][newy] = Store[x][y];
8415   }
8416   else if (element == EL_QUICKSAND_FAST_FILLING)
8417   {
8418     element = Feld[newx][newy] = get_next_element(element);
8419     Store[newx][newy] = Store[x][y];
8420   }
8421   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8422   {
8423     Feld[x][y] = get_next_element(element);
8424     element = Feld[newx][newy] = Store[x][y];
8425   }
8426   else if (element == EL_MAGIC_WALL_FILLING)
8427   {
8428     element = Feld[newx][newy] = get_next_element(element);
8429     if (!game.magic_wall_active)
8430       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8431     Store[newx][newy] = Store[x][y];
8432   }
8433   else if (element == EL_MAGIC_WALL_EMPTYING)
8434   {
8435     Feld[x][y] = get_next_element(element);
8436     if (!game.magic_wall_active)
8437       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8438     element = Feld[newx][newy] = Store[x][y];
8439
8440     InitField(newx, newy, FALSE);
8441   }
8442   else if (element == EL_BD_MAGIC_WALL_FILLING)
8443   {
8444     element = Feld[newx][newy] = get_next_element(element);
8445     if (!game.magic_wall_active)
8446       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8447     Store[newx][newy] = Store[x][y];
8448   }
8449   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8450   {
8451     Feld[x][y] = get_next_element(element);
8452     if (!game.magic_wall_active)
8453       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8454     element = Feld[newx][newy] = Store[x][y];
8455
8456     InitField(newx, newy, FALSE);
8457   }
8458   else if (element == EL_DC_MAGIC_WALL_FILLING)
8459   {
8460     element = Feld[newx][newy] = get_next_element(element);
8461     if (!game.magic_wall_active)
8462       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8463     Store[newx][newy] = Store[x][y];
8464   }
8465   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8466   {
8467     Feld[x][y] = get_next_element(element);
8468     if (!game.magic_wall_active)
8469       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8470     element = Feld[newx][newy] = Store[x][y];
8471
8472     InitField(newx, newy, FALSE);
8473   }
8474   else if (element == EL_AMOEBA_DROPPING)
8475   {
8476     Feld[x][y] = get_next_element(element);
8477     element = Feld[newx][newy] = Store[x][y];
8478   }
8479   else if (element == EL_SOKOBAN_OBJECT)
8480   {
8481     if (Back[x][y])
8482       Feld[x][y] = Back[x][y];
8483
8484     if (Back[newx][newy])
8485       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8486
8487     Back[x][y] = Back[newx][newy] = 0;
8488   }
8489
8490   Store[x][y] = EL_EMPTY;
8491   MovPos[x][y] = 0;
8492   MovDir[x][y] = 0;
8493   MovDelay[x][y] = 0;
8494
8495   MovDelay[newx][newy] = 0;
8496
8497   if (CAN_CHANGE_OR_HAS_ACTION(element))
8498   {
8499     // copy element change control values to new field
8500     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8501     ChangePage[newx][newy]  = ChangePage[x][y];
8502     ChangeCount[newx][newy] = ChangeCount[x][y];
8503     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8504   }
8505
8506   CustomValue[newx][newy] = CustomValue[x][y];
8507
8508   ChangeDelay[x][y] = 0;
8509   ChangePage[x][y] = -1;
8510   ChangeCount[x][y] = 0;
8511   ChangeEvent[x][y] = -1;
8512
8513   CustomValue[x][y] = 0;
8514
8515   // copy animation control values to new field
8516   GfxFrame[newx][newy]  = GfxFrame[x][y];
8517   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8518   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8519   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8520
8521   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8522
8523   // some elements can leave other elements behind after moving
8524   if (ei->move_leave_element != EL_EMPTY &&
8525       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8526       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8527   {
8528     int move_leave_element = ei->move_leave_element;
8529
8530     // this makes it possible to leave the removed element again
8531     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8532       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8533
8534     Feld[x][y] = move_leave_element;
8535
8536     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8537       MovDir[x][y] = direction;
8538
8539     InitField(x, y, FALSE);
8540
8541     if (GFX_CRUMBLED(Feld[x][y]))
8542       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8543
8544     if (ELEM_IS_PLAYER(move_leave_element))
8545       RelocatePlayer(x, y, move_leave_element);
8546   }
8547
8548   // do this after checking for left-behind element
8549   ResetGfxAnimation(x, y);      // reset animation values for old field
8550
8551   if (!CAN_MOVE(element) ||
8552       (CAN_FALL(element) && direction == MV_DOWN &&
8553        (element == EL_SPRING ||
8554         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8555         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8556     GfxDir[x][y] = MovDir[newx][newy] = 0;
8557
8558   TEST_DrawLevelField(x, y);
8559   TEST_DrawLevelField(newx, newy);
8560
8561   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8562
8563   // prevent pushed element from moving on in pushed direction
8564   if (pushed_by_player && CAN_MOVE(element) &&
8565       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8566       !(element_info[element].move_pattern & direction))
8567     TurnRound(newx, newy);
8568
8569   // prevent elements on conveyor belt from moving on in last direction
8570   if (pushed_by_conveyor && CAN_FALL(element) &&
8571       direction & MV_HORIZONTAL)
8572     MovDir[newx][newy] = 0;
8573
8574   if (!pushed_by_player)
8575   {
8576     int nextx = newx + dx, nexty = newy + dy;
8577     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8578
8579     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8580
8581     if (CAN_FALL(element) && direction == MV_DOWN)
8582       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8583
8584     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8585       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8586
8587     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8588       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8589   }
8590
8591   if (DONT_TOUCH(element))      // object may be nasty to player or others
8592   {
8593     TestIfBadThingTouchesPlayer(newx, newy);
8594     TestIfBadThingTouchesFriend(newx, newy);
8595
8596     if (!IS_CUSTOM_ELEMENT(element))
8597       TestIfBadThingTouchesOtherBadThing(newx, newy);
8598   }
8599   else if (element == EL_PENGUIN)
8600     TestIfFriendTouchesBadThing(newx, newy);
8601
8602   if (DONT_GET_HIT_BY(element))
8603   {
8604     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8605   }
8606
8607   // give the player one last chance (one more frame) to move away
8608   if (CAN_FALL(element) && direction == MV_DOWN &&
8609       (last_line || (!IS_FREE(x, newy + 1) &&
8610                      (!IS_PLAYER(x, newy + 1) ||
8611                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8612     Impact(x, newy);
8613
8614   if (pushed_by_player && !game.use_change_when_pushing_bug)
8615   {
8616     int push_side = MV_DIR_OPPOSITE(direction);
8617     struct PlayerInfo *player = PLAYERINFO(x, y);
8618
8619     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8620                                player->index_bit, push_side);
8621     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8622                                         player->index_bit, push_side);
8623   }
8624
8625   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8626     MovDelay[newx][newy] = 1;
8627
8628   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8629
8630   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8631   TestIfElementHitsCustomElement(newx, newy, direction);
8632   TestIfPlayerTouchesCustomElement(newx, newy);
8633   TestIfElementTouchesCustomElement(newx, newy);
8634
8635   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8636       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8637     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8638                              MV_DIR_OPPOSITE(direction));
8639 }
8640
8641 int AmoebeNachbarNr(int ax, int ay)
8642 {
8643   int i;
8644   int element = Feld[ax][ay];
8645   int group_nr = 0;
8646   static int xy[4][2] =
8647   {
8648     { 0, -1 },
8649     { -1, 0 },
8650     { +1, 0 },
8651     { 0, +1 }
8652   };
8653
8654   for (i = 0; i < NUM_DIRECTIONS; i++)
8655   {
8656     int x = ax + xy[i][0];
8657     int y = ay + xy[i][1];
8658
8659     if (!IN_LEV_FIELD(x, y))
8660       continue;
8661
8662     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8663       group_nr = AmoebaNr[x][y];
8664   }
8665
8666   return group_nr;
8667 }
8668
8669 static void AmoebenVereinigen(int ax, int ay)
8670 {
8671   int i, x, y, xx, yy;
8672   int new_group_nr = AmoebaNr[ax][ay];
8673   static int xy[4][2] =
8674   {
8675     { 0, -1 },
8676     { -1, 0 },
8677     { +1, 0 },
8678     { 0, +1 }
8679   };
8680
8681   if (new_group_nr == 0)
8682     return;
8683
8684   for (i = 0; i < NUM_DIRECTIONS; i++)
8685   {
8686     x = ax + xy[i][0];
8687     y = ay + xy[i][1];
8688
8689     if (!IN_LEV_FIELD(x, y))
8690       continue;
8691
8692     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8693          Feld[x][y] == EL_BD_AMOEBA ||
8694          Feld[x][y] == EL_AMOEBA_DEAD) &&
8695         AmoebaNr[x][y] != new_group_nr)
8696     {
8697       int old_group_nr = AmoebaNr[x][y];
8698
8699       if (old_group_nr == 0)
8700         return;
8701
8702       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8703       AmoebaCnt[old_group_nr] = 0;
8704       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8705       AmoebaCnt2[old_group_nr] = 0;
8706
8707       SCAN_PLAYFIELD(xx, yy)
8708       {
8709         if (AmoebaNr[xx][yy] == old_group_nr)
8710           AmoebaNr[xx][yy] = new_group_nr;
8711       }
8712     }
8713   }
8714 }
8715
8716 void AmoebeUmwandeln(int ax, int ay)
8717 {
8718   int i, x, y;
8719
8720   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8721   {
8722     int group_nr = AmoebaNr[ax][ay];
8723
8724 #ifdef DEBUG
8725     if (group_nr == 0)
8726     {
8727       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8728       printf("AmoebeUmwandeln(): This should never happen!\n");
8729       return;
8730     }
8731 #endif
8732
8733     SCAN_PLAYFIELD(x, y)
8734     {
8735       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8736       {
8737         AmoebaNr[x][y] = 0;
8738         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8739       }
8740     }
8741
8742     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8743                             SND_AMOEBA_TURNING_TO_GEM :
8744                             SND_AMOEBA_TURNING_TO_ROCK));
8745     Bang(ax, ay);
8746   }
8747   else
8748   {
8749     static int xy[4][2] =
8750     {
8751       { 0, -1 },
8752       { -1, 0 },
8753       { +1, 0 },
8754       { 0, +1 }
8755     };
8756
8757     for (i = 0; i < NUM_DIRECTIONS; i++)
8758     {
8759       x = ax + xy[i][0];
8760       y = ay + xy[i][1];
8761
8762       if (!IN_LEV_FIELD(x, y))
8763         continue;
8764
8765       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8766       {
8767         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8768                               SND_AMOEBA_TURNING_TO_GEM :
8769                               SND_AMOEBA_TURNING_TO_ROCK));
8770         Bang(x, y);
8771       }
8772     }
8773   }
8774 }
8775
8776 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8777 {
8778   int x, y;
8779   int group_nr = AmoebaNr[ax][ay];
8780   boolean done = FALSE;
8781
8782 #ifdef DEBUG
8783   if (group_nr == 0)
8784   {
8785     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8786     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8787     return;
8788   }
8789 #endif
8790
8791   SCAN_PLAYFIELD(x, y)
8792   {
8793     if (AmoebaNr[x][y] == group_nr &&
8794         (Feld[x][y] == EL_AMOEBA_DEAD ||
8795          Feld[x][y] == EL_BD_AMOEBA ||
8796          Feld[x][y] == EL_AMOEBA_GROWING))
8797     {
8798       AmoebaNr[x][y] = 0;
8799       Feld[x][y] = new_element;
8800       InitField(x, y, FALSE);
8801       TEST_DrawLevelField(x, y);
8802       done = TRUE;
8803     }
8804   }
8805
8806   if (done)
8807     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8808                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8809                             SND_BD_AMOEBA_TURNING_TO_GEM));
8810 }
8811
8812 static void AmoebeWaechst(int x, int y)
8813 {
8814   static unsigned int sound_delay = 0;
8815   static unsigned int sound_delay_value = 0;
8816
8817   if (!MovDelay[x][y])          // start new growing cycle
8818   {
8819     MovDelay[x][y] = 7;
8820
8821     if (DelayReached(&sound_delay, sound_delay_value))
8822     {
8823       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8824       sound_delay_value = 30;
8825     }
8826   }
8827
8828   if (MovDelay[x][y])           // wait some time before growing bigger
8829   {
8830     MovDelay[x][y]--;
8831     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8832     {
8833       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8834                                            6 - MovDelay[x][y]);
8835
8836       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8837     }
8838
8839     if (!MovDelay[x][y])
8840     {
8841       Feld[x][y] = Store[x][y];
8842       Store[x][y] = 0;
8843       TEST_DrawLevelField(x, y);
8844     }
8845   }
8846 }
8847
8848 static void AmoebaDisappearing(int x, int y)
8849 {
8850   static unsigned int sound_delay = 0;
8851   static unsigned int sound_delay_value = 0;
8852
8853   if (!MovDelay[x][y])          // start new shrinking cycle
8854   {
8855     MovDelay[x][y] = 7;
8856
8857     if (DelayReached(&sound_delay, sound_delay_value))
8858       sound_delay_value = 30;
8859   }
8860
8861   if (MovDelay[x][y])           // wait some time before shrinking
8862   {
8863     MovDelay[x][y]--;
8864     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8865     {
8866       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8867                                            6 - MovDelay[x][y]);
8868
8869       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8870     }
8871
8872     if (!MovDelay[x][y])
8873     {
8874       Feld[x][y] = EL_EMPTY;
8875       TEST_DrawLevelField(x, y);
8876
8877       // don't let mole enter this field in this cycle;
8878       // (give priority to objects falling to this field from above)
8879       Stop[x][y] = TRUE;
8880     }
8881   }
8882 }
8883
8884 static void AmoebeAbleger(int ax, int ay)
8885 {
8886   int i;
8887   int element = Feld[ax][ay];
8888   int graphic = el2img(element);
8889   int newax = ax, neway = ay;
8890   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8891   static int xy[4][2] =
8892   {
8893     { 0, -1 },
8894     { -1, 0 },
8895     { +1, 0 },
8896     { 0, +1 }
8897   };
8898
8899   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8900   {
8901     Feld[ax][ay] = EL_AMOEBA_DEAD;
8902     TEST_DrawLevelField(ax, ay);
8903     return;
8904   }
8905
8906   if (IS_ANIMATED(graphic))
8907     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8908
8909   if (!MovDelay[ax][ay])        // start making new amoeba field
8910     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8911
8912   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8913   {
8914     MovDelay[ax][ay]--;
8915     if (MovDelay[ax][ay])
8916       return;
8917   }
8918
8919   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8920   {
8921     int start = RND(4);
8922     int x = ax + xy[start][0];
8923     int y = ay + xy[start][1];
8924
8925     if (!IN_LEV_FIELD(x, y))
8926       return;
8927
8928     if (IS_FREE(x, y) ||
8929         CAN_GROW_INTO(Feld[x][y]) ||
8930         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8931         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8932     {
8933       newax = x;
8934       neway = y;
8935     }
8936
8937     if (newax == ax && neway == ay)
8938       return;
8939   }
8940   else                          // normal or "filled" (BD style) amoeba
8941   {
8942     int start = RND(4);
8943     boolean waiting_for_player = FALSE;
8944
8945     for (i = 0; i < NUM_DIRECTIONS; i++)
8946     {
8947       int j = (start + i) % 4;
8948       int x = ax + xy[j][0];
8949       int y = ay + xy[j][1];
8950
8951       if (!IN_LEV_FIELD(x, y))
8952         continue;
8953
8954       if (IS_FREE(x, y) ||
8955           CAN_GROW_INTO(Feld[x][y]) ||
8956           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8957           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8958       {
8959         newax = x;
8960         neway = y;
8961         break;
8962       }
8963       else if (IS_PLAYER(x, y))
8964         waiting_for_player = TRUE;
8965     }
8966
8967     if (newax == ax && neway == ay)             // amoeba cannot grow
8968     {
8969       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8970       {
8971         Feld[ax][ay] = EL_AMOEBA_DEAD;
8972         TEST_DrawLevelField(ax, ay);
8973         AmoebaCnt[AmoebaNr[ax][ay]]--;
8974
8975         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8976         {
8977           if (element == EL_AMOEBA_FULL)
8978             AmoebeUmwandeln(ax, ay);
8979           else if (element == EL_BD_AMOEBA)
8980             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8981         }
8982       }
8983       return;
8984     }
8985     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8986     {
8987       // amoeba gets larger by growing in some direction
8988
8989       int new_group_nr = AmoebaNr[ax][ay];
8990
8991 #ifdef DEBUG
8992   if (new_group_nr == 0)
8993   {
8994     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8995     printf("AmoebeAbleger(): This should never happen!\n");
8996     return;
8997   }
8998 #endif
8999
9000       AmoebaNr[newax][neway] = new_group_nr;
9001       AmoebaCnt[new_group_nr]++;
9002       AmoebaCnt2[new_group_nr]++;
9003
9004       // if amoeba touches other amoeba(s) after growing, unify them
9005       AmoebenVereinigen(newax, neway);
9006
9007       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9008       {
9009         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9010         return;
9011       }
9012     }
9013   }
9014
9015   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9016       (neway == lev_fieldy - 1 && newax != ax))
9017   {
9018     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9019     Store[newax][neway] = element;
9020   }
9021   else if (neway == ay || element == EL_EMC_DRIPPER)
9022   {
9023     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9024
9025     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9026   }
9027   else
9028   {
9029     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9030     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9031     Store[ax][ay] = EL_AMOEBA_DROP;
9032     ContinueMoving(ax, ay);
9033     return;
9034   }
9035
9036   TEST_DrawLevelField(newax, neway);
9037 }
9038
9039 static void Life(int ax, int ay)
9040 {
9041   int x1, y1, x2, y2;
9042   int life_time = 40;
9043   int element = Feld[ax][ay];
9044   int graphic = el2img(element);
9045   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9046                          level.biomaze);
9047   boolean changed = FALSE;
9048
9049   if (IS_ANIMATED(graphic))
9050     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9051
9052   if (Stop[ax][ay])
9053     return;
9054
9055   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9056     MovDelay[ax][ay] = life_time;
9057
9058   if (MovDelay[ax][ay])         // wait some time before next cycle
9059   {
9060     MovDelay[ax][ay]--;
9061     if (MovDelay[ax][ay])
9062       return;
9063   }
9064
9065   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9066   {
9067     int xx = ax+x1, yy = ay+y1;
9068     int old_element = Feld[xx][yy];
9069     int num_neighbours = 0;
9070
9071     if (!IN_LEV_FIELD(xx, yy))
9072       continue;
9073
9074     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9075     {
9076       int x = xx+x2, y = yy+y2;
9077
9078       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9079         continue;
9080
9081       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9082       boolean is_neighbour = FALSE;
9083
9084       if (level.use_life_bugs)
9085         is_neighbour =
9086           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9087            (IS_FREE(x, y)                             &&  Stop[x][y]));
9088       else
9089         is_neighbour =
9090           (Last[x][y] == element || is_player_cell);
9091
9092       if (is_neighbour)
9093         num_neighbours++;
9094     }
9095
9096     boolean is_free = FALSE;
9097
9098     if (level.use_life_bugs)
9099       is_free = (IS_FREE(xx, yy));
9100     else
9101       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9102
9103     if (xx == ax && yy == ay)           // field in the middle
9104     {
9105       if (num_neighbours < life_parameter[0] ||
9106           num_neighbours > life_parameter[1])
9107       {
9108         Feld[xx][yy] = EL_EMPTY;
9109         if (Feld[xx][yy] != old_element)
9110           TEST_DrawLevelField(xx, yy);
9111         Stop[xx][yy] = TRUE;
9112         changed = TRUE;
9113       }
9114     }
9115     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9116     {                                   // free border field
9117       if (num_neighbours >= life_parameter[2] &&
9118           num_neighbours <= life_parameter[3])
9119       {
9120         Feld[xx][yy] = element;
9121         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9122         if (Feld[xx][yy] != old_element)
9123           TEST_DrawLevelField(xx, yy);
9124         Stop[xx][yy] = TRUE;
9125         changed = TRUE;
9126       }
9127     }
9128   }
9129
9130   if (changed)
9131     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9132                    SND_GAME_OF_LIFE_GROWING);
9133 }
9134
9135 static void InitRobotWheel(int x, int y)
9136 {
9137   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9138 }
9139
9140 static void RunRobotWheel(int x, int y)
9141 {
9142   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9143 }
9144
9145 static void StopRobotWheel(int x, int y)
9146 {
9147   if (game.robot_wheel_x == x &&
9148       game.robot_wheel_y == y)
9149   {
9150     game.robot_wheel_x = -1;
9151     game.robot_wheel_y = -1;
9152     game.robot_wheel_active = FALSE;
9153   }
9154 }
9155
9156 static void InitTimegateWheel(int x, int y)
9157 {
9158   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9159 }
9160
9161 static void RunTimegateWheel(int x, int y)
9162 {
9163   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9164 }
9165
9166 static void InitMagicBallDelay(int x, int y)
9167 {
9168   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9169 }
9170
9171 static void ActivateMagicBall(int bx, int by)
9172 {
9173   int x, y;
9174
9175   if (level.ball_random)
9176   {
9177     int pos_border = RND(8);    // select one of the eight border elements
9178     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9179     int xx = pos_content % 3;
9180     int yy = pos_content / 3;
9181
9182     x = bx - 1 + xx;
9183     y = by - 1 + yy;
9184
9185     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9186       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9187   }
9188   else
9189   {
9190     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9191     {
9192       int xx = x - bx + 1;
9193       int yy = y - by + 1;
9194
9195       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9196         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9197     }
9198   }
9199
9200   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9201 }
9202
9203 static void CheckExit(int x, int y)
9204 {
9205   if (game.gems_still_needed > 0 ||
9206       game.sokoban_fields_still_needed > 0 ||
9207       game.sokoban_objects_still_needed > 0 ||
9208       game.lights_still_needed > 0)
9209   {
9210     int element = Feld[x][y];
9211     int graphic = el2img(element);
9212
9213     if (IS_ANIMATED(graphic))
9214       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9215
9216     return;
9217   }
9218
9219   // do not re-open exit door closed after last player
9220   if (game.all_players_gone)
9221     return;
9222
9223   Feld[x][y] = EL_EXIT_OPENING;
9224
9225   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9226 }
9227
9228 static void CheckExitEM(int x, int y)
9229 {
9230   if (game.gems_still_needed > 0 ||
9231       game.sokoban_fields_still_needed > 0 ||
9232       game.sokoban_objects_still_needed > 0 ||
9233       game.lights_still_needed > 0)
9234   {
9235     int element = Feld[x][y];
9236     int graphic = el2img(element);
9237
9238     if (IS_ANIMATED(graphic))
9239       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9240
9241     return;
9242   }
9243
9244   // do not re-open exit door closed after last player
9245   if (game.all_players_gone)
9246     return;
9247
9248   Feld[x][y] = EL_EM_EXIT_OPENING;
9249
9250   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9251 }
9252
9253 static void CheckExitSteel(int x, int y)
9254 {
9255   if (game.gems_still_needed > 0 ||
9256       game.sokoban_fields_still_needed > 0 ||
9257       game.sokoban_objects_still_needed > 0 ||
9258       game.lights_still_needed > 0)
9259   {
9260     int element = Feld[x][y];
9261     int graphic = el2img(element);
9262
9263     if (IS_ANIMATED(graphic))
9264       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9265
9266     return;
9267   }
9268
9269   // do not re-open exit door closed after last player
9270   if (game.all_players_gone)
9271     return;
9272
9273   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9274
9275   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9276 }
9277
9278 static void CheckExitSteelEM(int x, int y)
9279 {
9280   if (game.gems_still_needed > 0 ||
9281       game.sokoban_fields_still_needed > 0 ||
9282       game.sokoban_objects_still_needed > 0 ||
9283       game.lights_still_needed > 0)
9284   {
9285     int element = Feld[x][y];
9286     int graphic = el2img(element);
9287
9288     if (IS_ANIMATED(graphic))
9289       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9290
9291     return;
9292   }
9293
9294   // do not re-open exit door closed after last player
9295   if (game.all_players_gone)
9296     return;
9297
9298   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9299
9300   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9301 }
9302
9303 static void CheckExitSP(int x, int y)
9304 {
9305   if (game.gems_still_needed > 0)
9306   {
9307     int element = Feld[x][y];
9308     int graphic = el2img(element);
9309
9310     if (IS_ANIMATED(graphic))
9311       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9312
9313     return;
9314   }
9315
9316   // do not re-open exit door closed after last player
9317   if (game.all_players_gone)
9318     return;
9319
9320   Feld[x][y] = EL_SP_EXIT_OPENING;
9321
9322   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9323 }
9324
9325 static void CloseAllOpenTimegates(void)
9326 {
9327   int x, y;
9328
9329   SCAN_PLAYFIELD(x, y)
9330   {
9331     int element = Feld[x][y];
9332
9333     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9334     {
9335       Feld[x][y] = EL_TIMEGATE_CLOSING;
9336
9337       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9338     }
9339   }
9340 }
9341
9342 static void DrawTwinkleOnField(int x, int y)
9343 {
9344   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9345     return;
9346
9347   if (Feld[x][y] == EL_BD_DIAMOND)
9348     return;
9349
9350   if (MovDelay[x][y] == 0)      // next animation frame
9351     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9352
9353   if (MovDelay[x][y] != 0)      // wait some time before next frame
9354   {
9355     MovDelay[x][y]--;
9356
9357     DrawLevelElementAnimation(x, y, Feld[x][y]);
9358
9359     if (MovDelay[x][y] != 0)
9360     {
9361       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9362                                            10 - MovDelay[x][y]);
9363
9364       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9365     }
9366   }
9367 }
9368
9369 static void MauerWaechst(int x, int y)
9370 {
9371   int delay = 6;
9372
9373   if (!MovDelay[x][y])          // next animation frame
9374     MovDelay[x][y] = 3 * delay;
9375
9376   if (MovDelay[x][y])           // wait some time before next frame
9377   {
9378     MovDelay[x][y]--;
9379
9380     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9381     {
9382       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9383       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9384
9385       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9386     }
9387
9388     if (!MovDelay[x][y])
9389     {
9390       if (MovDir[x][y] == MV_LEFT)
9391       {
9392         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9393           TEST_DrawLevelField(x - 1, y);
9394       }
9395       else if (MovDir[x][y] == MV_RIGHT)
9396       {
9397         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9398           TEST_DrawLevelField(x + 1, y);
9399       }
9400       else if (MovDir[x][y] == MV_UP)
9401       {
9402         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9403           TEST_DrawLevelField(x, y - 1);
9404       }
9405       else
9406       {
9407         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9408           TEST_DrawLevelField(x, y + 1);
9409       }
9410
9411       Feld[x][y] = Store[x][y];
9412       Store[x][y] = 0;
9413       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9414       TEST_DrawLevelField(x, y);
9415     }
9416   }
9417 }
9418
9419 static void MauerAbleger(int ax, int ay)
9420 {
9421   int element = Feld[ax][ay];
9422   int graphic = el2img(element);
9423   boolean oben_frei = FALSE, unten_frei = FALSE;
9424   boolean links_frei = FALSE, rechts_frei = FALSE;
9425   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9426   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9427   boolean new_wall = FALSE;
9428
9429   if (IS_ANIMATED(graphic))
9430     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9431
9432   if (!MovDelay[ax][ay])        // start building new wall
9433     MovDelay[ax][ay] = 6;
9434
9435   if (MovDelay[ax][ay])         // wait some time before building new wall
9436   {
9437     MovDelay[ax][ay]--;
9438     if (MovDelay[ax][ay])
9439       return;
9440   }
9441
9442   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9443     oben_frei = TRUE;
9444   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9445     unten_frei = TRUE;
9446   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9447     links_frei = TRUE;
9448   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9449     rechts_frei = TRUE;
9450
9451   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9452       element == EL_EXPANDABLE_WALL_ANY)
9453   {
9454     if (oben_frei)
9455     {
9456       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9457       Store[ax][ay-1] = element;
9458       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9459       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9460         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9461                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9462       new_wall = TRUE;
9463     }
9464     if (unten_frei)
9465     {
9466       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9467       Store[ax][ay+1] = element;
9468       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9469       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9470         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9471                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9472       new_wall = TRUE;
9473     }
9474   }
9475
9476   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9477       element == EL_EXPANDABLE_WALL_ANY ||
9478       element == EL_EXPANDABLE_WALL ||
9479       element == EL_BD_EXPANDABLE_WALL)
9480   {
9481     if (links_frei)
9482     {
9483       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_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_WALL_GROWING_LEFT, 0);
9489       new_wall = TRUE;
9490     }
9491
9492     if (rechts_frei)
9493     {
9494       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_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_WALL_GROWING_RIGHT, 0);
9500       new_wall = TRUE;
9501     }
9502   }
9503
9504   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9505     TEST_DrawLevelField(ax, ay);
9506
9507   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9508     oben_massiv = TRUE;
9509   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9510     unten_massiv = TRUE;
9511   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9512     links_massiv = TRUE;
9513   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9514     rechts_massiv = TRUE;
9515
9516   if (((oben_massiv && unten_massiv) ||
9517        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9518        element == EL_EXPANDABLE_WALL) &&
9519       ((links_massiv && rechts_massiv) ||
9520        element == EL_EXPANDABLE_WALL_VERTICAL))
9521     Feld[ax][ay] = EL_WALL;
9522
9523   if (new_wall)
9524     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9525 }
9526
9527 static void MauerAblegerStahl(int ax, int ay)
9528 {
9529   int element = Feld[ax][ay];
9530   int graphic = el2img(element);
9531   boolean oben_frei = FALSE, unten_frei = FALSE;
9532   boolean links_frei = FALSE, rechts_frei = FALSE;
9533   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9534   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9535   boolean new_wall = FALSE;
9536
9537   if (IS_ANIMATED(graphic))
9538     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9539
9540   if (!MovDelay[ax][ay])        // start building new wall
9541     MovDelay[ax][ay] = 6;
9542
9543   if (MovDelay[ax][ay])         // wait some time before building new wall
9544   {
9545     MovDelay[ax][ay]--;
9546     if (MovDelay[ax][ay])
9547       return;
9548   }
9549
9550   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9551     oben_frei = TRUE;
9552   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9553     unten_frei = TRUE;
9554   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9555     links_frei = TRUE;
9556   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9557     rechts_frei = TRUE;
9558
9559   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9560       element == EL_EXPANDABLE_STEELWALL_ANY)
9561   {
9562     if (oben_frei)
9563     {
9564       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9565       Store[ax][ay-1] = element;
9566       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9567       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9568         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9569                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9570       new_wall = TRUE;
9571     }
9572     if (unten_frei)
9573     {
9574       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9575       Store[ax][ay+1] = element;
9576       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9577       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9578         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9579                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9580       new_wall = TRUE;
9581     }
9582   }
9583
9584   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9585       element == EL_EXPANDABLE_STEELWALL_ANY)
9586   {
9587     if (links_frei)
9588     {
9589       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9590       Store[ax-1][ay] = element;
9591       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9592       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9593         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9594                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9595       new_wall = TRUE;
9596     }
9597
9598     if (rechts_frei)
9599     {
9600       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9601       Store[ax+1][ay] = element;
9602       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9603       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9604         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9605                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9606       new_wall = TRUE;
9607     }
9608   }
9609
9610   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9611     oben_massiv = TRUE;
9612   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9613     unten_massiv = TRUE;
9614   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9615     links_massiv = TRUE;
9616   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9617     rechts_massiv = TRUE;
9618
9619   if (((oben_massiv && unten_massiv) ||
9620        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9621       ((links_massiv && rechts_massiv) ||
9622        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9623     Feld[ax][ay] = EL_STEELWALL;
9624
9625   if (new_wall)
9626     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9627 }
9628
9629 static void CheckForDragon(int x, int y)
9630 {
9631   int i, j;
9632   boolean dragon_found = FALSE;
9633   static int xy[4][2] =
9634   {
9635     { 0, -1 },
9636     { -1, 0 },
9637     { +1, 0 },
9638     { 0, +1 }
9639   };
9640
9641   for (i = 0; i < NUM_DIRECTIONS; i++)
9642   {
9643     for (j = 0; j < 4; j++)
9644     {
9645       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9646
9647       if (IN_LEV_FIELD(xx, yy) &&
9648           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9649       {
9650         if (Feld[xx][yy] == EL_DRAGON)
9651           dragon_found = TRUE;
9652       }
9653       else
9654         break;
9655     }
9656   }
9657
9658   if (!dragon_found)
9659   {
9660     for (i = 0; i < NUM_DIRECTIONS; i++)
9661     {
9662       for (j = 0; j < 3; j++)
9663       {
9664         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9665   
9666         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9667         {
9668           Feld[xx][yy] = EL_EMPTY;
9669           TEST_DrawLevelField(xx, yy);
9670         }
9671         else
9672           break;
9673       }
9674     }
9675   }
9676 }
9677
9678 static void InitBuggyBase(int x, int y)
9679 {
9680   int element = Feld[x][y];
9681   int activating_delay = FRAMES_PER_SECOND / 4;
9682
9683   ChangeDelay[x][y] =
9684     (element == EL_SP_BUGGY_BASE ?
9685      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9686      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9687      activating_delay :
9688      element == EL_SP_BUGGY_BASE_ACTIVE ?
9689      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9690 }
9691
9692 static void WarnBuggyBase(int x, int y)
9693 {
9694   int i;
9695   static int xy[4][2] =
9696   {
9697     { 0, -1 },
9698     { -1, 0 },
9699     { +1, 0 },
9700     { 0, +1 }
9701   };
9702
9703   for (i = 0; i < NUM_DIRECTIONS; i++)
9704   {
9705     int xx = x + xy[i][0];
9706     int yy = y + xy[i][1];
9707
9708     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9709     {
9710       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9711
9712       break;
9713     }
9714   }
9715 }
9716
9717 static void InitTrap(int x, int y)
9718 {
9719   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9720 }
9721
9722 static void ActivateTrap(int x, int y)
9723 {
9724   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9725 }
9726
9727 static void ChangeActiveTrap(int x, int y)
9728 {
9729   int graphic = IMG_TRAP_ACTIVE;
9730
9731   // if new animation frame was drawn, correct crumbled sand border
9732   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9733     TEST_DrawLevelFieldCrumbled(x, y);
9734 }
9735
9736 static int getSpecialActionElement(int element, int number, int base_element)
9737 {
9738   return (element != EL_EMPTY ? element :
9739           number != -1 ? base_element + number - 1 :
9740           EL_EMPTY);
9741 }
9742
9743 static int getModifiedActionNumber(int value_old, int operator, int operand,
9744                                    int value_min, int value_max)
9745 {
9746   int value_new = (operator == CA_MODE_SET      ? operand :
9747                    operator == CA_MODE_ADD      ? value_old + operand :
9748                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9749                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9750                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9751                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9752                    value_old);
9753
9754   return (value_new < value_min ? value_min :
9755           value_new > value_max ? value_max :
9756           value_new);
9757 }
9758
9759 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9760 {
9761   struct ElementInfo *ei = &element_info[element];
9762   struct ElementChangeInfo *change = &ei->change_page[page];
9763   int target_element = change->target_element;
9764   int action_type = change->action_type;
9765   int action_mode = change->action_mode;
9766   int action_arg = change->action_arg;
9767   int action_element = change->action_element;
9768   int i;
9769
9770   if (!change->has_action)
9771     return;
9772
9773   // ---------- determine action paramater values -----------------------------
9774
9775   int level_time_value =
9776     (level.time > 0 ? TimeLeft :
9777      TimePlayed);
9778
9779   int action_arg_element_raw =
9780     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9781      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9782      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9783      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9784      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9785      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9786      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9787      EL_EMPTY);
9788   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9789
9790   int action_arg_direction =
9791     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9792      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9793      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9794      change->actual_trigger_side :
9795      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9796      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9797      MV_NONE);
9798
9799   int action_arg_number_min =
9800     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9801      CA_ARG_MIN);
9802
9803   int action_arg_number_max =
9804     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9805      action_type == CA_SET_LEVEL_GEMS ? 999 :
9806      action_type == CA_SET_LEVEL_TIME ? 9999 :
9807      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9808      action_type == CA_SET_CE_VALUE ? 9999 :
9809      action_type == CA_SET_CE_SCORE ? 9999 :
9810      CA_ARG_MAX);
9811
9812   int action_arg_number_reset =
9813     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9814      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9815      action_type == CA_SET_LEVEL_TIME ? level.time :
9816      action_type == CA_SET_LEVEL_SCORE ? 0 :
9817      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9818      action_type == CA_SET_CE_SCORE ? 0 :
9819      0);
9820
9821   int action_arg_number =
9822     (action_arg <= CA_ARG_MAX ? action_arg :
9823      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9824      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9825      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9826      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9827      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9828      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9829      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9830      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9831      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9832      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9833      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9834      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9835      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9836      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9837      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9838      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9839      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9840      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9841      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9842      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9843      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9844      -1);
9845
9846   int action_arg_number_old =
9847     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9848      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9849      action_type == CA_SET_LEVEL_SCORE ? game.score :
9850      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9851      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9852      0);
9853
9854   int action_arg_number_new =
9855     getModifiedActionNumber(action_arg_number_old,
9856                             action_mode, action_arg_number,
9857                             action_arg_number_min, action_arg_number_max);
9858
9859   int trigger_player_bits =
9860     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9861      change->actual_trigger_player_bits : change->trigger_player);
9862
9863   int action_arg_player_bits =
9864     (action_arg >= CA_ARG_PLAYER_1 &&
9865      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9866      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9867      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9868      PLAYER_BITS_ANY);
9869
9870   // ---------- execute action  -----------------------------------------------
9871
9872   switch (action_type)
9873   {
9874     case CA_NO_ACTION:
9875     {
9876       return;
9877     }
9878
9879     // ---------- level actions  ----------------------------------------------
9880
9881     case CA_RESTART_LEVEL:
9882     {
9883       game.restart_level = TRUE;
9884
9885       break;
9886     }
9887
9888     case CA_SHOW_ENVELOPE:
9889     {
9890       int element = getSpecialActionElement(action_arg_element,
9891                                             action_arg_number, EL_ENVELOPE_1);
9892
9893       if (IS_ENVELOPE(element))
9894         local_player->show_envelope = element;
9895
9896       break;
9897     }
9898
9899     case CA_SET_LEVEL_TIME:
9900     {
9901       if (level.time > 0)       // only modify limited time value
9902       {
9903         TimeLeft = action_arg_number_new;
9904
9905         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9906
9907         DisplayGameControlValues();
9908
9909         if (!TimeLeft && setup.time_limit)
9910           for (i = 0; i < MAX_PLAYERS; i++)
9911             KillPlayer(&stored_player[i]);
9912       }
9913
9914       break;
9915     }
9916
9917     case CA_SET_LEVEL_SCORE:
9918     {
9919       game.score = action_arg_number_new;
9920
9921       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9922
9923       DisplayGameControlValues();
9924
9925       break;
9926     }
9927
9928     case CA_SET_LEVEL_GEMS:
9929     {
9930       game.gems_still_needed = action_arg_number_new;
9931
9932       game.snapshot.collected_item = TRUE;
9933
9934       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9935
9936       DisplayGameControlValues();
9937
9938       break;
9939     }
9940
9941     case CA_SET_LEVEL_WIND:
9942     {
9943       game.wind_direction = action_arg_direction;
9944
9945       break;
9946     }
9947
9948     case CA_SET_LEVEL_RANDOM_SEED:
9949     {
9950       // ensure that setting a new random seed while playing is predictable
9951       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9952
9953       break;
9954     }
9955
9956     // ---------- player actions  ---------------------------------------------
9957
9958     case CA_MOVE_PLAYER:
9959     case CA_MOVE_PLAYER_NEW:
9960     {
9961       // automatically move to the next field in specified direction
9962       for (i = 0; i < MAX_PLAYERS; i++)
9963         if (trigger_player_bits & (1 << i))
9964           if (action_type == CA_MOVE_PLAYER ||
9965               stored_player[i].MovPos == 0)
9966             stored_player[i].programmed_action = action_arg_direction;
9967
9968       break;
9969     }
9970
9971     case CA_EXIT_PLAYER:
9972     {
9973       for (i = 0; i < MAX_PLAYERS; i++)
9974         if (action_arg_player_bits & (1 << i))
9975           ExitPlayer(&stored_player[i]);
9976
9977       if (game.players_still_needed == 0)
9978         LevelSolved();
9979
9980       break;
9981     }
9982
9983     case CA_KILL_PLAYER:
9984     {
9985       for (i = 0; i < MAX_PLAYERS; i++)
9986         if (action_arg_player_bits & (1 << i))
9987           KillPlayer(&stored_player[i]);
9988
9989       break;
9990     }
9991
9992     case CA_SET_PLAYER_KEYS:
9993     {
9994       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9995       int element = getSpecialActionElement(action_arg_element,
9996                                             action_arg_number, EL_KEY_1);
9997
9998       if (IS_KEY(element))
9999       {
10000         for (i = 0; i < MAX_PLAYERS; i++)
10001         {
10002           if (trigger_player_bits & (1 << i))
10003           {
10004             stored_player[i].key[KEY_NR(element)] = key_state;
10005
10006             DrawGameDoorValues();
10007           }
10008         }
10009       }
10010
10011       break;
10012     }
10013
10014     case CA_SET_PLAYER_SPEED:
10015     {
10016       for (i = 0; i < MAX_PLAYERS; i++)
10017       {
10018         if (trigger_player_bits & (1 << i))
10019         {
10020           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10021
10022           if (action_arg == CA_ARG_SPEED_FASTER &&
10023               stored_player[i].cannot_move)
10024           {
10025             action_arg_number = STEPSIZE_VERY_SLOW;
10026           }
10027           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10028                    action_arg == CA_ARG_SPEED_FASTER)
10029           {
10030             action_arg_number = 2;
10031             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10032                            CA_MODE_MULTIPLY);
10033           }
10034           else if (action_arg == CA_ARG_NUMBER_RESET)
10035           {
10036             action_arg_number = level.initial_player_stepsize[i];
10037           }
10038
10039           move_stepsize =
10040             getModifiedActionNumber(move_stepsize,
10041                                     action_mode,
10042                                     action_arg_number,
10043                                     action_arg_number_min,
10044                                     action_arg_number_max);
10045
10046           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10047         }
10048       }
10049
10050       break;
10051     }
10052
10053     case CA_SET_PLAYER_SHIELD:
10054     {
10055       for (i = 0; i < MAX_PLAYERS; i++)
10056       {
10057         if (trigger_player_bits & (1 << i))
10058         {
10059           if (action_arg == CA_ARG_SHIELD_OFF)
10060           {
10061             stored_player[i].shield_normal_time_left = 0;
10062             stored_player[i].shield_deadly_time_left = 0;
10063           }
10064           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10065           {
10066             stored_player[i].shield_normal_time_left = 999999;
10067           }
10068           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10069           {
10070             stored_player[i].shield_normal_time_left = 999999;
10071             stored_player[i].shield_deadly_time_left = 999999;
10072           }
10073         }
10074       }
10075
10076       break;
10077     }
10078
10079     case CA_SET_PLAYER_GRAVITY:
10080     {
10081       for (i = 0; i < MAX_PLAYERS; i++)
10082       {
10083         if (trigger_player_bits & (1 << i))
10084         {
10085           stored_player[i].gravity =
10086             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10087              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10088              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10089              stored_player[i].gravity);
10090         }
10091       }
10092
10093       break;
10094     }
10095
10096     case CA_SET_PLAYER_ARTWORK:
10097     {
10098       for (i = 0; i < MAX_PLAYERS; i++)
10099       {
10100         if (trigger_player_bits & (1 << i))
10101         {
10102           int artwork_element = action_arg_element;
10103
10104           if (action_arg == CA_ARG_ELEMENT_RESET)
10105             artwork_element =
10106               (level.use_artwork_element[i] ? level.artwork_element[i] :
10107                stored_player[i].element_nr);
10108
10109           if (stored_player[i].artwork_element != artwork_element)
10110             stored_player[i].Frame = 0;
10111
10112           stored_player[i].artwork_element = artwork_element;
10113
10114           SetPlayerWaiting(&stored_player[i], FALSE);
10115
10116           // set number of special actions for bored and sleeping animation
10117           stored_player[i].num_special_action_bored =
10118             get_num_special_action(artwork_element,
10119                                    ACTION_BORING_1, ACTION_BORING_LAST);
10120           stored_player[i].num_special_action_sleeping =
10121             get_num_special_action(artwork_element,
10122                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10123         }
10124       }
10125
10126       break;
10127     }
10128
10129     case CA_SET_PLAYER_INVENTORY:
10130     {
10131       for (i = 0; i < MAX_PLAYERS; i++)
10132       {
10133         struct PlayerInfo *player = &stored_player[i];
10134         int j, k;
10135
10136         if (trigger_player_bits & (1 << i))
10137         {
10138           int inventory_element = action_arg_element;
10139
10140           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10141               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10142               action_arg == CA_ARG_ELEMENT_ACTION)
10143           {
10144             int element = inventory_element;
10145             int collect_count = element_info[element].collect_count_initial;
10146
10147             if (!IS_CUSTOM_ELEMENT(element))
10148               collect_count = 1;
10149
10150             if (collect_count == 0)
10151               player->inventory_infinite_element = element;
10152             else
10153               for (k = 0; k < collect_count; k++)
10154                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10155                   player->inventory_element[player->inventory_size++] =
10156                     element;
10157           }
10158           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10159                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10160                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10161           {
10162             if (player->inventory_infinite_element != EL_UNDEFINED &&
10163                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10164                                      action_arg_element_raw))
10165               player->inventory_infinite_element = EL_UNDEFINED;
10166
10167             for (k = 0, j = 0; j < player->inventory_size; j++)
10168             {
10169               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10170                                         action_arg_element_raw))
10171                 player->inventory_element[k++] = player->inventory_element[j];
10172             }
10173
10174             player->inventory_size = k;
10175           }
10176           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10177           {
10178             if (player->inventory_size > 0)
10179             {
10180               for (j = 0; j < player->inventory_size - 1; j++)
10181                 player->inventory_element[j] = player->inventory_element[j + 1];
10182
10183               player->inventory_size--;
10184             }
10185           }
10186           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10187           {
10188             if (player->inventory_size > 0)
10189               player->inventory_size--;
10190           }
10191           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10192           {
10193             player->inventory_infinite_element = EL_UNDEFINED;
10194             player->inventory_size = 0;
10195           }
10196           else if (action_arg == CA_ARG_INVENTORY_RESET)
10197           {
10198             player->inventory_infinite_element = EL_UNDEFINED;
10199             player->inventory_size = 0;
10200
10201             if (level.use_initial_inventory[i])
10202             {
10203               for (j = 0; j < level.initial_inventory_size[i]; j++)
10204               {
10205                 int element = level.initial_inventory_content[i][j];
10206                 int collect_count = element_info[element].collect_count_initial;
10207
10208                 if (!IS_CUSTOM_ELEMENT(element))
10209                   collect_count = 1;
10210
10211                 if (collect_count == 0)
10212                   player->inventory_infinite_element = element;
10213                 else
10214                   for (k = 0; k < collect_count; k++)
10215                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10216                       player->inventory_element[player->inventory_size++] =
10217                         element;
10218               }
10219             }
10220           }
10221         }
10222       }
10223
10224       break;
10225     }
10226
10227     // ---------- CE actions  -------------------------------------------------
10228
10229     case CA_SET_CE_VALUE:
10230     {
10231       int last_ce_value = CustomValue[x][y];
10232
10233       CustomValue[x][y] = action_arg_number_new;
10234
10235       if (CustomValue[x][y] != last_ce_value)
10236       {
10237         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10238         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10239
10240         if (CustomValue[x][y] == 0)
10241         {
10242           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10243           ChangeCount[x][y] = 0;        // allow at least one more change
10244
10245           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10246           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10247         }
10248       }
10249
10250       break;
10251     }
10252
10253     case CA_SET_CE_SCORE:
10254     {
10255       int last_ce_score = ei->collect_score;
10256
10257       ei->collect_score = action_arg_number_new;
10258
10259       if (ei->collect_score != last_ce_score)
10260       {
10261         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10262         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10263
10264         if (ei->collect_score == 0)
10265         {
10266           int xx, yy;
10267
10268           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10269           ChangeCount[x][y] = 0;        // allow at least one more change
10270
10271           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10272           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10273
10274           /*
10275             This is a very special case that seems to be a mixture between
10276             CheckElementChange() and CheckTriggeredElementChange(): while
10277             the first one only affects single elements that are triggered
10278             directly, the second one affects multiple elements in the playfield
10279             that are triggered indirectly by another element. This is a third
10280             case: Changing the CE score always affects multiple identical CEs,
10281             so every affected CE must be checked, not only the single CE for
10282             which the CE score was changed in the first place (as every instance
10283             of that CE shares the same CE score, and therefore also can change)!
10284           */
10285           SCAN_PLAYFIELD(xx, yy)
10286           {
10287             if (Feld[xx][yy] == element)
10288               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10289                                  CE_SCORE_GETS_ZERO);
10290           }
10291         }
10292       }
10293
10294       break;
10295     }
10296
10297     case CA_SET_CE_ARTWORK:
10298     {
10299       int artwork_element = action_arg_element;
10300       boolean reset_frame = FALSE;
10301       int xx, yy;
10302
10303       if (action_arg == CA_ARG_ELEMENT_RESET)
10304         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10305                            element);
10306
10307       if (ei->gfx_element != artwork_element)
10308         reset_frame = TRUE;
10309
10310       ei->gfx_element = artwork_element;
10311
10312       SCAN_PLAYFIELD(xx, yy)
10313       {
10314         if (Feld[xx][yy] == element)
10315         {
10316           if (reset_frame)
10317           {
10318             ResetGfxAnimation(xx, yy);
10319             ResetRandomAnimationValue(xx, yy);
10320           }
10321
10322           TEST_DrawLevelField(xx, yy);
10323         }
10324       }
10325
10326       break;
10327     }
10328
10329     // ---------- engine actions  ---------------------------------------------
10330
10331     case CA_SET_ENGINE_SCAN_MODE:
10332     {
10333       InitPlayfieldScanMode(action_arg);
10334
10335       break;
10336     }
10337
10338     default:
10339       break;
10340   }
10341 }
10342
10343 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10344 {
10345   int old_element = Feld[x][y];
10346   int new_element = GetElementFromGroupElement(element);
10347   int previous_move_direction = MovDir[x][y];
10348   int last_ce_value = CustomValue[x][y];
10349   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10350   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10351   boolean add_player_onto_element = (new_element_is_player &&
10352                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10353                                      IS_WALKABLE(old_element));
10354
10355   if (!add_player_onto_element)
10356   {
10357     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10358       RemoveMovingField(x, y);
10359     else
10360       RemoveField(x, y);
10361
10362     Feld[x][y] = new_element;
10363
10364     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10365       MovDir[x][y] = previous_move_direction;
10366
10367     if (element_info[new_element].use_last_ce_value)
10368       CustomValue[x][y] = last_ce_value;
10369
10370     InitField_WithBug1(x, y, FALSE);
10371
10372     new_element = Feld[x][y];   // element may have changed
10373
10374     ResetGfxAnimation(x, y);
10375     ResetRandomAnimationValue(x, y);
10376
10377     TEST_DrawLevelField(x, y);
10378
10379     if (GFX_CRUMBLED(new_element))
10380       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10381   }
10382
10383   // check if element under the player changes from accessible to unaccessible
10384   // (needed for special case of dropping element which then changes)
10385   // (must be checked after creating new element for walkable group elements)
10386   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10387       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10388   {
10389     Bang(x, y);
10390
10391     return;
10392   }
10393
10394   // "ChangeCount" not set yet to allow "entered by player" change one time
10395   if (new_element_is_player)
10396     RelocatePlayer(x, y, new_element);
10397
10398   if (is_change)
10399     ChangeCount[x][y]++;        // count number of changes in the same frame
10400
10401   TestIfBadThingTouchesPlayer(x, y);
10402   TestIfPlayerTouchesCustomElement(x, y);
10403   TestIfElementTouchesCustomElement(x, y);
10404 }
10405
10406 static void CreateField(int x, int y, int element)
10407 {
10408   CreateFieldExt(x, y, element, FALSE);
10409 }
10410
10411 static void CreateElementFromChange(int x, int y, int element)
10412 {
10413   element = GET_VALID_RUNTIME_ELEMENT(element);
10414
10415   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10416   {
10417     int old_element = Feld[x][y];
10418
10419     // prevent changed element from moving in same engine frame
10420     // unless both old and new element can either fall or move
10421     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10422         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10423       Stop[x][y] = TRUE;
10424   }
10425
10426   CreateFieldExt(x, y, element, TRUE);
10427 }
10428
10429 static boolean ChangeElement(int x, int y, int element, int page)
10430 {
10431   struct ElementInfo *ei = &element_info[element];
10432   struct ElementChangeInfo *change = &ei->change_page[page];
10433   int ce_value = CustomValue[x][y];
10434   int ce_score = ei->collect_score;
10435   int target_element;
10436   int old_element = Feld[x][y];
10437
10438   // always use default change event to prevent running into a loop
10439   if (ChangeEvent[x][y] == -1)
10440     ChangeEvent[x][y] = CE_DELAY;
10441
10442   if (ChangeEvent[x][y] == CE_DELAY)
10443   {
10444     // reset actual trigger element, trigger player and action element
10445     change->actual_trigger_element = EL_EMPTY;
10446     change->actual_trigger_player = EL_EMPTY;
10447     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10448     change->actual_trigger_side = CH_SIDE_NONE;
10449     change->actual_trigger_ce_value = 0;
10450     change->actual_trigger_ce_score = 0;
10451   }
10452
10453   // do not change elements more than a specified maximum number of changes
10454   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10455     return FALSE;
10456
10457   ChangeCount[x][y]++;          // count number of changes in the same frame
10458
10459   if (change->explode)
10460   {
10461     Bang(x, y);
10462
10463     return TRUE;
10464   }
10465
10466   if (change->use_target_content)
10467   {
10468     boolean complete_replace = TRUE;
10469     boolean can_replace[3][3];
10470     int xx, yy;
10471
10472     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10473     {
10474       boolean is_empty;
10475       boolean is_walkable;
10476       boolean is_diggable;
10477       boolean is_collectible;
10478       boolean is_removable;
10479       boolean is_destructible;
10480       int ex = x + xx - 1;
10481       int ey = y + yy - 1;
10482       int content_element = change->target_content.e[xx][yy];
10483       int e;
10484
10485       can_replace[xx][yy] = TRUE;
10486
10487       if (ex == x && ey == y)   // do not check changing element itself
10488         continue;
10489
10490       if (content_element == EL_EMPTY_SPACE)
10491       {
10492         can_replace[xx][yy] = FALSE;    // do not replace border with space
10493
10494         continue;
10495       }
10496
10497       if (!IN_LEV_FIELD(ex, ey))
10498       {
10499         can_replace[xx][yy] = FALSE;
10500         complete_replace = FALSE;
10501
10502         continue;
10503       }
10504
10505       e = Feld[ex][ey];
10506
10507       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10508         e = MovingOrBlocked2Element(ex, ey);
10509
10510       is_empty = (IS_FREE(ex, ey) ||
10511                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10512
10513       is_walkable     = (is_empty || IS_WALKABLE(e));
10514       is_diggable     = (is_empty || IS_DIGGABLE(e));
10515       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10516       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10517       is_removable    = (is_diggable || is_collectible);
10518
10519       can_replace[xx][yy] =
10520         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10521           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10522           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10523           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10524           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10525           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10526          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10527
10528       if (!can_replace[xx][yy])
10529         complete_replace = FALSE;
10530     }
10531
10532     if (!change->only_if_complete || complete_replace)
10533     {
10534       boolean something_has_changed = FALSE;
10535
10536       if (change->only_if_complete && change->use_random_replace &&
10537           RND(100) < change->random_percentage)
10538         return FALSE;
10539
10540       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10541       {
10542         int ex = x + xx - 1;
10543         int ey = y + yy - 1;
10544         int content_element;
10545
10546         if (can_replace[xx][yy] && (!change->use_random_replace ||
10547                                     RND(100) < change->random_percentage))
10548         {
10549           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10550             RemoveMovingField(ex, ey);
10551
10552           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10553
10554           content_element = change->target_content.e[xx][yy];
10555           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10556                                               ce_value, ce_score);
10557
10558           CreateElementFromChange(ex, ey, target_element);
10559
10560           something_has_changed = TRUE;
10561
10562           // for symmetry reasons, freeze newly created border elements
10563           if (ex != x || ey != y)
10564             Stop[ex][ey] = TRUE;        // no more moving in this frame
10565         }
10566       }
10567
10568       if (something_has_changed)
10569       {
10570         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10571         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10572       }
10573     }
10574   }
10575   else
10576   {
10577     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10578                                         ce_value, ce_score);
10579
10580     if (element == EL_DIAGONAL_GROWING ||
10581         element == EL_DIAGONAL_SHRINKING)
10582     {
10583       target_element = Store[x][y];
10584
10585       Store[x][y] = EL_EMPTY;
10586     }
10587
10588     CreateElementFromChange(x, y, target_element);
10589
10590     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10591     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10592   }
10593
10594   // this uses direct change before indirect change
10595   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10596
10597   return TRUE;
10598 }
10599
10600 static void HandleElementChange(int x, int y, int page)
10601 {
10602   int element = MovingOrBlocked2Element(x, y);
10603   struct ElementInfo *ei = &element_info[element];
10604   struct ElementChangeInfo *change = &ei->change_page[page];
10605   boolean handle_action_before_change = FALSE;
10606
10607 #ifdef DEBUG
10608   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10609       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10610   {
10611     printf("\n\n");
10612     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10613            x, y, element, element_info[element].token_name);
10614     printf("HandleElementChange(): This should never happen!\n");
10615     printf("\n\n");
10616   }
10617 #endif
10618
10619   // this can happen with classic bombs on walkable, changing elements
10620   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10621   {
10622     return;
10623   }
10624
10625   if (ChangeDelay[x][y] == 0)           // initialize element change
10626   {
10627     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10628
10629     if (change->can_change)
10630     {
10631       // !!! not clear why graphic animation should be reset at all here !!!
10632       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10633       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10634
10635       /*
10636         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10637
10638         When using an animation frame delay of 1 (this only happens with
10639         "sp_zonk.moving.left/right" in the classic graphics), the default
10640         (non-moving) animation shows wrong animation frames (while the
10641         moving animation, like "sp_zonk.moving.left/right", is correct,
10642         so this graphical bug never shows up with the classic graphics).
10643         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10644         be drawn instead of the correct frames 0,1,2,3. This is caused by
10645         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10646         an element change: First when the change delay ("ChangeDelay[][]")
10647         counter has reached zero after decrementing, then a second time in
10648         the next frame (after "GfxFrame[][]" was already incremented) when
10649         "ChangeDelay[][]" is reset to the initial delay value again.
10650
10651         This causes frame 0 to be drawn twice, while the last frame won't
10652         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10653
10654         As some animations may already be cleverly designed around this bug
10655         (at least the "Snake Bite" snake tail animation does this), it cannot
10656         simply be fixed here without breaking such existing animations.
10657         Unfortunately, it cannot easily be detected if a graphics set was
10658         designed "before" or "after" the bug was fixed. As a workaround,
10659         a new graphics set option "game.graphics_engine_version" was added
10660         to be able to specify the game's major release version for which the
10661         graphics set was designed, which can then be used to decide if the
10662         bugfix should be used (version 4 and above) or not (version 3 or
10663         below, or if no version was specified at all, as with old sets).
10664
10665         (The wrong/fixed animation frames can be tested with the test level set
10666         "test_gfxframe" and level "000", which contains a specially prepared
10667         custom element at level position (x/y) == (11/9) which uses the zonk
10668         animation mentioned above. Using "game.graphics_engine_version: 4"
10669         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10670         This can also be seen from the debug output for this test element.)
10671       */
10672
10673       // when a custom element is about to change (for example by change delay),
10674       // do not reset graphic animation when the custom element is moving
10675       if (game.graphics_engine_version < 4 &&
10676           !IS_MOVING(x, y))
10677       {
10678         ResetGfxAnimation(x, y);
10679         ResetRandomAnimationValue(x, y);
10680       }
10681
10682       if (change->pre_change_function)
10683         change->pre_change_function(x, y);
10684     }
10685   }
10686
10687   ChangeDelay[x][y]--;
10688
10689   if (ChangeDelay[x][y] != 0)           // continue element change
10690   {
10691     if (change->can_change)
10692     {
10693       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10694
10695       if (IS_ANIMATED(graphic))
10696         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10697
10698       if (change->change_function)
10699         change->change_function(x, y);
10700     }
10701   }
10702   else                                  // finish element change
10703   {
10704     if (ChangePage[x][y] != -1)         // remember page from delayed change
10705     {
10706       page = ChangePage[x][y];
10707       ChangePage[x][y] = -1;
10708
10709       change = &ei->change_page[page];
10710     }
10711
10712     if (IS_MOVING(x, y))                // never change a running system ;-)
10713     {
10714       ChangeDelay[x][y] = 1;            // try change after next move step
10715       ChangePage[x][y] = page;          // remember page to use for change
10716
10717       return;
10718     }
10719
10720     // special case: set new level random seed before changing element
10721     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10722       handle_action_before_change = TRUE;
10723
10724     if (change->has_action && handle_action_before_change)
10725       ExecuteCustomElementAction(x, y, element, page);
10726
10727     if (change->can_change)
10728     {
10729       if (ChangeElement(x, y, element, page))
10730       {
10731         if (change->post_change_function)
10732           change->post_change_function(x, y);
10733       }
10734     }
10735
10736     if (change->has_action && !handle_action_before_change)
10737       ExecuteCustomElementAction(x, y, element, page);
10738   }
10739 }
10740
10741 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10742                                               int trigger_element,
10743                                               int trigger_event,
10744                                               int trigger_player,
10745                                               int trigger_side,
10746                                               int trigger_page)
10747 {
10748   boolean change_done_any = FALSE;
10749   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10750   int i;
10751
10752   if (!(trigger_events[trigger_element][trigger_event]))
10753     return FALSE;
10754
10755   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10756
10757   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10758   {
10759     int element = EL_CUSTOM_START + i;
10760     boolean change_done = FALSE;
10761     int p;
10762
10763     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10764         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10765       continue;
10766
10767     for (p = 0; p < element_info[element].num_change_pages; p++)
10768     {
10769       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10770
10771       if (change->can_change_or_has_action &&
10772           change->has_event[trigger_event] &&
10773           change->trigger_side & trigger_side &&
10774           change->trigger_player & trigger_player &&
10775           change->trigger_page & trigger_page_bits &&
10776           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10777       {
10778         change->actual_trigger_element = trigger_element;
10779         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10780         change->actual_trigger_player_bits = trigger_player;
10781         change->actual_trigger_side = trigger_side;
10782         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10783         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10784
10785         if ((change->can_change && !change_done) || change->has_action)
10786         {
10787           int x, y;
10788
10789           SCAN_PLAYFIELD(x, y)
10790           {
10791             if (Feld[x][y] == element)
10792             {
10793               if (change->can_change && !change_done)
10794               {
10795                 // if element already changed in this frame, not only prevent
10796                 // another element change (checked in ChangeElement()), but
10797                 // also prevent additional element actions for this element
10798
10799                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10800                     !level.use_action_after_change_bug)
10801                   continue;
10802
10803                 ChangeDelay[x][y] = 1;
10804                 ChangeEvent[x][y] = trigger_event;
10805
10806                 HandleElementChange(x, y, p);
10807               }
10808               else if (change->has_action)
10809               {
10810                 // if element already changed in this frame, not only prevent
10811                 // another element change (checked in ChangeElement()), but
10812                 // also prevent additional element actions for this element
10813
10814                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10815                     !level.use_action_after_change_bug)
10816                   continue;
10817
10818                 ExecuteCustomElementAction(x, y, element, p);
10819                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10820               }
10821             }
10822           }
10823
10824           if (change->can_change)
10825           {
10826             change_done = TRUE;
10827             change_done_any = TRUE;
10828           }
10829         }
10830       }
10831     }
10832   }
10833
10834   RECURSION_LOOP_DETECTION_END();
10835
10836   return change_done_any;
10837 }
10838
10839 static boolean CheckElementChangeExt(int x, int y,
10840                                      int element,
10841                                      int trigger_element,
10842                                      int trigger_event,
10843                                      int trigger_player,
10844                                      int trigger_side)
10845 {
10846   boolean change_done = FALSE;
10847   int p;
10848
10849   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10850       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10851     return FALSE;
10852
10853   if (Feld[x][y] == EL_BLOCKED)
10854   {
10855     Blocked2Moving(x, y, &x, &y);
10856     element = Feld[x][y];
10857   }
10858
10859   // check if element has already changed or is about to change after moving
10860   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10861        Feld[x][y] != element) ||
10862
10863       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10864        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10865         ChangePage[x][y] != -1)))
10866     return FALSE;
10867
10868   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10869
10870   for (p = 0; p < element_info[element].num_change_pages; p++)
10871   {
10872     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10873
10874     /* check trigger element for all events where the element that is checked
10875        for changing interacts with a directly adjacent element -- this is
10876        different to element changes that affect other elements to change on the
10877        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10878     boolean check_trigger_element =
10879       (trigger_event == CE_TOUCHING_X ||
10880        trigger_event == CE_HITTING_X ||
10881        trigger_event == CE_HIT_BY_X ||
10882        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10883
10884     if (change->can_change_or_has_action &&
10885         change->has_event[trigger_event] &&
10886         change->trigger_side & trigger_side &&
10887         change->trigger_player & trigger_player &&
10888         (!check_trigger_element ||
10889          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10890     {
10891       change->actual_trigger_element = trigger_element;
10892       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10893       change->actual_trigger_player_bits = trigger_player;
10894       change->actual_trigger_side = trigger_side;
10895       change->actual_trigger_ce_value = CustomValue[x][y];
10896       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10897
10898       // special case: trigger element not at (x,y) position for some events
10899       if (check_trigger_element)
10900       {
10901         static struct
10902         {
10903           int dx, dy;
10904         } move_xy[] =
10905           {
10906             {  0,  0 },
10907             { -1,  0 },
10908             { +1,  0 },
10909             {  0,  0 },
10910             {  0, -1 },
10911             {  0,  0 }, { 0, 0 }, { 0, 0 },
10912             {  0, +1 }
10913           };
10914
10915         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10916         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10917
10918         change->actual_trigger_ce_value = CustomValue[xx][yy];
10919         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10920       }
10921
10922       if (change->can_change && !change_done)
10923       {
10924         ChangeDelay[x][y] = 1;
10925         ChangeEvent[x][y] = trigger_event;
10926
10927         HandleElementChange(x, y, p);
10928
10929         change_done = TRUE;
10930       }
10931       else if (change->has_action)
10932       {
10933         ExecuteCustomElementAction(x, y, element, p);
10934         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10935       }
10936     }
10937   }
10938
10939   RECURSION_LOOP_DETECTION_END();
10940
10941   return change_done;
10942 }
10943
10944 static void PlayPlayerSound(struct PlayerInfo *player)
10945 {
10946   int jx = player->jx, jy = player->jy;
10947   int sound_element = player->artwork_element;
10948   int last_action = player->last_action_waiting;
10949   int action = player->action_waiting;
10950
10951   if (player->is_waiting)
10952   {
10953     if (action != last_action)
10954       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10955     else
10956       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10957   }
10958   else
10959   {
10960     if (action != last_action)
10961       StopSound(element_info[sound_element].sound[last_action]);
10962
10963     if (last_action == ACTION_SLEEPING)
10964       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10965   }
10966 }
10967
10968 static void PlayAllPlayersSound(void)
10969 {
10970   int i;
10971
10972   for (i = 0; i < MAX_PLAYERS; i++)
10973     if (stored_player[i].active)
10974       PlayPlayerSound(&stored_player[i]);
10975 }
10976
10977 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10978 {
10979   boolean last_waiting = player->is_waiting;
10980   int move_dir = player->MovDir;
10981
10982   player->dir_waiting = move_dir;
10983   player->last_action_waiting = player->action_waiting;
10984
10985   if (is_waiting)
10986   {
10987     if (!last_waiting)          // not waiting -> waiting
10988     {
10989       player->is_waiting = TRUE;
10990
10991       player->frame_counter_bored =
10992         FrameCounter +
10993         game.player_boring_delay_fixed +
10994         GetSimpleRandom(game.player_boring_delay_random);
10995       player->frame_counter_sleeping =
10996         FrameCounter +
10997         game.player_sleeping_delay_fixed +
10998         GetSimpleRandom(game.player_sleeping_delay_random);
10999
11000       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11001     }
11002
11003     if (game.player_sleeping_delay_fixed +
11004         game.player_sleeping_delay_random > 0 &&
11005         player->anim_delay_counter == 0 &&
11006         player->post_delay_counter == 0 &&
11007         FrameCounter >= player->frame_counter_sleeping)
11008       player->is_sleeping = TRUE;
11009     else if (game.player_boring_delay_fixed +
11010              game.player_boring_delay_random > 0 &&
11011              FrameCounter >= player->frame_counter_bored)
11012       player->is_bored = TRUE;
11013
11014     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11015                               player->is_bored ? ACTION_BORING :
11016                               ACTION_WAITING);
11017
11018     if (player->is_sleeping && player->use_murphy)
11019     {
11020       // special case for sleeping Murphy when leaning against non-free tile
11021
11022       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11023           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11024            !IS_MOVING(player->jx - 1, player->jy)))
11025         move_dir = MV_LEFT;
11026       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11027                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11028                 !IS_MOVING(player->jx + 1, player->jy)))
11029         move_dir = MV_RIGHT;
11030       else
11031         player->is_sleeping = FALSE;
11032
11033       player->dir_waiting = move_dir;
11034     }
11035
11036     if (player->is_sleeping)
11037     {
11038       if (player->num_special_action_sleeping > 0)
11039       {
11040         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11041         {
11042           int last_special_action = player->special_action_sleeping;
11043           int num_special_action = player->num_special_action_sleeping;
11044           int special_action =
11045             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11046              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11047              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11048              last_special_action + 1 : ACTION_SLEEPING);
11049           int special_graphic =
11050             el_act_dir2img(player->artwork_element, special_action, move_dir);
11051
11052           player->anim_delay_counter =
11053             graphic_info[special_graphic].anim_delay_fixed +
11054             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11055           player->post_delay_counter =
11056             graphic_info[special_graphic].post_delay_fixed +
11057             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11058
11059           player->special_action_sleeping = special_action;
11060         }
11061
11062         if (player->anim_delay_counter > 0)
11063         {
11064           player->action_waiting = player->special_action_sleeping;
11065           player->anim_delay_counter--;
11066         }
11067         else if (player->post_delay_counter > 0)
11068         {
11069           player->post_delay_counter--;
11070         }
11071       }
11072     }
11073     else if (player->is_bored)
11074     {
11075       if (player->num_special_action_bored > 0)
11076       {
11077         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11078         {
11079           int special_action =
11080             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11081           int special_graphic =
11082             el_act_dir2img(player->artwork_element, special_action, move_dir);
11083
11084           player->anim_delay_counter =
11085             graphic_info[special_graphic].anim_delay_fixed +
11086             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11087           player->post_delay_counter =
11088             graphic_info[special_graphic].post_delay_fixed +
11089             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11090
11091           player->special_action_bored = special_action;
11092         }
11093
11094         if (player->anim_delay_counter > 0)
11095         {
11096           player->action_waiting = player->special_action_bored;
11097           player->anim_delay_counter--;
11098         }
11099         else if (player->post_delay_counter > 0)
11100         {
11101           player->post_delay_counter--;
11102         }
11103       }
11104     }
11105   }
11106   else if (last_waiting)        // waiting -> not waiting
11107   {
11108     player->is_waiting = FALSE;
11109     player->is_bored = FALSE;
11110     player->is_sleeping = FALSE;
11111
11112     player->frame_counter_bored = -1;
11113     player->frame_counter_sleeping = -1;
11114
11115     player->anim_delay_counter = 0;
11116     player->post_delay_counter = 0;
11117
11118     player->dir_waiting = player->MovDir;
11119     player->action_waiting = ACTION_DEFAULT;
11120
11121     player->special_action_bored = ACTION_DEFAULT;
11122     player->special_action_sleeping = ACTION_DEFAULT;
11123   }
11124 }
11125
11126 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11127 {
11128   if ((!player->is_moving  && player->was_moving) ||
11129       (player->MovPos == 0 && player->was_moving) ||
11130       (player->is_snapping && !player->was_snapping) ||
11131       (player->is_dropping && !player->was_dropping))
11132   {
11133     if (!CheckSaveEngineSnapshotToList())
11134       return;
11135
11136     player->was_moving = FALSE;
11137     player->was_snapping = TRUE;
11138     player->was_dropping = TRUE;
11139   }
11140   else
11141   {
11142     if (player->is_moving)
11143       player->was_moving = TRUE;
11144
11145     if (!player->is_snapping)
11146       player->was_snapping = FALSE;
11147
11148     if (!player->is_dropping)
11149       player->was_dropping = FALSE;
11150   }
11151 }
11152
11153 static void CheckSingleStepMode(struct PlayerInfo *player)
11154 {
11155   if (tape.single_step && tape.recording && !tape.pausing)
11156   {
11157     /* as it is called "single step mode", just return to pause mode when the
11158        player stopped moving after one tile (or never starts moving at all) */
11159     if (!player->is_moving &&
11160         !player->is_pushing &&
11161         !player->is_dropping_pressed)
11162       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11163   }
11164
11165   CheckSaveEngineSnapshot(player);
11166 }
11167
11168 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11169 {
11170   int left      = player_action & JOY_LEFT;
11171   int right     = player_action & JOY_RIGHT;
11172   int up        = player_action & JOY_UP;
11173   int down      = player_action & JOY_DOWN;
11174   int button1   = player_action & JOY_BUTTON_1;
11175   int button2   = player_action & JOY_BUTTON_2;
11176   int dx        = (left ? -1 : right ? 1 : 0);
11177   int dy        = (up   ? -1 : down  ? 1 : 0);
11178
11179   if (!player->active || tape.pausing)
11180     return 0;
11181
11182   if (player_action)
11183   {
11184     if (button1)
11185       SnapField(player, dx, dy);
11186     else
11187     {
11188       if (button2)
11189         DropElement(player);
11190
11191       MovePlayer(player, dx, dy);
11192     }
11193
11194     CheckSingleStepMode(player);
11195
11196     SetPlayerWaiting(player, FALSE);
11197
11198     return player_action;
11199   }
11200   else
11201   {
11202     // no actions for this player (no input at player's configured device)
11203
11204     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11205     SnapField(player, 0, 0);
11206     CheckGravityMovementWhenNotMoving(player);
11207
11208     if (player->MovPos == 0)
11209       SetPlayerWaiting(player, TRUE);
11210
11211     if (player->MovPos == 0)    // needed for tape.playing
11212       player->is_moving = FALSE;
11213
11214     player->is_dropping = FALSE;
11215     player->is_dropping_pressed = FALSE;
11216     player->drop_pressed_delay = 0;
11217
11218     CheckSingleStepMode(player);
11219
11220     return 0;
11221   }
11222 }
11223
11224 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11225                                          byte *tape_action)
11226 {
11227   if (!tape.use_mouse_actions)
11228     return;
11229
11230   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11231   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11232   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11233 }
11234
11235 static void SetTapeActionFromMouseAction(byte *tape_action,
11236                                          struct MouseActionInfo *mouse_action)
11237 {
11238   if (!tape.use_mouse_actions)
11239     return;
11240
11241   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11242   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11243   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11244 }
11245
11246 static void CheckLevelSolved(void)
11247 {
11248   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11249   {
11250     if (game_em.level_solved &&
11251         !game_em.game_over)                             // game won
11252     {
11253       LevelSolved();
11254
11255       game_em.game_over = TRUE;
11256
11257       game.all_players_gone = TRUE;
11258     }
11259
11260     if (game_em.game_over)                              // game lost
11261       game.all_players_gone = TRUE;
11262   }
11263   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11264   {
11265     if (game_sp.level_solved &&
11266         !game_sp.game_over)                             // game won
11267     {
11268       LevelSolved();
11269
11270       game_sp.game_over = TRUE;
11271
11272       game.all_players_gone = TRUE;
11273     }
11274
11275     if (game_sp.game_over)                              // game lost
11276       game.all_players_gone = TRUE;
11277   }
11278   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11279   {
11280     if (game_mm.level_solved &&
11281         !game_mm.game_over)                             // game won
11282     {
11283       LevelSolved();
11284
11285       game_mm.game_over = TRUE;
11286
11287       game.all_players_gone = TRUE;
11288     }
11289
11290     if (game_mm.game_over)                              // game lost
11291       game.all_players_gone = TRUE;
11292   }
11293 }
11294
11295 static void CheckLevelTime(void)
11296 {
11297   int i;
11298
11299   if (TimeFrames >= FRAMES_PER_SECOND)
11300   {
11301     TimeFrames = 0;
11302     TapeTime++;
11303
11304     for (i = 0; i < MAX_PLAYERS; i++)
11305     {
11306       struct PlayerInfo *player = &stored_player[i];
11307
11308       if (SHIELD_ON(player))
11309       {
11310         player->shield_normal_time_left--;
11311
11312         if (player->shield_deadly_time_left > 0)
11313           player->shield_deadly_time_left--;
11314       }
11315     }
11316
11317     if (!game.LevelSolved && !level.use_step_counter)
11318     {
11319       TimePlayed++;
11320
11321       if (TimeLeft > 0)
11322       {
11323         TimeLeft--;
11324
11325         if (TimeLeft <= 10 && setup.time_limit)
11326           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11327
11328         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11329            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11330
11331         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11332
11333         if (!TimeLeft && setup.time_limit)
11334         {
11335           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11336             game_em.lev->killed_out_of_time = TRUE;
11337           else
11338             for (i = 0; i < MAX_PLAYERS; i++)
11339               KillPlayer(&stored_player[i]);
11340         }
11341       }
11342       else if (game.no_time_limit && !game.all_players_gone)
11343       {
11344         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11345       }
11346
11347       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11348     }
11349
11350     if (tape.recording || tape.playing)
11351       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11352   }
11353
11354   if (tape.recording || tape.playing)
11355     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11356
11357   UpdateAndDisplayGameControlValues();
11358 }
11359
11360 void AdvanceFrameAndPlayerCounters(int player_nr)
11361 {
11362   int i;
11363
11364   // advance frame counters (global frame counter and time frame counter)
11365   FrameCounter++;
11366   TimeFrames++;
11367
11368   // advance player counters (counters for move delay, move animation etc.)
11369   for (i = 0; i < MAX_PLAYERS; i++)
11370   {
11371     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11372     int move_delay_value = stored_player[i].move_delay_value;
11373     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11374
11375     if (!advance_player_counters)       // not all players may be affected
11376       continue;
11377
11378     if (move_frames == 0)       // less than one move per game frame
11379     {
11380       int stepsize = TILEX / move_delay_value;
11381       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11382       int count = (stored_player[i].is_moving ?
11383                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11384
11385       if (count % delay == 0)
11386         move_frames = 1;
11387     }
11388
11389     stored_player[i].Frame += move_frames;
11390
11391     if (stored_player[i].MovPos != 0)
11392       stored_player[i].StepFrame += move_frames;
11393
11394     if (stored_player[i].move_delay > 0)
11395       stored_player[i].move_delay--;
11396
11397     // due to bugs in previous versions, counter must count up, not down
11398     if (stored_player[i].push_delay != -1)
11399       stored_player[i].push_delay++;
11400
11401     if (stored_player[i].drop_delay > 0)
11402       stored_player[i].drop_delay--;
11403
11404     if (stored_player[i].is_dropping_pressed)
11405       stored_player[i].drop_pressed_delay++;
11406   }
11407 }
11408
11409 void StartGameActions(boolean init_network_game, boolean record_tape,
11410                       int random_seed)
11411 {
11412   unsigned int new_random_seed = InitRND(random_seed);
11413
11414   if (record_tape)
11415     TapeStartRecording(new_random_seed);
11416
11417   if (init_network_game)
11418   {
11419     SendToServer_LevelFile();
11420     SendToServer_StartPlaying();
11421
11422     return;
11423   }
11424
11425   InitGame();
11426 }
11427
11428 static void GameActionsExt(void)
11429 {
11430 #if 0
11431   static unsigned int game_frame_delay = 0;
11432 #endif
11433   unsigned int game_frame_delay_value;
11434   byte *recorded_player_action;
11435   byte summarized_player_action = 0;
11436   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11437   int i;
11438
11439   // detect endless loops, caused by custom element programming
11440   if (recursion_loop_detected && recursion_loop_depth == 0)
11441   {
11442     char *message = getStringCat3("Internal Error! Element ",
11443                                   EL_NAME(recursion_loop_element),
11444                                   " caused endless loop! Quit the game?");
11445
11446     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11447           EL_NAME(recursion_loop_element));
11448
11449     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11450
11451     recursion_loop_detected = FALSE;    // if game should be continued
11452
11453     free(message);
11454
11455     return;
11456   }
11457
11458   if (game.restart_level)
11459     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11460
11461   CheckLevelSolved();
11462
11463   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11464     GameWon();
11465
11466   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11467     TapeStop();
11468
11469   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11470     return;
11471
11472   game_frame_delay_value =
11473     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11474
11475   if (tape.playing && tape.warp_forward && !tape.pausing)
11476     game_frame_delay_value = 0;
11477
11478   SetVideoFrameDelay(game_frame_delay_value);
11479
11480   // (de)activate virtual buttons depending on current game status
11481   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11482   {
11483     if (game.all_players_gone)  // if no players there to be controlled anymore
11484       SetOverlayActive(FALSE);
11485     else if (!tape.playing)     // if game continues after tape stopped playing
11486       SetOverlayActive(TRUE);
11487   }
11488
11489 #if 0
11490 #if 0
11491   // ---------- main game synchronization point ----------
11492
11493   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11494
11495   printf("::: skip == %d\n", skip);
11496
11497 #else
11498   // ---------- main game synchronization point ----------
11499
11500   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11501 #endif
11502 #endif
11503
11504   if (network_playing && !network_player_action_received)
11505   {
11506     // try to get network player actions in time
11507
11508     // last chance to get network player actions without main loop delay
11509     HandleNetworking();
11510
11511     // game was quit by network peer
11512     if (game_status != GAME_MODE_PLAYING)
11513       return;
11514
11515     // check if network player actions still missing and game still running
11516     if (!network_player_action_received && !checkGameEnded())
11517       return;           // failed to get network player actions in time
11518
11519     // do not yet reset "network_player_action_received" (for tape.pausing)
11520   }
11521
11522   if (tape.pausing)
11523     return;
11524
11525   // at this point we know that we really continue executing the game
11526
11527   network_player_action_received = FALSE;
11528
11529   // when playing tape, read previously recorded player input from tape data
11530   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11531
11532   local_player->effective_mouse_action = local_player->mouse_action;
11533
11534   if (recorded_player_action != NULL)
11535     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11536                                  recorded_player_action);
11537
11538   // TapePlayAction() may return NULL when toggling to "pause before death"
11539   if (tape.pausing)
11540     return;
11541
11542   if (tape.set_centered_player)
11543   {
11544     game.centered_player_nr_next = tape.centered_player_nr_next;
11545     game.set_centered_player = TRUE;
11546   }
11547
11548   for (i = 0; i < MAX_PLAYERS; i++)
11549   {
11550     summarized_player_action |= stored_player[i].action;
11551
11552     if (!network_playing && (game.team_mode || tape.playing))
11553       stored_player[i].effective_action = stored_player[i].action;
11554   }
11555
11556   if (network_playing && !checkGameEnded())
11557     SendToServer_MovePlayer(summarized_player_action);
11558
11559   // summarize all actions at local players mapped input device position
11560   // (this allows using different input devices in single player mode)
11561   if (!network.enabled && !game.team_mode)
11562     stored_player[map_player_action[local_player->index_nr]].effective_action =
11563       summarized_player_action;
11564
11565   // summarize all actions at centered player in local team mode
11566   if (tape.recording &&
11567       setup.team_mode && !network.enabled &&
11568       setup.input_on_focus &&
11569       game.centered_player_nr != -1)
11570   {
11571     for (i = 0; i < MAX_PLAYERS; i++)
11572       stored_player[map_player_action[i]].effective_action =
11573         (i == game.centered_player_nr ? summarized_player_action : 0);
11574   }
11575
11576   if (recorded_player_action != NULL)
11577     for (i = 0; i < MAX_PLAYERS; i++)
11578       stored_player[i].effective_action = recorded_player_action[i];
11579
11580   for (i = 0; i < MAX_PLAYERS; i++)
11581   {
11582     tape_action[i] = stored_player[i].effective_action;
11583
11584     /* (this may happen in the RND game engine if a player was not present on
11585        the playfield on level start, but appeared later from a custom element */
11586     if (setup.team_mode &&
11587         tape.recording &&
11588         tape_action[i] &&
11589         !tape.player_participates[i])
11590       tape.player_participates[i] = TRUE;
11591   }
11592
11593   SetTapeActionFromMouseAction(tape_action,
11594                                &local_player->effective_mouse_action);
11595
11596   // only record actions from input devices, but not programmed actions
11597   if (tape.recording)
11598     TapeRecordAction(tape_action);
11599
11600   // remember if game was played (especially after tape stopped playing)
11601   if (!tape.playing && summarized_player_action)
11602     game.GamePlayed = TRUE;
11603
11604 #if USE_NEW_PLAYER_ASSIGNMENTS
11605   // !!! also map player actions in single player mode !!!
11606   // if (game.team_mode)
11607   if (1)
11608   {
11609     byte mapped_action[MAX_PLAYERS];
11610
11611 #if DEBUG_PLAYER_ACTIONS
11612     printf(":::");
11613     for (i = 0; i < MAX_PLAYERS; i++)
11614       printf(" %d, ", stored_player[i].effective_action);
11615 #endif
11616
11617     for (i = 0; i < MAX_PLAYERS; i++)
11618       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11619
11620     for (i = 0; i < MAX_PLAYERS; i++)
11621       stored_player[i].effective_action = mapped_action[i];
11622
11623 #if DEBUG_PLAYER_ACTIONS
11624     printf(" =>");
11625     for (i = 0; i < MAX_PLAYERS; i++)
11626       printf(" %d, ", stored_player[i].effective_action);
11627     printf("\n");
11628 #endif
11629   }
11630 #if DEBUG_PLAYER_ACTIONS
11631   else
11632   {
11633     printf(":::");
11634     for (i = 0; i < MAX_PLAYERS; i++)
11635       printf(" %d, ", stored_player[i].effective_action);
11636     printf("\n");
11637   }
11638 #endif
11639 #endif
11640
11641   for (i = 0; i < MAX_PLAYERS; i++)
11642   {
11643     // allow engine snapshot in case of changed movement attempt
11644     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11645         (stored_player[i].effective_action & KEY_MOTION))
11646       game.snapshot.changed_action = TRUE;
11647
11648     // allow engine snapshot in case of snapping/dropping attempt
11649     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11650         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11651       game.snapshot.changed_action = TRUE;
11652
11653     game.snapshot.last_action[i] = stored_player[i].effective_action;
11654   }
11655
11656   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11657   {
11658     GameActions_EM_Main();
11659   }
11660   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11661   {
11662     GameActions_SP_Main();
11663   }
11664   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11665   {
11666     GameActions_MM_Main();
11667   }
11668   else
11669   {
11670     GameActions_RND_Main();
11671   }
11672
11673   BlitScreenToBitmap(backbuffer);
11674
11675   CheckLevelSolved();
11676   CheckLevelTime();
11677
11678   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11679
11680   if (global.show_frames_per_second)
11681   {
11682     static unsigned int fps_counter = 0;
11683     static int fps_frames = 0;
11684     unsigned int fps_delay_ms = Counter() - fps_counter;
11685
11686     fps_frames++;
11687
11688     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11689     {
11690       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11691
11692       fps_frames = 0;
11693       fps_counter = Counter();
11694
11695       // always draw FPS to screen after FPS value was updated
11696       redraw_mask |= REDRAW_FPS;
11697     }
11698
11699     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11700     if (GetDrawDeactivationMask() == REDRAW_NONE)
11701       redraw_mask |= REDRAW_FPS;
11702   }
11703 }
11704
11705 static void GameActions_CheckSaveEngineSnapshot(void)
11706 {
11707   if (!game.snapshot.save_snapshot)
11708     return;
11709
11710   // clear flag for saving snapshot _before_ saving snapshot
11711   game.snapshot.save_snapshot = FALSE;
11712
11713   SaveEngineSnapshotToList();
11714 }
11715
11716 void GameActions(void)
11717 {
11718   GameActionsExt();
11719
11720   GameActions_CheckSaveEngineSnapshot();
11721 }
11722
11723 void GameActions_EM_Main(void)
11724 {
11725   byte effective_action[MAX_PLAYERS];
11726   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11727   int i;
11728
11729   for (i = 0; i < MAX_PLAYERS; i++)
11730     effective_action[i] = stored_player[i].effective_action;
11731
11732   GameActions_EM(effective_action, warp_mode);
11733 }
11734
11735 void GameActions_SP_Main(void)
11736 {
11737   byte effective_action[MAX_PLAYERS];
11738   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11739   int i;
11740
11741   for (i = 0; i < MAX_PLAYERS; i++)
11742     effective_action[i] = stored_player[i].effective_action;
11743
11744   GameActions_SP(effective_action, warp_mode);
11745
11746   for (i = 0; i < MAX_PLAYERS; i++)
11747   {
11748     if (stored_player[i].force_dropping)
11749       stored_player[i].action |= KEY_BUTTON_DROP;
11750
11751     stored_player[i].force_dropping = FALSE;
11752   }
11753 }
11754
11755 void GameActions_MM_Main(void)
11756 {
11757   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11758
11759   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11760 }
11761
11762 void GameActions_RND_Main(void)
11763 {
11764   GameActions_RND();
11765 }
11766
11767 void GameActions_RND(void)
11768 {
11769   static struct MouseActionInfo mouse_action_last = { 0 };
11770   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11771   int magic_wall_x = 0, magic_wall_y = 0;
11772   int i, x, y, element, graphic, last_gfx_frame;
11773
11774   InitPlayfieldScanModeVars();
11775
11776   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11777   {
11778     SCAN_PLAYFIELD(x, y)
11779     {
11780       ChangeCount[x][y] = 0;
11781       ChangeEvent[x][y] = -1;
11782     }
11783   }
11784
11785   if (game.set_centered_player)
11786   {
11787     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11788
11789     // switching to "all players" only possible if all players fit to screen
11790     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11791     {
11792       game.centered_player_nr_next = game.centered_player_nr;
11793       game.set_centered_player = FALSE;
11794     }
11795
11796     // do not switch focus to non-existing (or non-active) player
11797     if (game.centered_player_nr_next >= 0 &&
11798         !stored_player[game.centered_player_nr_next].active)
11799     {
11800       game.centered_player_nr_next = game.centered_player_nr;
11801       game.set_centered_player = FALSE;
11802     }
11803   }
11804
11805   if (game.set_centered_player &&
11806       ScreenMovPos == 0)        // screen currently aligned at tile position
11807   {
11808     int sx, sy;
11809
11810     if (game.centered_player_nr_next == -1)
11811     {
11812       setScreenCenteredToAllPlayers(&sx, &sy);
11813     }
11814     else
11815     {
11816       sx = stored_player[game.centered_player_nr_next].jx;
11817       sy = stored_player[game.centered_player_nr_next].jy;
11818     }
11819
11820     game.centered_player_nr = game.centered_player_nr_next;
11821     game.set_centered_player = FALSE;
11822
11823     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11824     DrawGameDoorValues();
11825   }
11826
11827   for (i = 0; i < MAX_PLAYERS; i++)
11828   {
11829     int actual_player_action = stored_player[i].effective_action;
11830
11831 #if 1
11832     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11833        - rnd_equinox_tetrachloride 048
11834        - rnd_equinox_tetrachloride_ii 096
11835        - rnd_emanuel_schmieg 002
11836        - doctor_sloan_ww 001, 020
11837     */
11838     if (stored_player[i].MovPos == 0)
11839       CheckGravityMovement(&stored_player[i]);
11840 #endif
11841
11842     // overwrite programmed action with tape action
11843     if (stored_player[i].programmed_action)
11844       actual_player_action = stored_player[i].programmed_action;
11845
11846     PlayerActions(&stored_player[i], actual_player_action);
11847
11848     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11849   }
11850
11851   ScrollScreen(NULL, SCROLL_GO_ON);
11852
11853   /* for backwards compatibility, the following code emulates a fixed bug that
11854      occured when pushing elements (causing elements that just made their last
11855      pushing step to already (if possible) make their first falling step in the
11856      same game frame, which is bad); this code is also needed to use the famous
11857      "spring push bug" which is used in older levels and might be wanted to be
11858      used also in newer levels, but in this case the buggy pushing code is only
11859      affecting the "spring" element and no other elements */
11860
11861   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11862   {
11863     for (i = 0; i < MAX_PLAYERS; i++)
11864     {
11865       struct PlayerInfo *player = &stored_player[i];
11866       int x = player->jx;
11867       int y = player->jy;
11868
11869       if (player->active && player->is_pushing && player->is_moving &&
11870           IS_MOVING(x, y) &&
11871           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11872            Feld[x][y] == EL_SPRING))
11873       {
11874         ContinueMoving(x, y);
11875
11876         // continue moving after pushing (this is actually a bug)
11877         if (!IS_MOVING(x, y))
11878           Stop[x][y] = FALSE;
11879       }
11880     }
11881   }
11882
11883   SCAN_PLAYFIELD(x, y)
11884   {
11885     Last[x][y] = Feld[x][y];
11886
11887     ChangeCount[x][y] = 0;
11888     ChangeEvent[x][y] = -1;
11889
11890     // this must be handled before main playfield loop
11891     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11892     {
11893       MovDelay[x][y]--;
11894       if (MovDelay[x][y] <= 0)
11895         RemoveField(x, y);
11896     }
11897
11898     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11899     {
11900       MovDelay[x][y]--;
11901       if (MovDelay[x][y] <= 0)
11902       {
11903         RemoveField(x, y);
11904         TEST_DrawLevelField(x, y);
11905
11906         TestIfElementTouchesCustomElement(x, y);        // for empty space
11907       }
11908     }
11909
11910 #if DEBUG
11911     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11912     {
11913       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11914       printf("GameActions(): This should never happen!\n");
11915
11916       ChangePage[x][y] = -1;
11917     }
11918 #endif
11919
11920     Stop[x][y] = FALSE;
11921     if (WasJustMoving[x][y] > 0)
11922       WasJustMoving[x][y]--;
11923     if (WasJustFalling[x][y] > 0)
11924       WasJustFalling[x][y]--;
11925     if (CheckCollision[x][y] > 0)
11926       CheckCollision[x][y]--;
11927     if (CheckImpact[x][y] > 0)
11928       CheckImpact[x][y]--;
11929
11930     GfxFrame[x][y]++;
11931
11932     /* reset finished pushing action (not done in ContinueMoving() to allow
11933        continuous pushing animation for elements with zero push delay) */
11934     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11935     {
11936       ResetGfxAnimation(x, y);
11937       TEST_DrawLevelField(x, y);
11938     }
11939
11940 #if DEBUG
11941     if (IS_BLOCKED(x, y))
11942     {
11943       int oldx, oldy;
11944
11945       Blocked2Moving(x, y, &oldx, &oldy);
11946       if (!IS_MOVING(oldx, oldy))
11947       {
11948         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11949         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11950         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11951         printf("GameActions(): This should never happen!\n");
11952       }
11953     }
11954 #endif
11955   }
11956
11957   if (mouse_action.button)
11958   {
11959     int new_button = (mouse_action.button && mouse_action_last.button == 0);
11960
11961     x = mouse_action.lx;
11962     y = mouse_action.ly;
11963     element = Feld[x][y];
11964
11965     if (new_button)
11966     {
11967       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
11968       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
11969     }
11970
11971     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
11972     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
11973   }
11974
11975   SCAN_PLAYFIELD(x, y)
11976   {
11977     element = Feld[x][y];
11978     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11979     last_gfx_frame = GfxFrame[x][y];
11980
11981     ResetGfxFrame(x, y);
11982
11983     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11984       DrawLevelGraphicAnimation(x, y, graphic);
11985
11986     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11987         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11988       ResetRandomAnimationValue(x, y);
11989
11990     SetRandomAnimationValue(x, y);
11991
11992     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11993
11994     if (IS_INACTIVE(element))
11995     {
11996       if (IS_ANIMATED(graphic))
11997         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11998
11999       continue;
12000     }
12001
12002     // this may take place after moving, so 'element' may have changed
12003     if (IS_CHANGING(x, y) &&
12004         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12005     {
12006       int page = element_info[element].event_page_nr[CE_DELAY];
12007
12008       HandleElementChange(x, y, page);
12009
12010       element = Feld[x][y];
12011       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12012     }
12013
12014     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12015     {
12016       StartMoving(x, y);
12017
12018       element = Feld[x][y];
12019       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12020
12021       if (IS_ANIMATED(graphic) &&
12022           !IS_MOVING(x, y) &&
12023           !Stop[x][y])
12024         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12025
12026       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12027         TEST_DrawTwinkleOnField(x, y);
12028     }
12029     else if (element == EL_ACID)
12030     {
12031       if (!Stop[x][y])
12032         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12033     }
12034     else if ((element == EL_EXIT_OPEN ||
12035               element == EL_EM_EXIT_OPEN ||
12036               element == EL_SP_EXIT_OPEN ||
12037               element == EL_STEEL_EXIT_OPEN ||
12038               element == EL_EM_STEEL_EXIT_OPEN ||
12039               element == EL_SP_TERMINAL ||
12040               element == EL_SP_TERMINAL_ACTIVE ||
12041               element == EL_EXTRA_TIME ||
12042               element == EL_SHIELD_NORMAL ||
12043               element == EL_SHIELD_DEADLY) &&
12044              IS_ANIMATED(graphic))
12045       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12046     else if (IS_MOVING(x, y))
12047       ContinueMoving(x, y);
12048     else if (IS_ACTIVE_BOMB(element))
12049       CheckDynamite(x, y);
12050     else if (element == EL_AMOEBA_GROWING)
12051       AmoebeWaechst(x, y);
12052     else if (element == EL_AMOEBA_SHRINKING)
12053       AmoebaDisappearing(x, y);
12054
12055 #if !USE_NEW_AMOEBA_CODE
12056     else if (IS_AMOEBALIVE(element))
12057       AmoebeAbleger(x, y);
12058 #endif
12059
12060     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12061       Life(x, y);
12062     else if (element == EL_EXIT_CLOSED)
12063       CheckExit(x, y);
12064     else if (element == EL_EM_EXIT_CLOSED)
12065       CheckExitEM(x, y);
12066     else if (element == EL_STEEL_EXIT_CLOSED)
12067       CheckExitSteel(x, y);
12068     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12069       CheckExitSteelEM(x, y);
12070     else if (element == EL_SP_EXIT_CLOSED)
12071       CheckExitSP(x, y);
12072     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12073              element == EL_EXPANDABLE_STEELWALL_GROWING)
12074       MauerWaechst(x, y);
12075     else if (element == EL_EXPANDABLE_WALL ||
12076              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12077              element == EL_EXPANDABLE_WALL_VERTICAL ||
12078              element == EL_EXPANDABLE_WALL_ANY ||
12079              element == EL_BD_EXPANDABLE_WALL)
12080       MauerAbleger(x, y);
12081     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12082              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12083              element == EL_EXPANDABLE_STEELWALL_ANY)
12084       MauerAblegerStahl(x, y);
12085     else if (element == EL_FLAMES)
12086       CheckForDragon(x, y);
12087     else if (element == EL_EXPLOSION)
12088       ; // drawing of correct explosion animation is handled separately
12089     else if (element == EL_ELEMENT_SNAPPING ||
12090              element == EL_DIAGONAL_SHRINKING ||
12091              element == EL_DIAGONAL_GROWING)
12092     {
12093       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12094
12095       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12096     }
12097     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12098       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12099
12100     if (IS_BELT_ACTIVE(element))
12101       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12102
12103     if (game.magic_wall_active)
12104     {
12105       int jx = local_player->jx, jy = local_player->jy;
12106
12107       // play the element sound at the position nearest to the player
12108       if ((element == EL_MAGIC_WALL_FULL ||
12109            element == EL_MAGIC_WALL_ACTIVE ||
12110            element == EL_MAGIC_WALL_EMPTYING ||
12111            element == EL_BD_MAGIC_WALL_FULL ||
12112            element == EL_BD_MAGIC_WALL_ACTIVE ||
12113            element == EL_BD_MAGIC_WALL_EMPTYING ||
12114            element == EL_DC_MAGIC_WALL_FULL ||
12115            element == EL_DC_MAGIC_WALL_ACTIVE ||
12116            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12117           ABS(x - jx) + ABS(y - jy) <
12118           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12119       {
12120         magic_wall_x = x;
12121         magic_wall_y = y;
12122       }
12123     }
12124   }
12125
12126 #if USE_NEW_AMOEBA_CODE
12127   // new experimental amoeba growth stuff
12128   if (!(FrameCounter % 8))
12129   {
12130     static unsigned int random = 1684108901;
12131
12132     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12133     {
12134       x = RND(lev_fieldx);
12135       y = RND(lev_fieldy);
12136       element = Feld[x][y];
12137
12138       if (!IS_PLAYER(x,y) &&
12139           (element == EL_EMPTY ||
12140            CAN_GROW_INTO(element) ||
12141            element == EL_QUICKSAND_EMPTY ||
12142            element == EL_QUICKSAND_FAST_EMPTY ||
12143            element == EL_ACID_SPLASH_LEFT ||
12144            element == EL_ACID_SPLASH_RIGHT))
12145       {
12146         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12147             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12148             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12149             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12150           Feld[x][y] = EL_AMOEBA_DROP;
12151       }
12152
12153       random = random * 129 + 1;
12154     }
12155   }
12156 #endif
12157
12158   game.explosions_delayed = FALSE;
12159
12160   SCAN_PLAYFIELD(x, y)
12161   {
12162     element = Feld[x][y];
12163
12164     if (ExplodeField[x][y])
12165       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12166     else if (element == EL_EXPLOSION)
12167       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12168
12169     ExplodeField[x][y] = EX_TYPE_NONE;
12170   }
12171
12172   game.explosions_delayed = TRUE;
12173
12174   if (game.magic_wall_active)
12175   {
12176     if (!(game.magic_wall_time_left % 4))
12177     {
12178       int element = Feld[magic_wall_x][magic_wall_y];
12179
12180       if (element == EL_BD_MAGIC_WALL_FULL ||
12181           element == EL_BD_MAGIC_WALL_ACTIVE ||
12182           element == EL_BD_MAGIC_WALL_EMPTYING)
12183         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12184       else if (element == EL_DC_MAGIC_WALL_FULL ||
12185                element == EL_DC_MAGIC_WALL_ACTIVE ||
12186                element == EL_DC_MAGIC_WALL_EMPTYING)
12187         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12188       else
12189         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12190     }
12191
12192     if (game.magic_wall_time_left > 0)
12193     {
12194       game.magic_wall_time_left--;
12195
12196       if (!game.magic_wall_time_left)
12197       {
12198         SCAN_PLAYFIELD(x, y)
12199         {
12200           element = Feld[x][y];
12201
12202           if (element == EL_MAGIC_WALL_ACTIVE ||
12203               element == EL_MAGIC_WALL_FULL)
12204           {
12205             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12206             TEST_DrawLevelField(x, y);
12207           }
12208           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12209                    element == EL_BD_MAGIC_WALL_FULL)
12210           {
12211             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12212             TEST_DrawLevelField(x, y);
12213           }
12214           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12215                    element == EL_DC_MAGIC_WALL_FULL)
12216           {
12217             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12218             TEST_DrawLevelField(x, y);
12219           }
12220         }
12221
12222         game.magic_wall_active = FALSE;
12223       }
12224     }
12225   }
12226
12227   if (game.light_time_left > 0)
12228   {
12229     game.light_time_left--;
12230
12231     if (game.light_time_left == 0)
12232       RedrawAllLightSwitchesAndInvisibleElements();
12233   }
12234
12235   if (game.timegate_time_left > 0)
12236   {
12237     game.timegate_time_left--;
12238
12239     if (game.timegate_time_left == 0)
12240       CloseAllOpenTimegates();
12241   }
12242
12243   if (game.lenses_time_left > 0)
12244   {
12245     game.lenses_time_left--;
12246
12247     if (game.lenses_time_left == 0)
12248       RedrawAllInvisibleElementsForLenses();
12249   }
12250
12251   if (game.magnify_time_left > 0)
12252   {
12253     game.magnify_time_left--;
12254
12255     if (game.magnify_time_left == 0)
12256       RedrawAllInvisibleElementsForMagnifier();
12257   }
12258
12259   for (i = 0; i < MAX_PLAYERS; i++)
12260   {
12261     struct PlayerInfo *player = &stored_player[i];
12262
12263     if (SHIELD_ON(player))
12264     {
12265       if (player->shield_deadly_time_left)
12266         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12267       else if (player->shield_normal_time_left)
12268         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12269     }
12270   }
12271
12272 #if USE_DELAYED_GFX_REDRAW
12273   SCAN_PLAYFIELD(x, y)
12274   {
12275     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12276     {
12277       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12278          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12279
12280       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12281         DrawLevelField(x, y);
12282
12283       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12284         DrawLevelFieldCrumbled(x, y);
12285
12286       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12287         DrawLevelFieldCrumbledNeighbours(x, y);
12288
12289       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12290         DrawTwinkleOnField(x, y);
12291     }
12292
12293     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12294   }
12295 #endif
12296
12297   DrawAllPlayers();
12298   PlayAllPlayersSound();
12299
12300   for (i = 0; i < MAX_PLAYERS; i++)
12301   {
12302     struct PlayerInfo *player = &stored_player[i];
12303
12304     if (player->show_envelope != 0 && (!player->active ||
12305                                        player->MovPos == 0))
12306     {
12307       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12308
12309       player->show_envelope = 0;
12310     }
12311   }
12312
12313   // use random number generator in every frame to make it less predictable
12314   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12315     RND(1);
12316
12317   mouse_action_last = mouse_action;
12318 }
12319
12320 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12321 {
12322   int min_x = x, min_y = y, max_x = x, max_y = y;
12323   int i;
12324
12325   for (i = 0; i < MAX_PLAYERS; i++)
12326   {
12327     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12328
12329     if (!stored_player[i].active || &stored_player[i] == player)
12330       continue;
12331
12332     min_x = MIN(min_x, jx);
12333     min_y = MIN(min_y, jy);
12334     max_x = MAX(max_x, jx);
12335     max_y = MAX(max_y, jy);
12336   }
12337
12338   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12339 }
12340
12341 static boolean AllPlayersInVisibleScreen(void)
12342 {
12343   int i;
12344
12345   for (i = 0; i < MAX_PLAYERS; i++)
12346   {
12347     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12348
12349     if (!stored_player[i].active)
12350       continue;
12351
12352     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12353       return FALSE;
12354   }
12355
12356   return TRUE;
12357 }
12358
12359 void ScrollLevel(int dx, int dy)
12360 {
12361   int scroll_offset = 2 * TILEX_VAR;
12362   int x, y;
12363
12364   BlitBitmap(drawto_field, drawto_field,
12365              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12366              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12367              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12368              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12369              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12370              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12371
12372   if (dx != 0)
12373   {
12374     x = (dx == 1 ? BX1 : BX2);
12375     for (y = BY1; y <= BY2; y++)
12376       DrawScreenField(x, y);
12377   }
12378
12379   if (dy != 0)
12380   {
12381     y = (dy == 1 ? BY1 : BY2);
12382     for (x = BX1; x <= BX2; x++)
12383       DrawScreenField(x, y);
12384   }
12385
12386   redraw_mask |= REDRAW_FIELD;
12387 }
12388
12389 static boolean canFallDown(struct PlayerInfo *player)
12390 {
12391   int jx = player->jx, jy = player->jy;
12392
12393   return (IN_LEV_FIELD(jx, jy + 1) &&
12394           (IS_FREE(jx, jy + 1) ||
12395            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12396           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12397           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12398 }
12399
12400 static boolean canPassField(int x, int y, int move_dir)
12401 {
12402   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12403   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12404   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12405   int nextx = x + dx;
12406   int nexty = y + dy;
12407   int element = Feld[x][y];
12408
12409   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12410           !CAN_MOVE(element) &&
12411           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12412           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12413           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12414 }
12415
12416 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12417 {
12418   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12419   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12420   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12421   int newx = x + dx;
12422   int newy = y + dy;
12423
12424   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12425           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12426           (IS_DIGGABLE(Feld[newx][newy]) ||
12427            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12428            canPassField(newx, newy, move_dir)));
12429 }
12430
12431 static void CheckGravityMovement(struct PlayerInfo *player)
12432 {
12433   if (player->gravity && !player->programmed_action)
12434   {
12435     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12436     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12437     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12438     int jx = player->jx, jy = player->jy;
12439     boolean player_is_moving_to_valid_field =
12440       (!player_is_snapping &&
12441        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12442         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12443     boolean player_can_fall_down = canFallDown(player);
12444
12445     if (player_can_fall_down &&
12446         !player_is_moving_to_valid_field)
12447       player->programmed_action = MV_DOWN;
12448   }
12449 }
12450
12451 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12452 {
12453   return CheckGravityMovement(player);
12454
12455   if (player->gravity && !player->programmed_action)
12456   {
12457     int jx = player->jx, jy = player->jy;
12458     boolean field_under_player_is_free =
12459       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12460     boolean player_is_standing_on_valid_field =
12461       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12462        (IS_WALKABLE(Feld[jx][jy]) &&
12463         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12464
12465     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12466       player->programmed_action = MV_DOWN;
12467   }
12468 }
12469
12470 /*
12471   MovePlayerOneStep()
12472   -----------------------------------------------------------------------------
12473   dx, dy:               direction (non-diagonal) to try to move the player to
12474   real_dx, real_dy:     direction as read from input device (can be diagonal)
12475 */
12476
12477 boolean MovePlayerOneStep(struct PlayerInfo *player,
12478                           int dx, int dy, int real_dx, int real_dy)
12479 {
12480   int jx = player->jx, jy = player->jy;
12481   int new_jx = jx + dx, new_jy = jy + dy;
12482   int can_move;
12483   boolean player_can_move = !player->cannot_move;
12484
12485   if (!player->active || (!dx && !dy))
12486     return MP_NO_ACTION;
12487
12488   player->MovDir = (dx < 0 ? MV_LEFT :
12489                     dx > 0 ? MV_RIGHT :
12490                     dy < 0 ? MV_UP :
12491                     dy > 0 ? MV_DOWN :  MV_NONE);
12492
12493   if (!IN_LEV_FIELD(new_jx, new_jy))
12494     return MP_NO_ACTION;
12495
12496   if (!player_can_move)
12497   {
12498     if (player->MovPos == 0)
12499     {
12500       player->is_moving = FALSE;
12501       player->is_digging = FALSE;
12502       player->is_collecting = FALSE;
12503       player->is_snapping = FALSE;
12504       player->is_pushing = FALSE;
12505     }
12506   }
12507
12508   if (!network.enabled && game.centered_player_nr == -1 &&
12509       !AllPlayersInSight(player, new_jx, new_jy))
12510     return MP_NO_ACTION;
12511
12512   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12513   if (can_move != MP_MOVING)
12514     return can_move;
12515
12516   // check if DigField() has caused relocation of the player
12517   if (player->jx != jx || player->jy != jy)
12518     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12519
12520   StorePlayer[jx][jy] = 0;
12521   player->last_jx = jx;
12522   player->last_jy = jy;
12523   player->jx = new_jx;
12524   player->jy = new_jy;
12525   StorePlayer[new_jx][new_jy] = player->element_nr;
12526
12527   if (player->move_delay_value_next != -1)
12528   {
12529     player->move_delay_value = player->move_delay_value_next;
12530     player->move_delay_value_next = -1;
12531   }
12532
12533   player->MovPos =
12534     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12535
12536   player->step_counter++;
12537
12538   PlayerVisit[jx][jy] = FrameCounter;
12539
12540   player->is_moving = TRUE;
12541
12542 #if 1
12543   // should better be called in MovePlayer(), but this breaks some tapes
12544   ScrollPlayer(player, SCROLL_INIT);
12545 #endif
12546
12547   return MP_MOVING;
12548 }
12549
12550 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12551 {
12552   int jx = player->jx, jy = player->jy;
12553   int old_jx = jx, old_jy = jy;
12554   int moved = MP_NO_ACTION;
12555
12556   if (!player->active)
12557     return FALSE;
12558
12559   if (!dx && !dy)
12560   {
12561     if (player->MovPos == 0)
12562     {
12563       player->is_moving = FALSE;
12564       player->is_digging = FALSE;
12565       player->is_collecting = FALSE;
12566       player->is_snapping = FALSE;
12567       player->is_pushing = FALSE;
12568     }
12569
12570     return FALSE;
12571   }
12572
12573   if (player->move_delay > 0)
12574     return FALSE;
12575
12576   player->move_delay = -1;              // set to "uninitialized" value
12577
12578   // store if player is automatically moved to next field
12579   player->is_auto_moving = (player->programmed_action != MV_NONE);
12580
12581   // remove the last programmed player action
12582   player->programmed_action = 0;
12583
12584   if (player->MovPos)
12585   {
12586     // should only happen if pre-1.2 tape recordings are played
12587     // this is only for backward compatibility
12588
12589     int original_move_delay_value = player->move_delay_value;
12590
12591 #if DEBUG
12592     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12593            tape.counter);
12594 #endif
12595
12596     // scroll remaining steps with finest movement resolution
12597     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12598
12599     while (player->MovPos)
12600     {
12601       ScrollPlayer(player, SCROLL_GO_ON);
12602       ScrollScreen(NULL, SCROLL_GO_ON);
12603
12604       AdvanceFrameAndPlayerCounters(player->index_nr);
12605
12606       DrawAllPlayers();
12607       BackToFront_WithFrameDelay(0);
12608     }
12609
12610     player->move_delay_value = original_move_delay_value;
12611   }
12612
12613   player->is_active = FALSE;
12614
12615   if (player->last_move_dir & MV_HORIZONTAL)
12616   {
12617     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12618       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12619   }
12620   else
12621   {
12622     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12623       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12624   }
12625
12626   if (!moved && !player->is_active)
12627   {
12628     player->is_moving = FALSE;
12629     player->is_digging = FALSE;
12630     player->is_collecting = FALSE;
12631     player->is_snapping = FALSE;
12632     player->is_pushing = FALSE;
12633   }
12634
12635   jx = player->jx;
12636   jy = player->jy;
12637
12638   if (moved & MP_MOVING && !ScreenMovPos &&
12639       (player->index_nr == game.centered_player_nr ||
12640        game.centered_player_nr == -1))
12641   {
12642     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12643
12644     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12645     {
12646       // actual player has left the screen -- scroll in that direction
12647       if (jx != old_jx)         // player has moved horizontally
12648         scroll_x += (jx - old_jx);
12649       else                      // player has moved vertically
12650         scroll_y += (jy - old_jy);
12651     }
12652     else
12653     {
12654       int offset_raw = game.scroll_delay_value;
12655
12656       if (jx != old_jx)         // player has moved horizontally
12657       {
12658         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12659         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12660         int new_scroll_x = jx - MIDPOSX + offset_x;
12661
12662         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12663             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12664           scroll_x = new_scroll_x;
12665
12666         // don't scroll over playfield boundaries
12667         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12668
12669         // don't scroll more than one field at a time
12670         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12671
12672         // don't scroll against the player's moving direction
12673         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12674             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12675           scroll_x = old_scroll_x;
12676       }
12677       else                      // player has moved vertically
12678       {
12679         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12680         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12681         int new_scroll_y = jy - MIDPOSY + offset_y;
12682
12683         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12684             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12685           scroll_y = new_scroll_y;
12686
12687         // don't scroll over playfield boundaries
12688         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12689
12690         // don't scroll more than one field at a time
12691         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12692
12693         // don't scroll against the player's moving direction
12694         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12695             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12696           scroll_y = old_scroll_y;
12697       }
12698     }
12699
12700     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12701     {
12702       if (!network.enabled && game.centered_player_nr == -1 &&
12703           !AllPlayersInVisibleScreen())
12704       {
12705         scroll_x = old_scroll_x;
12706         scroll_y = old_scroll_y;
12707       }
12708       else
12709       {
12710         ScrollScreen(player, SCROLL_INIT);
12711         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12712       }
12713     }
12714   }
12715
12716   player->StepFrame = 0;
12717
12718   if (moved & MP_MOVING)
12719   {
12720     if (old_jx != jx && old_jy == jy)
12721       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12722     else if (old_jx == jx && old_jy != jy)
12723       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12724
12725     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12726
12727     player->last_move_dir = player->MovDir;
12728     player->is_moving = TRUE;
12729     player->is_snapping = FALSE;
12730     player->is_switching = FALSE;
12731     player->is_dropping = FALSE;
12732     player->is_dropping_pressed = FALSE;
12733     player->drop_pressed_delay = 0;
12734
12735 #if 0
12736     // should better be called here than above, but this breaks some tapes
12737     ScrollPlayer(player, SCROLL_INIT);
12738 #endif
12739   }
12740   else
12741   {
12742     CheckGravityMovementWhenNotMoving(player);
12743
12744     player->is_moving = FALSE;
12745
12746     /* at this point, the player is allowed to move, but cannot move right now
12747        (e.g. because of something blocking the way) -- ensure that the player
12748        is also allowed to move in the next frame (in old versions before 3.1.1,
12749        the player was forced to wait again for eight frames before next try) */
12750
12751     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12752       player->move_delay = 0;   // allow direct movement in the next frame
12753   }
12754
12755   if (player->move_delay == -1)         // not yet initialized by DigField()
12756     player->move_delay = player->move_delay_value;
12757
12758   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12759   {
12760     TestIfPlayerTouchesBadThing(jx, jy);
12761     TestIfPlayerTouchesCustomElement(jx, jy);
12762   }
12763
12764   if (!player->active)
12765     RemovePlayer(player);
12766
12767   return moved;
12768 }
12769
12770 void ScrollPlayer(struct PlayerInfo *player, int mode)
12771 {
12772   int jx = player->jx, jy = player->jy;
12773   int last_jx = player->last_jx, last_jy = player->last_jy;
12774   int move_stepsize = TILEX / player->move_delay_value;
12775
12776   if (!player->active)
12777     return;
12778
12779   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12780     return;
12781
12782   if (mode == SCROLL_INIT)
12783   {
12784     player->actual_frame_counter = FrameCounter;
12785     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12786
12787     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12788         Feld[last_jx][last_jy] == EL_EMPTY)
12789     {
12790       int last_field_block_delay = 0;   // start with no blocking at all
12791       int block_delay_adjustment = player->block_delay_adjustment;
12792
12793       // if player blocks last field, add delay for exactly one move
12794       if (player->block_last_field)
12795       {
12796         last_field_block_delay += player->move_delay_value;
12797
12798         // when blocking enabled, prevent moving up despite gravity
12799         if (player->gravity && player->MovDir == MV_UP)
12800           block_delay_adjustment = -1;
12801       }
12802
12803       // add block delay adjustment (also possible when not blocking)
12804       last_field_block_delay += block_delay_adjustment;
12805
12806       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12807       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12808     }
12809
12810     if (player->MovPos != 0)    // player has not yet reached destination
12811       return;
12812   }
12813   else if (!FrameReached(&player->actual_frame_counter, 1))
12814     return;
12815
12816   if (player->MovPos != 0)
12817   {
12818     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12819     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12820
12821     // before DrawPlayer() to draw correct player graphic for this case
12822     if (player->MovPos == 0)
12823       CheckGravityMovement(player);
12824   }
12825
12826   if (player->MovPos == 0)      // player reached destination field
12827   {
12828     if (player->move_delay_reset_counter > 0)
12829     {
12830       player->move_delay_reset_counter--;
12831
12832       if (player->move_delay_reset_counter == 0)
12833       {
12834         // continue with normal speed after quickly moving through gate
12835         HALVE_PLAYER_SPEED(player);
12836
12837         // be able to make the next move without delay
12838         player->move_delay = 0;
12839       }
12840     }
12841
12842     player->last_jx = jx;
12843     player->last_jy = jy;
12844
12845     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12846         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12847         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12848         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12849         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12850         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12851         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12852         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12853     {
12854       ExitPlayer(player);
12855
12856       if (game.players_still_needed == 0 &&
12857           (game.friends_still_needed == 0 ||
12858            IS_SP_ELEMENT(Feld[jx][jy])))
12859         LevelSolved();
12860     }
12861
12862     // this breaks one level: "machine", level 000
12863     {
12864       int move_direction = player->MovDir;
12865       int enter_side = MV_DIR_OPPOSITE(move_direction);
12866       int leave_side = move_direction;
12867       int old_jx = last_jx;
12868       int old_jy = last_jy;
12869       int old_element = Feld[old_jx][old_jy];
12870       int new_element = Feld[jx][jy];
12871
12872       if (IS_CUSTOM_ELEMENT(old_element))
12873         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12874                                    CE_LEFT_BY_PLAYER,
12875                                    player->index_bit, leave_side);
12876
12877       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12878                                           CE_PLAYER_LEAVES_X,
12879                                           player->index_bit, leave_side);
12880
12881       if (IS_CUSTOM_ELEMENT(new_element))
12882         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12883                                    player->index_bit, enter_side);
12884
12885       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12886                                           CE_PLAYER_ENTERS_X,
12887                                           player->index_bit, enter_side);
12888
12889       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12890                                         CE_MOVE_OF_X, move_direction);
12891     }
12892
12893     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12894     {
12895       TestIfPlayerTouchesBadThing(jx, jy);
12896       TestIfPlayerTouchesCustomElement(jx, jy);
12897
12898       /* needed because pushed element has not yet reached its destination,
12899          so it would trigger a change event at its previous field location */
12900       if (!player->is_pushing)
12901         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12902
12903       if (!player->active)
12904         RemovePlayer(player);
12905     }
12906
12907     if (!game.LevelSolved && level.use_step_counter)
12908     {
12909       int i;
12910
12911       TimePlayed++;
12912
12913       if (TimeLeft > 0)
12914       {
12915         TimeLeft--;
12916
12917         if (TimeLeft <= 10 && setup.time_limit)
12918           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12919
12920         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12921
12922         DisplayGameControlValues();
12923
12924         if (!TimeLeft && setup.time_limit)
12925           for (i = 0; i < MAX_PLAYERS; i++)
12926             KillPlayer(&stored_player[i]);
12927       }
12928       else if (game.no_time_limit && !game.all_players_gone)
12929       {
12930         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12931
12932         DisplayGameControlValues();
12933       }
12934     }
12935
12936     if (tape.single_step && tape.recording && !tape.pausing &&
12937         !player->programmed_action)
12938       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12939
12940     if (!player->programmed_action)
12941       CheckSaveEngineSnapshot(player);
12942   }
12943 }
12944
12945 void ScrollScreen(struct PlayerInfo *player, int mode)
12946 {
12947   static unsigned int screen_frame_counter = 0;
12948
12949   if (mode == SCROLL_INIT)
12950   {
12951     // set scrolling step size according to actual player's moving speed
12952     ScrollStepSize = TILEX / player->move_delay_value;
12953
12954     screen_frame_counter = FrameCounter;
12955     ScreenMovDir = player->MovDir;
12956     ScreenMovPos = player->MovPos;
12957     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12958     return;
12959   }
12960   else if (!FrameReached(&screen_frame_counter, 1))
12961     return;
12962
12963   if (ScreenMovPos)
12964   {
12965     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12966     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12967     redraw_mask |= REDRAW_FIELD;
12968   }
12969   else
12970     ScreenMovDir = MV_NONE;
12971 }
12972
12973 void TestIfPlayerTouchesCustomElement(int x, int y)
12974 {
12975   static int xy[4][2] =
12976   {
12977     { 0, -1 },
12978     { -1, 0 },
12979     { +1, 0 },
12980     { 0, +1 }
12981   };
12982   static int trigger_sides[4][2] =
12983   {
12984     // center side       border side
12985     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12986     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12987     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12988     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12989   };
12990   static int touch_dir[4] =
12991   {
12992     MV_LEFT | MV_RIGHT,
12993     MV_UP   | MV_DOWN,
12994     MV_UP   | MV_DOWN,
12995     MV_LEFT | MV_RIGHT
12996   };
12997   int center_element = Feld[x][y];      // should always be non-moving!
12998   int i;
12999
13000   for (i = 0; i < NUM_DIRECTIONS; i++)
13001   {
13002     int xx = x + xy[i][0];
13003     int yy = y + xy[i][1];
13004     int center_side = trigger_sides[i][0];
13005     int border_side = trigger_sides[i][1];
13006     int border_element;
13007
13008     if (!IN_LEV_FIELD(xx, yy))
13009       continue;
13010
13011     if (IS_PLAYER(x, y))                // player found at center element
13012     {
13013       struct PlayerInfo *player = PLAYERINFO(x, y);
13014
13015       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13016         border_element = Feld[xx][yy];          // may be moving!
13017       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13018         border_element = Feld[xx][yy];
13019       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13020         border_element = MovingOrBlocked2Element(xx, yy);
13021       else
13022         continue;               // center and border element do not touch
13023
13024       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13025                                  player->index_bit, border_side);
13026       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13027                                           CE_PLAYER_TOUCHES_X,
13028                                           player->index_bit, border_side);
13029
13030       {
13031         /* use player element that is initially defined in the level playfield,
13032            not the player element that corresponds to the runtime player number
13033            (example: a level that contains EL_PLAYER_3 as the only player would
13034            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13035         int player_element = PLAYERINFO(x, y)->initial_element;
13036
13037         CheckElementChangeBySide(xx, yy, border_element, player_element,
13038                                  CE_TOUCHING_X, border_side);
13039       }
13040     }
13041     else if (IS_PLAYER(xx, yy))         // player found at border element
13042     {
13043       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13044
13045       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13046       {
13047         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13048           continue;             // center and border element do not touch
13049       }
13050
13051       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13052                                  player->index_bit, center_side);
13053       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13054                                           CE_PLAYER_TOUCHES_X,
13055                                           player->index_bit, center_side);
13056
13057       {
13058         /* use player element that is initially defined in the level playfield,
13059            not the player element that corresponds to the runtime player number
13060            (example: a level that contains EL_PLAYER_3 as the only player would
13061            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13062         int player_element = PLAYERINFO(xx, yy)->initial_element;
13063
13064         CheckElementChangeBySide(x, y, center_element, player_element,
13065                                  CE_TOUCHING_X, center_side);
13066       }
13067
13068       break;
13069     }
13070   }
13071 }
13072
13073 void TestIfElementTouchesCustomElement(int x, int y)
13074 {
13075   static int xy[4][2] =
13076   {
13077     { 0, -1 },
13078     { -1, 0 },
13079     { +1, 0 },
13080     { 0, +1 }
13081   };
13082   static int trigger_sides[4][2] =
13083   {
13084     // center side      border side
13085     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13086     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13087     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13088     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13089   };
13090   static int touch_dir[4] =
13091   {
13092     MV_LEFT | MV_RIGHT,
13093     MV_UP   | MV_DOWN,
13094     MV_UP   | MV_DOWN,
13095     MV_LEFT | MV_RIGHT
13096   };
13097   boolean change_center_element = FALSE;
13098   int center_element = Feld[x][y];      // should always be non-moving!
13099   int border_element_old[NUM_DIRECTIONS];
13100   int i;
13101
13102   for (i = 0; i < NUM_DIRECTIONS; i++)
13103   {
13104     int xx = x + xy[i][0];
13105     int yy = y + xy[i][1];
13106     int border_element;
13107
13108     border_element_old[i] = -1;
13109
13110     if (!IN_LEV_FIELD(xx, yy))
13111       continue;
13112
13113     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13114       border_element = Feld[xx][yy];    // may be moving!
13115     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13116       border_element = Feld[xx][yy];
13117     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13118       border_element = MovingOrBlocked2Element(xx, yy);
13119     else
13120       continue;                 // center and border element do not touch
13121
13122     border_element_old[i] = border_element;
13123   }
13124
13125   for (i = 0; i < NUM_DIRECTIONS; i++)
13126   {
13127     int xx = x + xy[i][0];
13128     int yy = y + xy[i][1];
13129     int center_side = trigger_sides[i][0];
13130     int border_element = border_element_old[i];
13131
13132     if (border_element == -1)
13133       continue;
13134
13135     // check for change of border element
13136     CheckElementChangeBySide(xx, yy, border_element, center_element,
13137                              CE_TOUCHING_X, center_side);
13138
13139     // (center element cannot be player, so we dont have to check this here)
13140   }
13141
13142   for (i = 0; i < NUM_DIRECTIONS; i++)
13143   {
13144     int xx = x + xy[i][0];
13145     int yy = y + xy[i][1];
13146     int border_side = trigger_sides[i][1];
13147     int border_element = border_element_old[i];
13148
13149     if (border_element == -1)
13150       continue;
13151
13152     // check for change of center element (but change it only once)
13153     if (!change_center_element)
13154       change_center_element =
13155         CheckElementChangeBySide(x, y, center_element, border_element,
13156                                  CE_TOUCHING_X, border_side);
13157
13158     if (IS_PLAYER(xx, yy))
13159     {
13160       /* use player element that is initially defined in the level playfield,
13161          not the player element that corresponds to the runtime player number
13162          (example: a level that contains EL_PLAYER_3 as the only player would
13163          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13164       int player_element = PLAYERINFO(xx, yy)->initial_element;
13165
13166       CheckElementChangeBySide(x, y, center_element, player_element,
13167                                CE_TOUCHING_X, border_side);
13168     }
13169   }
13170 }
13171
13172 void TestIfElementHitsCustomElement(int x, int y, int direction)
13173 {
13174   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13175   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13176   int hitx = x + dx, hity = y + dy;
13177   int hitting_element = Feld[x][y];
13178   int touched_element;
13179
13180   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13181     return;
13182
13183   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13184                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13185
13186   if (IN_LEV_FIELD(hitx, hity))
13187   {
13188     int opposite_direction = MV_DIR_OPPOSITE(direction);
13189     int hitting_side = direction;
13190     int touched_side = opposite_direction;
13191     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13192                           MovDir[hitx][hity] != direction ||
13193                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13194
13195     object_hit = TRUE;
13196
13197     if (object_hit)
13198     {
13199       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13200                                CE_HITTING_X, touched_side);
13201
13202       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13203                                CE_HIT_BY_X, hitting_side);
13204
13205       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13206                                CE_HIT_BY_SOMETHING, opposite_direction);
13207
13208       if (IS_PLAYER(hitx, hity))
13209       {
13210         /* use player element that is initially defined in the level playfield,
13211            not the player element that corresponds to the runtime player number
13212            (example: a level that contains EL_PLAYER_3 as the only player would
13213            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13214         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13215
13216         CheckElementChangeBySide(x, y, hitting_element, player_element,
13217                                  CE_HITTING_X, touched_side);
13218       }
13219     }
13220   }
13221
13222   // "hitting something" is also true when hitting the playfield border
13223   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13224                            CE_HITTING_SOMETHING, direction);
13225 }
13226
13227 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13228 {
13229   int i, kill_x = -1, kill_y = -1;
13230
13231   int bad_element = -1;
13232   static int test_xy[4][2] =
13233   {
13234     { 0, -1 },
13235     { -1, 0 },
13236     { +1, 0 },
13237     { 0, +1 }
13238   };
13239   static int test_dir[4] =
13240   {
13241     MV_UP,
13242     MV_LEFT,
13243     MV_RIGHT,
13244     MV_DOWN
13245   };
13246
13247   for (i = 0; i < NUM_DIRECTIONS; i++)
13248   {
13249     int test_x, test_y, test_move_dir, test_element;
13250
13251     test_x = good_x + test_xy[i][0];
13252     test_y = good_y + test_xy[i][1];
13253
13254     if (!IN_LEV_FIELD(test_x, test_y))
13255       continue;
13256
13257     test_move_dir =
13258       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13259
13260     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13261
13262     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13263        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13264     */
13265     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13266         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13267     {
13268       kill_x = test_x;
13269       kill_y = test_y;
13270       bad_element = test_element;
13271
13272       break;
13273     }
13274   }
13275
13276   if (kill_x != -1 || kill_y != -1)
13277   {
13278     if (IS_PLAYER(good_x, good_y))
13279     {
13280       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13281
13282       if (player->shield_deadly_time_left > 0 &&
13283           !IS_INDESTRUCTIBLE(bad_element))
13284         Bang(kill_x, kill_y);
13285       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13286         KillPlayer(player);
13287     }
13288     else
13289       Bang(good_x, good_y);
13290   }
13291 }
13292
13293 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13294 {
13295   int i, kill_x = -1, kill_y = -1;
13296   int bad_element = Feld[bad_x][bad_y];
13297   static int test_xy[4][2] =
13298   {
13299     { 0, -1 },
13300     { -1, 0 },
13301     { +1, 0 },
13302     { 0, +1 }
13303   };
13304   static int touch_dir[4] =
13305   {
13306     MV_LEFT | MV_RIGHT,
13307     MV_UP   | MV_DOWN,
13308     MV_UP   | MV_DOWN,
13309     MV_LEFT | MV_RIGHT
13310   };
13311   static int test_dir[4] =
13312   {
13313     MV_UP,
13314     MV_LEFT,
13315     MV_RIGHT,
13316     MV_DOWN
13317   };
13318
13319   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13320     return;
13321
13322   for (i = 0; i < NUM_DIRECTIONS; i++)
13323   {
13324     int test_x, test_y, test_move_dir, test_element;
13325
13326     test_x = bad_x + test_xy[i][0];
13327     test_y = bad_y + test_xy[i][1];
13328
13329     if (!IN_LEV_FIELD(test_x, test_y))
13330       continue;
13331
13332     test_move_dir =
13333       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13334
13335     test_element = Feld[test_x][test_y];
13336
13337     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13338        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13339     */
13340     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13341         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13342     {
13343       // good thing is player or penguin that does not move away
13344       if (IS_PLAYER(test_x, test_y))
13345       {
13346         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13347
13348         if (bad_element == EL_ROBOT && player->is_moving)
13349           continue;     // robot does not kill player if he is moving
13350
13351         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13352         {
13353           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13354             continue;           // center and border element do not touch
13355         }
13356
13357         kill_x = test_x;
13358         kill_y = test_y;
13359
13360         break;
13361       }
13362       else if (test_element == EL_PENGUIN)
13363       {
13364         kill_x = test_x;
13365         kill_y = test_y;
13366
13367         break;
13368       }
13369     }
13370   }
13371
13372   if (kill_x != -1 || kill_y != -1)
13373   {
13374     if (IS_PLAYER(kill_x, kill_y))
13375     {
13376       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13377
13378       if (player->shield_deadly_time_left > 0 &&
13379           !IS_INDESTRUCTIBLE(bad_element))
13380         Bang(bad_x, bad_y);
13381       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13382         KillPlayer(player);
13383     }
13384     else
13385       Bang(kill_x, kill_y);
13386   }
13387 }
13388
13389 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13390 {
13391   int bad_element = Feld[bad_x][bad_y];
13392   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13393   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13394   int test_x = bad_x + dx, test_y = bad_y + dy;
13395   int test_move_dir, test_element;
13396   int kill_x = -1, kill_y = -1;
13397
13398   if (!IN_LEV_FIELD(test_x, test_y))
13399     return;
13400
13401   test_move_dir =
13402     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13403
13404   test_element = Feld[test_x][test_y];
13405
13406   if (test_move_dir != bad_move_dir)
13407   {
13408     // good thing can be player or penguin that does not move away
13409     if (IS_PLAYER(test_x, test_y))
13410     {
13411       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13412
13413       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13414          player as being hit when he is moving towards the bad thing, because
13415          the "get hit by" condition would be lost after the player stops) */
13416       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13417         return;         // player moves away from bad thing
13418
13419       kill_x = test_x;
13420       kill_y = test_y;
13421     }
13422     else if (test_element == EL_PENGUIN)
13423     {
13424       kill_x = test_x;
13425       kill_y = test_y;
13426     }
13427   }
13428
13429   if (kill_x != -1 || kill_y != -1)
13430   {
13431     if (IS_PLAYER(kill_x, kill_y))
13432     {
13433       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13434
13435       if (player->shield_deadly_time_left > 0 &&
13436           !IS_INDESTRUCTIBLE(bad_element))
13437         Bang(bad_x, bad_y);
13438       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13439         KillPlayer(player);
13440     }
13441     else
13442       Bang(kill_x, kill_y);
13443   }
13444 }
13445
13446 void TestIfPlayerTouchesBadThing(int x, int y)
13447 {
13448   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13449 }
13450
13451 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13452 {
13453   TestIfGoodThingHitsBadThing(x, y, move_dir);
13454 }
13455
13456 void TestIfBadThingTouchesPlayer(int x, int y)
13457 {
13458   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13459 }
13460
13461 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13462 {
13463   TestIfBadThingHitsGoodThing(x, y, move_dir);
13464 }
13465
13466 void TestIfFriendTouchesBadThing(int x, int y)
13467 {
13468   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13469 }
13470
13471 void TestIfBadThingTouchesFriend(int x, int y)
13472 {
13473   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13474 }
13475
13476 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13477 {
13478   int i, kill_x = bad_x, kill_y = bad_y;
13479   static int xy[4][2] =
13480   {
13481     { 0, -1 },
13482     { -1, 0 },
13483     { +1, 0 },
13484     { 0, +1 }
13485   };
13486
13487   for (i = 0; i < NUM_DIRECTIONS; i++)
13488   {
13489     int x, y, element;
13490
13491     x = bad_x + xy[i][0];
13492     y = bad_y + xy[i][1];
13493     if (!IN_LEV_FIELD(x, y))
13494       continue;
13495
13496     element = Feld[x][y];
13497     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13498         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13499     {
13500       kill_x = x;
13501       kill_y = y;
13502       break;
13503     }
13504   }
13505
13506   if (kill_x != bad_x || kill_y != bad_y)
13507     Bang(bad_x, bad_y);
13508 }
13509
13510 void KillPlayer(struct PlayerInfo *player)
13511 {
13512   int jx = player->jx, jy = player->jy;
13513
13514   if (!player->active)
13515     return;
13516
13517 #if 0
13518   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13519          player->killed, player->active, player->reanimated);
13520 #endif
13521
13522   /* the following code was introduced to prevent an infinite loop when calling
13523      -> Bang()
13524      -> CheckTriggeredElementChangeExt()
13525      -> ExecuteCustomElementAction()
13526      -> KillPlayer()
13527      -> (infinitely repeating the above sequence of function calls)
13528      which occurs when killing the player while having a CE with the setting
13529      "kill player X when explosion of <player X>"; the solution using a new
13530      field "player->killed" was chosen for backwards compatibility, although
13531      clever use of the fields "player->active" etc. would probably also work */
13532 #if 1
13533   if (player->killed)
13534     return;
13535 #endif
13536
13537   player->killed = TRUE;
13538
13539   // remove accessible field at the player's position
13540   Feld[jx][jy] = EL_EMPTY;
13541
13542   // deactivate shield (else Bang()/Explode() would not work right)
13543   player->shield_normal_time_left = 0;
13544   player->shield_deadly_time_left = 0;
13545
13546 #if 0
13547   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13548          player->killed, player->active, player->reanimated);
13549 #endif
13550
13551   Bang(jx, jy);
13552
13553 #if 0
13554   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13555          player->killed, player->active, player->reanimated);
13556 #endif
13557
13558   if (player->reanimated)       // killed player may have been reanimated
13559     player->killed = player->reanimated = FALSE;
13560   else
13561     BuryPlayer(player);
13562 }
13563
13564 static void KillPlayerUnlessEnemyProtected(int x, int y)
13565 {
13566   if (!PLAYER_ENEMY_PROTECTED(x, y))
13567     KillPlayer(PLAYERINFO(x, y));
13568 }
13569
13570 static void KillPlayerUnlessExplosionProtected(int x, int y)
13571 {
13572   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13573     KillPlayer(PLAYERINFO(x, y));
13574 }
13575
13576 void BuryPlayer(struct PlayerInfo *player)
13577 {
13578   int jx = player->jx, jy = player->jy;
13579
13580   if (!player->active)
13581     return;
13582
13583   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13584   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13585
13586   RemovePlayer(player);
13587
13588   player->buried = TRUE;
13589
13590   if (game.all_players_gone)
13591     game.GameOver = TRUE;
13592 }
13593
13594 void RemovePlayer(struct PlayerInfo *player)
13595 {
13596   int jx = player->jx, jy = player->jy;
13597   int i, found = FALSE;
13598
13599   player->present = FALSE;
13600   player->active = FALSE;
13601
13602   // required for some CE actions (even if the player is not active anymore)
13603   player->MovPos = 0;
13604
13605   if (!ExplodeField[jx][jy])
13606     StorePlayer[jx][jy] = 0;
13607
13608   if (player->is_moving)
13609     TEST_DrawLevelField(player->last_jx, player->last_jy);
13610
13611   for (i = 0; i < MAX_PLAYERS; i++)
13612     if (stored_player[i].active)
13613       found = TRUE;
13614
13615   if (!found)
13616   {
13617     game.all_players_gone = TRUE;
13618     game.GameOver = TRUE;
13619   }
13620
13621   game.exit_x = game.robot_wheel_x = jx;
13622   game.exit_y = game.robot_wheel_y = jy;
13623 }
13624
13625 void ExitPlayer(struct PlayerInfo *player)
13626 {
13627   DrawPlayer(player);   // needed here only to cleanup last field
13628   RemovePlayer(player);
13629
13630   if (game.players_still_needed > 0)
13631     game.players_still_needed--;
13632 }
13633
13634 static void setFieldForSnapping(int x, int y, int element, int direction)
13635 {
13636   struct ElementInfo *ei = &element_info[element];
13637   int direction_bit = MV_DIR_TO_BIT(direction);
13638   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13639   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13640                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13641
13642   Feld[x][y] = EL_ELEMENT_SNAPPING;
13643   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13644
13645   ResetGfxAnimation(x, y);
13646
13647   GfxElement[x][y] = element;
13648   GfxAction[x][y] = action;
13649   GfxDir[x][y] = direction;
13650   GfxFrame[x][y] = -1;
13651 }
13652
13653 /*
13654   =============================================================================
13655   checkDiagonalPushing()
13656   -----------------------------------------------------------------------------
13657   check if diagonal input device direction results in pushing of object
13658   (by checking if the alternative direction is walkable, diggable, ...)
13659   =============================================================================
13660 */
13661
13662 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13663                                     int x, int y, int real_dx, int real_dy)
13664 {
13665   int jx, jy, dx, dy, xx, yy;
13666
13667   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13668     return TRUE;
13669
13670   // diagonal direction: check alternative direction
13671   jx = player->jx;
13672   jy = player->jy;
13673   dx = x - jx;
13674   dy = y - jy;
13675   xx = jx + (dx == 0 ? real_dx : 0);
13676   yy = jy + (dy == 0 ? real_dy : 0);
13677
13678   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13679 }
13680
13681 /*
13682   =============================================================================
13683   DigField()
13684   -----------------------------------------------------------------------------
13685   x, y:                 field next to player (non-diagonal) to try to dig to
13686   real_dx, real_dy:     direction as read from input device (can be diagonal)
13687   =============================================================================
13688 */
13689
13690 static int DigField(struct PlayerInfo *player,
13691                     int oldx, int oldy, int x, int y,
13692                     int real_dx, int real_dy, int mode)
13693 {
13694   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13695   boolean player_was_pushing = player->is_pushing;
13696   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13697   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13698   int jx = oldx, jy = oldy;
13699   int dx = x - jx, dy = y - jy;
13700   int nextx = x + dx, nexty = y + dy;
13701   int move_direction = (dx == -1 ? MV_LEFT  :
13702                         dx == +1 ? MV_RIGHT :
13703                         dy == -1 ? MV_UP    :
13704                         dy == +1 ? MV_DOWN  : MV_NONE);
13705   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13706   int dig_side = MV_DIR_OPPOSITE(move_direction);
13707   int old_element = Feld[jx][jy];
13708   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13709   int collect_count;
13710
13711   if (is_player)                // function can also be called by EL_PENGUIN
13712   {
13713     if (player->MovPos == 0)
13714     {
13715       player->is_digging = FALSE;
13716       player->is_collecting = FALSE;
13717     }
13718
13719     if (player->MovPos == 0)    // last pushing move finished
13720       player->is_pushing = FALSE;
13721
13722     if (mode == DF_NO_PUSH)     // player just stopped pushing
13723     {
13724       player->is_switching = FALSE;
13725       player->push_delay = -1;
13726
13727       return MP_NO_ACTION;
13728     }
13729   }
13730
13731   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13732     old_element = Back[jx][jy];
13733
13734   // in case of element dropped at player position, check background
13735   else if (Back[jx][jy] != EL_EMPTY &&
13736            game.engine_version >= VERSION_IDENT(2,2,0,0))
13737     old_element = Back[jx][jy];
13738
13739   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13740     return MP_NO_ACTION;        // field has no opening in this direction
13741
13742   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13743     return MP_NO_ACTION;        // field has no opening in this direction
13744
13745   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13746   {
13747     SplashAcid(x, y);
13748
13749     Feld[jx][jy] = player->artwork_element;
13750     InitMovingField(jx, jy, MV_DOWN);
13751     Store[jx][jy] = EL_ACID;
13752     ContinueMoving(jx, jy);
13753     BuryPlayer(player);
13754
13755     return MP_DONT_RUN_INTO;
13756   }
13757
13758   if (player_can_move && DONT_RUN_INTO(element))
13759   {
13760     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13761
13762     return MP_DONT_RUN_INTO;
13763   }
13764
13765   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13766     return MP_NO_ACTION;
13767
13768   collect_count = element_info[element].collect_count_initial;
13769
13770   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13771     return MP_NO_ACTION;
13772
13773   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13774     player_can_move = player_can_move_or_snap;
13775
13776   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13777       game.engine_version >= VERSION_IDENT(2,2,0,0))
13778   {
13779     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13780                                player->index_bit, dig_side);
13781     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13782                                         player->index_bit, dig_side);
13783
13784     if (element == EL_DC_LANDMINE)
13785       Bang(x, y);
13786
13787     if (Feld[x][y] != element)          // field changed by snapping
13788       return MP_ACTION;
13789
13790     return MP_NO_ACTION;
13791   }
13792
13793   if (player->gravity && is_player && !player->is_auto_moving &&
13794       canFallDown(player) && move_direction != MV_DOWN &&
13795       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13796     return MP_NO_ACTION;        // player cannot walk here due to gravity
13797
13798   if (player_can_move &&
13799       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13800   {
13801     int sound_element = SND_ELEMENT(element);
13802     int sound_action = ACTION_WALKING;
13803
13804     if (IS_RND_GATE(element))
13805     {
13806       if (!player->key[RND_GATE_NR(element)])
13807         return MP_NO_ACTION;
13808     }
13809     else if (IS_RND_GATE_GRAY(element))
13810     {
13811       if (!player->key[RND_GATE_GRAY_NR(element)])
13812         return MP_NO_ACTION;
13813     }
13814     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13815     {
13816       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13817         return MP_NO_ACTION;
13818     }
13819     else if (element == EL_EXIT_OPEN ||
13820              element == EL_EM_EXIT_OPEN ||
13821              element == EL_EM_EXIT_OPENING ||
13822              element == EL_STEEL_EXIT_OPEN ||
13823              element == EL_EM_STEEL_EXIT_OPEN ||
13824              element == EL_EM_STEEL_EXIT_OPENING ||
13825              element == EL_SP_EXIT_OPEN ||
13826              element == EL_SP_EXIT_OPENING)
13827     {
13828       sound_action = ACTION_PASSING;    // player is passing exit
13829     }
13830     else if (element == EL_EMPTY)
13831     {
13832       sound_action = ACTION_MOVING;             // nothing to walk on
13833     }
13834
13835     // play sound from background or player, whatever is available
13836     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13837       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13838     else
13839       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13840   }
13841   else if (player_can_move &&
13842            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13843   {
13844     if (!ACCESS_FROM(element, opposite_direction))
13845       return MP_NO_ACTION;      // field not accessible from this direction
13846
13847     if (CAN_MOVE(element))      // only fixed elements can be passed!
13848       return MP_NO_ACTION;
13849
13850     if (IS_EM_GATE(element))
13851     {
13852       if (!player->key[EM_GATE_NR(element)])
13853         return MP_NO_ACTION;
13854     }
13855     else if (IS_EM_GATE_GRAY(element))
13856     {
13857       if (!player->key[EM_GATE_GRAY_NR(element)])
13858         return MP_NO_ACTION;
13859     }
13860     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13861     {
13862       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13863         return MP_NO_ACTION;
13864     }
13865     else if (IS_EMC_GATE(element))
13866     {
13867       if (!player->key[EMC_GATE_NR(element)])
13868         return MP_NO_ACTION;
13869     }
13870     else if (IS_EMC_GATE_GRAY(element))
13871     {
13872       if (!player->key[EMC_GATE_GRAY_NR(element)])
13873         return MP_NO_ACTION;
13874     }
13875     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13876     {
13877       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13878         return MP_NO_ACTION;
13879     }
13880     else if (element == EL_DC_GATE_WHITE ||
13881              element == EL_DC_GATE_WHITE_GRAY ||
13882              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13883     {
13884       if (player->num_white_keys == 0)
13885         return MP_NO_ACTION;
13886
13887       player->num_white_keys--;
13888     }
13889     else if (IS_SP_PORT(element))
13890     {
13891       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13892           element == EL_SP_GRAVITY_PORT_RIGHT ||
13893           element == EL_SP_GRAVITY_PORT_UP ||
13894           element == EL_SP_GRAVITY_PORT_DOWN)
13895         player->gravity = !player->gravity;
13896       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13897                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13898                element == EL_SP_GRAVITY_ON_PORT_UP ||
13899                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13900         player->gravity = TRUE;
13901       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13902                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13903                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13904                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13905         player->gravity = FALSE;
13906     }
13907
13908     // automatically move to the next field with double speed
13909     player->programmed_action = move_direction;
13910
13911     if (player->move_delay_reset_counter == 0)
13912     {
13913       player->move_delay_reset_counter = 2;     // two double speed steps
13914
13915       DOUBLE_PLAYER_SPEED(player);
13916     }
13917
13918     PlayLevelSoundAction(x, y, ACTION_PASSING);
13919   }
13920   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13921   {
13922     RemoveField(x, y);
13923
13924     if (mode != DF_SNAP)
13925     {
13926       GfxElement[x][y] = GFX_ELEMENT(element);
13927       player->is_digging = TRUE;
13928     }
13929
13930     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13931
13932     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13933                                         player->index_bit, dig_side);
13934
13935     if (mode == DF_SNAP)
13936     {
13937       if (level.block_snap_field)
13938         setFieldForSnapping(x, y, element, move_direction);
13939       else
13940         TestIfElementTouchesCustomElement(x, y);        // for empty space
13941
13942       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13943                                           player->index_bit, dig_side);
13944     }
13945   }
13946   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13947   {
13948     RemoveField(x, y);
13949
13950     if (is_player && mode != DF_SNAP)
13951     {
13952       GfxElement[x][y] = element;
13953       player->is_collecting = TRUE;
13954     }
13955
13956     if (element == EL_SPEED_PILL)
13957     {
13958       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13959     }
13960     else if (element == EL_EXTRA_TIME && level.time > 0)
13961     {
13962       TimeLeft += level.extra_time;
13963
13964       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13965
13966       DisplayGameControlValues();
13967     }
13968     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13969     {
13970       player->shield_normal_time_left += level.shield_normal_time;
13971       if (element == EL_SHIELD_DEADLY)
13972         player->shield_deadly_time_left += level.shield_deadly_time;
13973     }
13974     else if (element == EL_DYNAMITE ||
13975              element == EL_EM_DYNAMITE ||
13976              element == EL_SP_DISK_RED)
13977     {
13978       if (player->inventory_size < MAX_INVENTORY_SIZE)
13979         player->inventory_element[player->inventory_size++] = element;
13980
13981       DrawGameDoorValues();
13982     }
13983     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13984     {
13985       player->dynabomb_count++;
13986       player->dynabombs_left++;
13987     }
13988     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13989     {
13990       player->dynabomb_size++;
13991     }
13992     else if (element == EL_DYNABOMB_INCREASE_POWER)
13993     {
13994       player->dynabomb_xl = TRUE;
13995     }
13996     else if (IS_KEY(element))
13997     {
13998       player->key[KEY_NR(element)] = TRUE;
13999
14000       DrawGameDoorValues();
14001     }
14002     else if (element == EL_DC_KEY_WHITE)
14003     {
14004       player->num_white_keys++;
14005
14006       // display white keys?
14007       // DrawGameDoorValues();
14008     }
14009     else if (IS_ENVELOPE(element))
14010     {
14011       player->show_envelope = element;
14012     }
14013     else if (element == EL_EMC_LENSES)
14014     {
14015       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14016
14017       RedrawAllInvisibleElementsForLenses();
14018     }
14019     else if (element == EL_EMC_MAGNIFIER)
14020     {
14021       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14022
14023       RedrawAllInvisibleElementsForMagnifier();
14024     }
14025     else if (IS_DROPPABLE(element) ||
14026              IS_THROWABLE(element))     // can be collected and dropped
14027     {
14028       int i;
14029
14030       if (collect_count == 0)
14031         player->inventory_infinite_element = element;
14032       else
14033         for (i = 0; i < collect_count; i++)
14034           if (player->inventory_size < MAX_INVENTORY_SIZE)
14035             player->inventory_element[player->inventory_size++] = element;
14036
14037       DrawGameDoorValues();
14038     }
14039     else if (collect_count > 0)
14040     {
14041       game.gems_still_needed -= collect_count;
14042       if (game.gems_still_needed < 0)
14043         game.gems_still_needed = 0;
14044
14045       game.snapshot.collected_item = TRUE;
14046
14047       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14048
14049       DisplayGameControlValues();
14050     }
14051
14052     RaiseScoreElement(element);
14053     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14054
14055     if (is_player)
14056       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14057                                           player->index_bit, dig_side);
14058
14059     if (mode == DF_SNAP)
14060     {
14061       if (level.block_snap_field)
14062         setFieldForSnapping(x, y, element, move_direction);
14063       else
14064         TestIfElementTouchesCustomElement(x, y);        // for empty space
14065
14066       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14067                                           player->index_bit, dig_side);
14068     }
14069   }
14070   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14071   {
14072     if (mode == DF_SNAP && element != EL_BD_ROCK)
14073       return MP_NO_ACTION;
14074
14075     if (CAN_FALL(element) && dy)
14076       return MP_NO_ACTION;
14077
14078     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14079         !(element == EL_SPRING && level.use_spring_bug))
14080       return MP_NO_ACTION;
14081
14082     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14083         ((move_direction & MV_VERTICAL &&
14084           ((element_info[element].move_pattern & MV_LEFT &&
14085             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14086            (element_info[element].move_pattern & MV_RIGHT &&
14087             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14088          (move_direction & MV_HORIZONTAL &&
14089           ((element_info[element].move_pattern & MV_UP &&
14090             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14091            (element_info[element].move_pattern & MV_DOWN &&
14092             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14093       return MP_NO_ACTION;
14094
14095     // do not push elements already moving away faster than player
14096     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14097         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14098       return MP_NO_ACTION;
14099
14100     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14101     {
14102       if (player->push_delay_value == -1 || !player_was_pushing)
14103         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14104     }
14105     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14106     {
14107       if (player->push_delay_value == -1)
14108         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14109     }
14110     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14111     {
14112       if (!player->is_pushing)
14113         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14114     }
14115
14116     player->is_pushing = TRUE;
14117     player->is_active = TRUE;
14118
14119     if (!(IN_LEV_FIELD(nextx, nexty) &&
14120           (IS_FREE(nextx, nexty) ||
14121            (IS_SB_ELEMENT(element) &&
14122             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14123            (IS_CUSTOM_ELEMENT(element) &&
14124             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14125       return MP_NO_ACTION;
14126
14127     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14128       return MP_NO_ACTION;
14129
14130     if (player->push_delay == -1)       // new pushing; restart delay
14131       player->push_delay = 0;
14132
14133     if (player->push_delay < player->push_delay_value &&
14134         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14135         element != EL_SPRING && element != EL_BALLOON)
14136     {
14137       // make sure that there is no move delay before next try to push
14138       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14139         player->move_delay = 0;
14140
14141       return MP_NO_ACTION;
14142     }
14143
14144     if (IS_CUSTOM_ELEMENT(element) &&
14145         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14146     {
14147       if (!DigFieldByCE(nextx, nexty, element))
14148         return MP_NO_ACTION;
14149     }
14150
14151     if (IS_SB_ELEMENT(element))
14152     {
14153       boolean sokoban_task_solved = FALSE;
14154
14155       if (element == EL_SOKOBAN_FIELD_FULL)
14156       {
14157         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14158
14159         IncrementSokobanFieldsNeeded();
14160         IncrementSokobanObjectsNeeded();
14161       }
14162
14163       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14164       {
14165         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14166
14167         DecrementSokobanFieldsNeeded();
14168         DecrementSokobanObjectsNeeded();
14169
14170         // sokoban object was pushed from empty field to sokoban field
14171         if (Back[x][y] == EL_EMPTY)
14172           sokoban_task_solved = TRUE;
14173       }
14174
14175       Feld[x][y] = EL_SOKOBAN_OBJECT;
14176
14177       if (Back[x][y] == Back[nextx][nexty])
14178         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14179       else if (Back[x][y] != 0)
14180         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14181                                     ACTION_EMPTYING);
14182       else
14183         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14184                                     ACTION_FILLING);
14185
14186       if (sokoban_task_solved &&
14187           game.sokoban_fields_still_needed == 0 &&
14188           game.sokoban_objects_still_needed == 0 &&
14189           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14190       {
14191         game.players_still_needed = 0;
14192
14193         LevelSolved();
14194
14195         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14196       }
14197     }
14198     else
14199       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14200
14201     InitMovingField(x, y, move_direction);
14202     GfxAction[x][y] = ACTION_PUSHING;
14203
14204     if (mode == DF_SNAP)
14205       ContinueMoving(x, y);
14206     else
14207       MovPos[x][y] = (dx != 0 ? dx : dy);
14208
14209     Pushed[x][y] = TRUE;
14210     Pushed[nextx][nexty] = TRUE;
14211
14212     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14213       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14214     else
14215       player->push_delay_value = -1;    // get new value later
14216
14217     // check for element change _after_ element has been pushed
14218     if (game.use_change_when_pushing_bug)
14219     {
14220       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14221                                  player->index_bit, dig_side);
14222       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14223                                           player->index_bit, dig_side);
14224     }
14225   }
14226   else if (IS_SWITCHABLE(element))
14227   {
14228     if (PLAYER_SWITCHING(player, x, y))
14229     {
14230       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14231                                           player->index_bit, dig_side);
14232
14233       return MP_ACTION;
14234     }
14235
14236     player->is_switching = TRUE;
14237     player->switch_x = x;
14238     player->switch_y = y;
14239
14240     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14241
14242     if (element == EL_ROBOT_WHEEL)
14243     {
14244       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14245
14246       game.robot_wheel_x = x;
14247       game.robot_wheel_y = y;
14248       game.robot_wheel_active = TRUE;
14249
14250       TEST_DrawLevelField(x, y);
14251     }
14252     else if (element == EL_SP_TERMINAL)
14253     {
14254       int xx, yy;
14255
14256       SCAN_PLAYFIELD(xx, yy)
14257       {
14258         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14259         {
14260           Bang(xx, yy);
14261         }
14262         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14263         {
14264           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14265
14266           ResetGfxAnimation(xx, yy);
14267           TEST_DrawLevelField(xx, yy);
14268         }
14269       }
14270     }
14271     else if (IS_BELT_SWITCH(element))
14272     {
14273       ToggleBeltSwitch(x, y);
14274     }
14275     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14276              element == EL_SWITCHGATE_SWITCH_DOWN ||
14277              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14278              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14279     {
14280       ToggleSwitchgateSwitch(x, y);
14281     }
14282     else if (element == EL_LIGHT_SWITCH ||
14283              element == EL_LIGHT_SWITCH_ACTIVE)
14284     {
14285       ToggleLightSwitch(x, y);
14286     }
14287     else if (element == EL_TIMEGATE_SWITCH ||
14288              element == EL_DC_TIMEGATE_SWITCH)
14289     {
14290       ActivateTimegateSwitch(x, y);
14291     }
14292     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14293              element == EL_BALLOON_SWITCH_RIGHT ||
14294              element == EL_BALLOON_SWITCH_UP    ||
14295              element == EL_BALLOON_SWITCH_DOWN  ||
14296              element == EL_BALLOON_SWITCH_NONE  ||
14297              element == EL_BALLOON_SWITCH_ANY)
14298     {
14299       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14300                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14301                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14302                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14303                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14304                              move_direction);
14305     }
14306     else if (element == EL_LAMP)
14307     {
14308       Feld[x][y] = EL_LAMP_ACTIVE;
14309       game.lights_still_needed--;
14310
14311       ResetGfxAnimation(x, y);
14312       TEST_DrawLevelField(x, y);
14313     }
14314     else if (element == EL_TIME_ORB_FULL)
14315     {
14316       Feld[x][y] = EL_TIME_ORB_EMPTY;
14317
14318       if (level.time > 0 || level.use_time_orb_bug)
14319       {
14320         TimeLeft += level.time_orb_time;
14321         game.no_time_limit = FALSE;
14322
14323         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14324
14325         DisplayGameControlValues();
14326       }
14327
14328       ResetGfxAnimation(x, y);
14329       TEST_DrawLevelField(x, y);
14330     }
14331     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14332              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14333     {
14334       int xx, yy;
14335
14336       game.ball_active = !game.ball_active;
14337
14338       SCAN_PLAYFIELD(xx, yy)
14339       {
14340         int e = Feld[xx][yy];
14341
14342         if (game.ball_active)
14343         {
14344           if (e == EL_EMC_MAGIC_BALL)
14345             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14346           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14347             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14348         }
14349         else
14350         {
14351           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14352             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14353           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14354             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14355         }
14356       }
14357     }
14358
14359     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14360                                         player->index_bit, dig_side);
14361
14362     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14363                                         player->index_bit, dig_side);
14364
14365     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14366                                         player->index_bit, dig_side);
14367
14368     return MP_ACTION;
14369   }
14370   else
14371   {
14372     if (!PLAYER_SWITCHING(player, x, y))
14373     {
14374       player->is_switching = TRUE;
14375       player->switch_x = x;
14376       player->switch_y = y;
14377
14378       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14379                                  player->index_bit, dig_side);
14380       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14381                                           player->index_bit, dig_side);
14382
14383       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14384                                  player->index_bit, dig_side);
14385       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14386                                           player->index_bit, dig_side);
14387     }
14388
14389     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14390                                player->index_bit, dig_side);
14391     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14392                                         player->index_bit, dig_side);
14393
14394     return MP_NO_ACTION;
14395   }
14396
14397   player->push_delay = -1;
14398
14399   if (is_player)                // function can also be called by EL_PENGUIN
14400   {
14401     if (Feld[x][y] != element)          // really digged/collected something
14402     {
14403       player->is_collecting = !player->is_digging;
14404       player->is_active = TRUE;
14405     }
14406   }
14407
14408   return MP_MOVING;
14409 }
14410
14411 static boolean DigFieldByCE(int x, int y, int digging_element)
14412 {
14413   int element = Feld[x][y];
14414
14415   if (!IS_FREE(x, y))
14416   {
14417     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14418                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14419                   ACTION_BREAKING);
14420
14421     // no element can dig solid indestructible elements
14422     if (IS_INDESTRUCTIBLE(element) &&
14423         !IS_DIGGABLE(element) &&
14424         !IS_COLLECTIBLE(element))
14425       return FALSE;
14426
14427     if (AmoebaNr[x][y] &&
14428         (element == EL_AMOEBA_FULL ||
14429          element == EL_BD_AMOEBA ||
14430          element == EL_AMOEBA_GROWING))
14431     {
14432       AmoebaCnt[AmoebaNr[x][y]]--;
14433       AmoebaCnt2[AmoebaNr[x][y]]--;
14434     }
14435
14436     if (IS_MOVING(x, y))
14437       RemoveMovingField(x, y);
14438     else
14439     {
14440       RemoveField(x, y);
14441       TEST_DrawLevelField(x, y);
14442     }
14443
14444     // if digged element was about to explode, prevent the explosion
14445     ExplodeField[x][y] = EX_TYPE_NONE;
14446
14447     PlayLevelSoundAction(x, y, action);
14448   }
14449
14450   Store[x][y] = EL_EMPTY;
14451
14452   // this makes it possible to leave the removed element again
14453   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14454     Store[x][y] = element;
14455
14456   return TRUE;
14457 }
14458
14459 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14460 {
14461   int jx = player->jx, jy = player->jy;
14462   int x = jx + dx, y = jy + dy;
14463   int snap_direction = (dx == -1 ? MV_LEFT  :
14464                         dx == +1 ? MV_RIGHT :
14465                         dy == -1 ? MV_UP    :
14466                         dy == +1 ? MV_DOWN  : MV_NONE);
14467   boolean can_continue_snapping = (level.continuous_snapping &&
14468                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14469
14470   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14471     return FALSE;
14472
14473   if (!player->active || !IN_LEV_FIELD(x, y))
14474     return FALSE;
14475
14476   if (dx && dy)
14477     return FALSE;
14478
14479   if (!dx && !dy)
14480   {
14481     if (player->MovPos == 0)
14482       player->is_pushing = FALSE;
14483
14484     player->is_snapping = FALSE;
14485
14486     if (player->MovPos == 0)
14487     {
14488       player->is_moving = FALSE;
14489       player->is_digging = FALSE;
14490       player->is_collecting = FALSE;
14491     }
14492
14493     return FALSE;
14494   }
14495
14496   // prevent snapping with already pressed snap key when not allowed
14497   if (player->is_snapping && !can_continue_snapping)
14498     return FALSE;
14499
14500   player->MovDir = snap_direction;
14501
14502   if (player->MovPos == 0)
14503   {
14504     player->is_moving = FALSE;
14505     player->is_digging = FALSE;
14506     player->is_collecting = FALSE;
14507   }
14508
14509   player->is_dropping = FALSE;
14510   player->is_dropping_pressed = FALSE;
14511   player->drop_pressed_delay = 0;
14512
14513   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14514     return FALSE;
14515
14516   player->is_snapping = TRUE;
14517   player->is_active = TRUE;
14518
14519   if (player->MovPos == 0)
14520   {
14521     player->is_moving = FALSE;
14522     player->is_digging = FALSE;
14523     player->is_collecting = FALSE;
14524   }
14525
14526   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14527     TEST_DrawLevelField(player->last_jx, player->last_jy);
14528
14529   TEST_DrawLevelField(x, y);
14530
14531   return TRUE;
14532 }
14533
14534 static boolean DropElement(struct PlayerInfo *player)
14535 {
14536   int old_element, new_element;
14537   int dropx = player->jx, dropy = player->jy;
14538   int drop_direction = player->MovDir;
14539   int drop_side = drop_direction;
14540   int drop_element = get_next_dropped_element(player);
14541
14542   /* do not drop an element on top of another element; when holding drop key
14543      pressed without moving, dropped element must move away before the next
14544      element can be dropped (this is especially important if the next element
14545      is dynamite, which can be placed on background for historical reasons) */
14546   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14547     return MP_ACTION;
14548
14549   if (IS_THROWABLE(drop_element))
14550   {
14551     dropx += GET_DX_FROM_DIR(drop_direction);
14552     dropy += GET_DY_FROM_DIR(drop_direction);
14553
14554     if (!IN_LEV_FIELD(dropx, dropy))
14555       return FALSE;
14556   }
14557
14558   old_element = Feld[dropx][dropy];     // old element at dropping position
14559   new_element = drop_element;           // default: no change when dropping
14560
14561   // check if player is active, not moving and ready to drop
14562   if (!player->active || player->MovPos || player->drop_delay > 0)
14563     return FALSE;
14564
14565   // check if player has anything that can be dropped
14566   if (new_element == EL_UNDEFINED)
14567     return FALSE;
14568
14569   // only set if player has anything that can be dropped
14570   player->is_dropping_pressed = TRUE;
14571
14572   // check if drop key was pressed long enough for EM style dynamite
14573   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14574     return FALSE;
14575
14576   // check if anything can be dropped at the current position
14577   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14578     return FALSE;
14579
14580   // collected custom elements can only be dropped on empty fields
14581   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14582     return FALSE;
14583
14584   if (old_element != EL_EMPTY)
14585     Back[dropx][dropy] = old_element;   // store old element on this field
14586
14587   ResetGfxAnimation(dropx, dropy);
14588   ResetRandomAnimationValue(dropx, dropy);
14589
14590   if (player->inventory_size > 0 ||
14591       player->inventory_infinite_element != EL_UNDEFINED)
14592   {
14593     if (player->inventory_size > 0)
14594     {
14595       player->inventory_size--;
14596
14597       DrawGameDoorValues();
14598
14599       if (new_element == EL_DYNAMITE)
14600         new_element = EL_DYNAMITE_ACTIVE;
14601       else if (new_element == EL_EM_DYNAMITE)
14602         new_element = EL_EM_DYNAMITE_ACTIVE;
14603       else if (new_element == EL_SP_DISK_RED)
14604         new_element = EL_SP_DISK_RED_ACTIVE;
14605     }
14606
14607     Feld[dropx][dropy] = new_element;
14608
14609     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14610       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14611                           el2img(Feld[dropx][dropy]), 0);
14612
14613     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14614
14615     // needed if previous element just changed to "empty" in the last frame
14616     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14617
14618     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14619                                player->index_bit, drop_side);
14620     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14621                                         CE_PLAYER_DROPS_X,
14622                                         player->index_bit, drop_side);
14623
14624     TestIfElementTouchesCustomElement(dropx, dropy);
14625   }
14626   else          // player is dropping a dyna bomb
14627   {
14628     player->dynabombs_left--;
14629
14630     Feld[dropx][dropy] = new_element;
14631
14632     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14633       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14634                           el2img(Feld[dropx][dropy]), 0);
14635
14636     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14637   }
14638
14639   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14640     InitField_WithBug1(dropx, dropy, FALSE);
14641
14642   new_element = Feld[dropx][dropy];     // element might have changed
14643
14644   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14645       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14646   {
14647     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14648       MovDir[dropx][dropy] = drop_direction;
14649
14650     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14651
14652     // do not cause impact style collision by dropping elements that can fall
14653     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14654   }
14655
14656   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14657   player->is_dropping = TRUE;
14658
14659   player->drop_pressed_delay = 0;
14660   player->is_dropping_pressed = FALSE;
14661
14662   player->drop_x = dropx;
14663   player->drop_y = dropy;
14664
14665   return TRUE;
14666 }
14667
14668 // ----------------------------------------------------------------------------
14669 // game sound playing functions
14670 // ----------------------------------------------------------------------------
14671
14672 static int *loop_sound_frame = NULL;
14673 static int *loop_sound_volume = NULL;
14674
14675 void InitPlayLevelSound(void)
14676 {
14677   int num_sounds = getSoundListSize();
14678
14679   checked_free(loop_sound_frame);
14680   checked_free(loop_sound_volume);
14681
14682   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14683   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14684 }
14685
14686 static void PlayLevelSound(int x, int y, int nr)
14687 {
14688   int sx = SCREENX(x), sy = SCREENY(y);
14689   int volume, stereo_position;
14690   int max_distance = 8;
14691   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14692
14693   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14694       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14695     return;
14696
14697   if (!IN_LEV_FIELD(x, y) ||
14698       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14699       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14700     return;
14701
14702   volume = SOUND_MAX_VOLUME;
14703
14704   if (!IN_SCR_FIELD(sx, sy))
14705   {
14706     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14707     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14708
14709     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14710   }
14711
14712   stereo_position = (SOUND_MAX_LEFT +
14713                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14714                      (SCR_FIELDX + 2 * max_distance));
14715
14716   if (IS_LOOP_SOUND(nr))
14717   {
14718     /* This assures that quieter loop sounds do not overwrite louder ones,
14719        while restarting sound volume comparison with each new game frame. */
14720
14721     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14722       return;
14723
14724     loop_sound_volume[nr] = volume;
14725     loop_sound_frame[nr] = FrameCounter;
14726   }
14727
14728   PlaySoundExt(nr, volume, stereo_position, type);
14729 }
14730
14731 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14732 {
14733   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14734                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14735                  y < LEVELY(BY1) ? LEVELY(BY1) :
14736                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14737                  sound_action);
14738 }
14739
14740 static void PlayLevelSoundAction(int x, int y, int action)
14741 {
14742   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14743 }
14744
14745 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14746 {
14747   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14748
14749   if (sound_effect != SND_UNDEFINED)
14750     PlayLevelSound(x, y, sound_effect);
14751 }
14752
14753 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14754                                               int action)
14755 {
14756   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14757
14758   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14759     PlayLevelSound(x, y, sound_effect);
14760 }
14761
14762 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14763 {
14764   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14765
14766   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14767     PlayLevelSound(x, y, sound_effect);
14768 }
14769
14770 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14771 {
14772   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14773
14774   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14775     StopSound(sound_effect);
14776 }
14777
14778 static int getLevelMusicNr(void)
14779 {
14780   if (levelset.music[level_nr] != MUS_UNDEFINED)
14781     return levelset.music[level_nr];            // from config file
14782   else
14783     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14784 }
14785
14786 static void FadeLevelSounds(void)
14787 {
14788   FadeSounds();
14789 }
14790
14791 static void FadeLevelMusic(void)
14792 {
14793   int music_nr = getLevelMusicNr();
14794   char *curr_music = getCurrentlyPlayingMusicFilename();
14795   char *next_music = getMusicInfoEntryFilename(music_nr);
14796
14797   if (!strEqual(curr_music, next_music))
14798     FadeMusic();
14799 }
14800
14801 void FadeLevelSoundsAndMusic(void)
14802 {
14803   FadeLevelSounds();
14804   FadeLevelMusic();
14805 }
14806
14807 static void PlayLevelMusic(void)
14808 {
14809   int music_nr = getLevelMusicNr();
14810   char *curr_music = getCurrentlyPlayingMusicFilename();
14811   char *next_music = getMusicInfoEntryFilename(music_nr);
14812
14813   if (!strEqual(curr_music, next_music))
14814     PlayMusicLoop(music_nr);
14815 }
14816
14817 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14818 {
14819   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14820   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14821   int x = xx - 1 - offset;
14822   int y = yy - 1 - offset;
14823
14824   switch (sample)
14825   {
14826     case SOUND_blank:
14827       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14828       break;
14829
14830     case SOUND_roll:
14831       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14832       break;
14833
14834     case SOUND_stone:
14835       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14836       break;
14837
14838     case SOUND_nut:
14839       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14840       break;
14841
14842     case SOUND_crack:
14843       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14844       break;
14845
14846     case SOUND_bug:
14847       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14848       break;
14849
14850     case SOUND_tank:
14851       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14852       break;
14853
14854     case SOUND_android_clone:
14855       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14856       break;
14857
14858     case SOUND_android_move:
14859       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14860       break;
14861
14862     case SOUND_spring:
14863       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14864       break;
14865
14866     case SOUND_slurp:
14867       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14868       break;
14869
14870     case SOUND_eater:
14871       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14872       break;
14873
14874     case SOUND_eater_eat:
14875       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14876       break;
14877
14878     case SOUND_alien:
14879       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14880       break;
14881
14882     case SOUND_collect:
14883       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14884       break;
14885
14886     case SOUND_diamond:
14887       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14888       break;
14889
14890     case SOUND_squash:
14891       // !!! CHECK THIS !!!
14892 #if 1
14893       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14894 #else
14895       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14896 #endif
14897       break;
14898
14899     case SOUND_wonderfall:
14900       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14901       break;
14902
14903     case SOUND_drip:
14904       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14905       break;
14906
14907     case SOUND_push:
14908       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14909       break;
14910
14911     case SOUND_dirt:
14912       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14913       break;
14914
14915     case SOUND_acid:
14916       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14917       break;
14918
14919     case SOUND_ball:
14920       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14921       break;
14922
14923     case SOUND_slide:
14924       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14925       break;
14926
14927     case SOUND_wonder:
14928       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14929       break;
14930
14931     case SOUND_door:
14932       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14933       break;
14934
14935     case SOUND_exit_open:
14936       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14937       break;
14938
14939     case SOUND_exit_leave:
14940       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14941       break;
14942
14943     case SOUND_dynamite:
14944       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14945       break;
14946
14947     case SOUND_tick:
14948       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14949       break;
14950
14951     case SOUND_press:
14952       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14953       break;
14954
14955     case SOUND_wheel:
14956       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14957       break;
14958
14959     case SOUND_boom:
14960       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14961       break;
14962
14963     case SOUND_die:
14964       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14965       break;
14966
14967     case SOUND_time:
14968       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14969       break;
14970
14971     default:
14972       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14973       break;
14974   }
14975 }
14976
14977 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14978 {
14979   int element = map_element_SP_to_RND(element_sp);
14980   int action = map_action_SP_to_RND(action_sp);
14981   int offset = (setup.sp_show_border_elements ? 0 : 1);
14982   int x = xx - offset;
14983   int y = yy - offset;
14984
14985   PlayLevelSoundElementAction(x, y, element, action);
14986 }
14987
14988 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14989 {
14990   int element = map_element_MM_to_RND(element_mm);
14991   int action = map_action_MM_to_RND(action_mm);
14992   int offset = 0;
14993   int x = xx - offset;
14994   int y = yy - offset;
14995
14996   if (!IS_MM_ELEMENT(element))
14997     element = EL_MM_DEFAULT;
14998
14999   PlayLevelSoundElementAction(x, y, element, action);
15000 }
15001
15002 void PlaySound_MM(int sound_mm)
15003 {
15004   int sound = map_sound_MM_to_RND(sound_mm);
15005
15006   if (sound == SND_UNDEFINED)
15007     return;
15008
15009   PlaySound(sound);
15010 }
15011
15012 void PlaySoundLoop_MM(int sound_mm)
15013 {
15014   int sound = map_sound_MM_to_RND(sound_mm);
15015
15016   if (sound == SND_UNDEFINED)
15017     return;
15018
15019   PlaySoundLoop(sound);
15020 }
15021
15022 void StopSound_MM(int sound_mm)
15023 {
15024   int sound = map_sound_MM_to_RND(sound_mm);
15025
15026   if (sound == SND_UNDEFINED)
15027     return;
15028
15029   StopSound(sound);
15030 }
15031
15032 void RaiseScore(int value)
15033 {
15034   game.score += value;
15035
15036   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15037
15038   DisplayGameControlValues();
15039 }
15040
15041 void RaiseScoreElement(int element)
15042 {
15043   switch (element)
15044   {
15045     case EL_EMERALD:
15046     case EL_BD_DIAMOND:
15047     case EL_EMERALD_YELLOW:
15048     case EL_EMERALD_RED:
15049     case EL_EMERALD_PURPLE:
15050     case EL_SP_INFOTRON:
15051       RaiseScore(level.score[SC_EMERALD]);
15052       break;
15053     case EL_DIAMOND:
15054       RaiseScore(level.score[SC_DIAMOND]);
15055       break;
15056     case EL_CRYSTAL:
15057       RaiseScore(level.score[SC_CRYSTAL]);
15058       break;
15059     case EL_PEARL:
15060       RaiseScore(level.score[SC_PEARL]);
15061       break;
15062     case EL_BUG:
15063     case EL_BD_BUTTERFLY:
15064     case EL_SP_ELECTRON:
15065       RaiseScore(level.score[SC_BUG]);
15066       break;
15067     case EL_SPACESHIP:
15068     case EL_BD_FIREFLY:
15069     case EL_SP_SNIKSNAK:
15070       RaiseScore(level.score[SC_SPACESHIP]);
15071       break;
15072     case EL_YAMYAM:
15073     case EL_DARK_YAMYAM:
15074       RaiseScore(level.score[SC_YAMYAM]);
15075       break;
15076     case EL_ROBOT:
15077       RaiseScore(level.score[SC_ROBOT]);
15078       break;
15079     case EL_PACMAN:
15080       RaiseScore(level.score[SC_PACMAN]);
15081       break;
15082     case EL_NUT:
15083       RaiseScore(level.score[SC_NUT]);
15084       break;
15085     case EL_DYNAMITE:
15086     case EL_EM_DYNAMITE:
15087     case EL_SP_DISK_RED:
15088     case EL_DYNABOMB_INCREASE_NUMBER:
15089     case EL_DYNABOMB_INCREASE_SIZE:
15090     case EL_DYNABOMB_INCREASE_POWER:
15091       RaiseScore(level.score[SC_DYNAMITE]);
15092       break;
15093     case EL_SHIELD_NORMAL:
15094     case EL_SHIELD_DEADLY:
15095       RaiseScore(level.score[SC_SHIELD]);
15096       break;
15097     case EL_EXTRA_TIME:
15098       RaiseScore(level.extra_time_score);
15099       break;
15100     case EL_KEY_1:
15101     case EL_KEY_2:
15102     case EL_KEY_3:
15103     case EL_KEY_4:
15104     case EL_EM_KEY_1:
15105     case EL_EM_KEY_2:
15106     case EL_EM_KEY_3:
15107     case EL_EM_KEY_4:
15108     case EL_EMC_KEY_5:
15109     case EL_EMC_KEY_6:
15110     case EL_EMC_KEY_7:
15111     case EL_EMC_KEY_8:
15112     case EL_DC_KEY_WHITE:
15113       RaiseScore(level.score[SC_KEY]);
15114       break;
15115     default:
15116       RaiseScore(element_info[element].collect_score);
15117       break;
15118   }
15119 }
15120
15121 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15122 {
15123   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15124   {
15125     // closing door required in case of envelope style request dialogs
15126     if (!skip_request)
15127     {
15128       // prevent short reactivation of overlay buttons while closing door
15129       SetOverlayActive(FALSE);
15130
15131       CloseDoor(DOOR_CLOSE_1);
15132     }
15133
15134     if (network.enabled)
15135       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15136     else
15137     {
15138       if (quick_quit)
15139         FadeSkipNextFadeIn();
15140
15141       SetGameStatus(GAME_MODE_MAIN);
15142
15143       DrawMainMenu();
15144     }
15145   }
15146   else          // continue playing the game
15147   {
15148     if (tape.playing && tape.deactivate_display)
15149       TapeDeactivateDisplayOff(TRUE);
15150
15151     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15152
15153     if (tape.playing && tape.deactivate_display)
15154       TapeDeactivateDisplayOn();
15155   }
15156 }
15157
15158 void RequestQuitGame(boolean ask_if_really_quit)
15159 {
15160   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15161   boolean skip_request = game.all_players_gone || quick_quit;
15162
15163   RequestQuitGameExt(skip_request, quick_quit,
15164                      "Do you really want to quit the game?");
15165 }
15166
15167 void RequestRestartGame(char *message)
15168 {
15169   game.restart_game_message = NULL;
15170
15171   boolean has_started_game = hasStartedNetworkGame();
15172   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15173
15174   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15175   {
15176     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15177   }
15178   else
15179   {
15180     SetGameStatus(GAME_MODE_MAIN);
15181
15182     DrawMainMenu();
15183   }
15184 }
15185
15186 void CheckGameOver(void)
15187 {
15188   static boolean last_game_over = FALSE;
15189   static int game_over_delay = 0;
15190   int game_over_delay_value = 50;
15191   boolean game_over = checkGameFailed();
15192
15193   // do not handle game over if request dialog is already active
15194   if (game.request_active)
15195     return;
15196
15197   // do not ask to play again if game was never actually played
15198   if (!game.GamePlayed)
15199     return;
15200
15201   if (!game_over)
15202   {
15203     last_game_over = FALSE;
15204     game_over_delay = game_over_delay_value;
15205
15206     return;
15207   }
15208
15209   if (game_over_delay > 0)
15210   {
15211     game_over_delay--;
15212
15213     return;
15214   }
15215
15216   if (last_game_over != game_over)
15217     game.restart_game_message = (hasStartedNetworkGame() ?
15218                                  "Game over! Play it again?" :
15219                                  "Game over!");
15220
15221   last_game_over = game_over;
15222 }
15223
15224 boolean checkGameSolved(void)
15225 {
15226   // set for all game engines if level was solved
15227   return game.LevelSolved_GameEnd;
15228 }
15229
15230 boolean checkGameFailed(void)
15231 {
15232   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15233     return (game_em.game_over && !game_em.level_solved);
15234   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15235     return (game_sp.game_over && !game_sp.level_solved);
15236   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15237     return (game_mm.game_over && !game_mm.level_solved);
15238   else                          // GAME_ENGINE_TYPE_RND
15239     return (game.GameOver && !game.LevelSolved);
15240 }
15241
15242 boolean checkGameEnded(void)
15243 {
15244   return (checkGameSolved() || checkGameFailed());
15245 }
15246
15247
15248 // ----------------------------------------------------------------------------
15249 // random generator functions
15250 // ----------------------------------------------------------------------------
15251
15252 unsigned int InitEngineRandom_RND(int seed)
15253 {
15254   game.num_random_calls = 0;
15255
15256   return InitEngineRandom(seed);
15257 }
15258
15259 unsigned int RND(int max)
15260 {
15261   if (max > 0)
15262   {
15263     game.num_random_calls++;
15264
15265     return GetEngineRandom(max);
15266   }
15267
15268   return 0;
15269 }
15270
15271
15272 // ----------------------------------------------------------------------------
15273 // game engine snapshot handling functions
15274 // ----------------------------------------------------------------------------
15275
15276 struct EngineSnapshotInfo
15277 {
15278   // runtime values for custom element collect score
15279   int collect_score[NUM_CUSTOM_ELEMENTS];
15280
15281   // runtime values for group element choice position
15282   int choice_pos[NUM_GROUP_ELEMENTS];
15283
15284   // runtime values for belt position animations
15285   int belt_graphic[4][NUM_BELT_PARTS];
15286   int belt_anim_mode[4][NUM_BELT_PARTS];
15287 };
15288
15289 static struct EngineSnapshotInfo engine_snapshot_rnd;
15290 static char *snapshot_level_identifier = NULL;
15291 static int snapshot_level_nr = -1;
15292
15293 static void SaveEngineSnapshotValues_RND(void)
15294 {
15295   static int belt_base_active_element[4] =
15296   {
15297     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15298     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15299     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15300     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15301   };
15302   int i, j;
15303
15304   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15305   {
15306     int element = EL_CUSTOM_START + i;
15307
15308     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15309   }
15310
15311   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15312   {
15313     int element = EL_GROUP_START + i;
15314
15315     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15316   }
15317
15318   for (i = 0; i < 4; i++)
15319   {
15320     for (j = 0; j < NUM_BELT_PARTS; j++)
15321     {
15322       int element = belt_base_active_element[i] + j;
15323       int graphic = el2img(element);
15324       int anim_mode = graphic_info[graphic].anim_mode;
15325
15326       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15327       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15328     }
15329   }
15330 }
15331
15332 static void LoadEngineSnapshotValues_RND(void)
15333 {
15334   unsigned int num_random_calls = game.num_random_calls;
15335   int i, j;
15336
15337   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15338   {
15339     int element = EL_CUSTOM_START + i;
15340
15341     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15342   }
15343
15344   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15345   {
15346     int element = EL_GROUP_START + i;
15347
15348     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15349   }
15350
15351   for (i = 0; i < 4; i++)
15352   {
15353     for (j = 0; j < NUM_BELT_PARTS; j++)
15354     {
15355       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15356       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15357
15358       graphic_info[graphic].anim_mode = anim_mode;
15359     }
15360   }
15361
15362   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15363   {
15364     InitRND(tape.random_seed);
15365     for (i = 0; i < num_random_calls; i++)
15366       RND(1);
15367   }
15368
15369   if (game.num_random_calls != num_random_calls)
15370   {
15371     Error(ERR_INFO, "number of random calls out of sync");
15372     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15373     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15374     Error(ERR_EXIT, "this should not happen -- please debug");
15375   }
15376 }
15377
15378 void FreeEngineSnapshotSingle(void)
15379 {
15380   FreeSnapshotSingle();
15381
15382   setString(&snapshot_level_identifier, NULL);
15383   snapshot_level_nr = -1;
15384 }
15385
15386 void FreeEngineSnapshotList(void)
15387 {
15388   FreeSnapshotList();
15389 }
15390
15391 static ListNode *SaveEngineSnapshotBuffers(void)
15392 {
15393   ListNode *buffers = NULL;
15394
15395   // copy some special values to a structure better suited for the snapshot
15396
15397   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15398     SaveEngineSnapshotValues_RND();
15399   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15400     SaveEngineSnapshotValues_EM();
15401   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15402     SaveEngineSnapshotValues_SP(&buffers);
15403   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15404     SaveEngineSnapshotValues_MM(&buffers);
15405
15406   // save values stored in special snapshot structure
15407
15408   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15409     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15410   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15411     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15412   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15413     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15414   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15415     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15416
15417   // save further RND engine values
15418
15419   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15420   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15421   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15422
15423   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15424   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15425   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15426   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15427   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15428
15429   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15430   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15431   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15432
15433   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15434
15435   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15436   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15437
15438   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15439   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15440   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15441   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15442   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15443   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15444   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15445   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15446   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15447   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15448   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15449   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15450   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15451   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15452   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15453   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15454   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15455   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15456
15457   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15458   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15459
15460   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15461   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15462   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15463
15464   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15465   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15466
15467   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15468   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15469   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15470   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15471   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15472
15473   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15474   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15475
15476 #if 0
15477   ListNode *node = engine_snapshot_list_rnd;
15478   int num_bytes = 0;
15479
15480   while (node != NULL)
15481   {
15482     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15483
15484     node = node->next;
15485   }
15486
15487   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15488 #endif
15489
15490   return buffers;
15491 }
15492
15493 void SaveEngineSnapshotSingle(void)
15494 {
15495   ListNode *buffers = SaveEngineSnapshotBuffers();
15496
15497   // finally save all snapshot buffers to single snapshot
15498   SaveSnapshotSingle(buffers);
15499
15500   // save level identification information
15501   setString(&snapshot_level_identifier, leveldir_current->identifier);
15502   snapshot_level_nr = level_nr;
15503 }
15504
15505 boolean CheckSaveEngineSnapshotToList(void)
15506 {
15507   boolean save_snapshot =
15508     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15509      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15510       game.snapshot.changed_action) ||
15511      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15512       game.snapshot.collected_item));
15513
15514   game.snapshot.changed_action = FALSE;
15515   game.snapshot.collected_item = FALSE;
15516   game.snapshot.save_snapshot = save_snapshot;
15517
15518   return save_snapshot;
15519 }
15520
15521 void SaveEngineSnapshotToList(void)
15522 {
15523   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15524       tape.quick_resume)
15525     return;
15526
15527   ListNode *buffers = SaveEngineSnapshotBuffers();
15528
15529   // finally save all snapshot buffers to snapshot list
15530   SaveSnapshotToList(buffers);
15531 }
15532
15533 void SaveEngineSnapshotToListInitial(void)
15534 {
15535   FreeEngineSnapshotList();
15536
15537   SaveEngineSnapshotToList();
15538 }
15539
15540 static void LoadEngineSnapshotValues(void)
15541 {
15542   // restore special values from snapshot structure
15543
15544   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15545     LoadEngineSnapshotValues_RND();
15546   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15547     LoadEngineSnapshotValues_EM();
15548   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15549     LoadEngineSnapshotValues_SP();
15550   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15551     LoadEngineSnapshotValues_MM();
15552 }
15553
15554 void LoadEngineSnapshotSingle(void)
15555 {
15556   LoadSnapshotSingle();
15557
15558   LoadEngineSnapshotValues();
15559 }
15560
15561 static void LoadEngineSnapshot_Undo(int steps)
15562 {
15563   LoadSnapshotFromList_Older(steps);
15564
15565   LoadEngineSnapshotValues();
15566 }
15567
15568 static void LoadEngineSnapshot_Redo(int steps)
15569 {
15570   LoadSnapshotFromList_Newer(steps);
15571
15572   LoadEngineSnapshotValues();
15573 }
15574
15575 boolean CheckEngineSnapshotSingle(void)
15576 {
15577   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15578           snapshot_level_nr == level_nr);
15579 }
15580
15581 boolean CheckEngineSnapshotList(void)
15582 {
15583   return CheckSnapshotList();
15584 }
15585
15586
15587 // ---------- new game button stuff -------------------------------------------
15588
15589 static struct
15590 {
15591   int graphic;
15592   struct XY *pos;
15593   int gadget_id;
15594   boolean *setup_value;
15595   boolean allowed_on_tape;
15596   boolean is_touch_button;
15597   char *infotext;
15598 } gamebutton_info[NUM_GAME_BUTTONS] =
15599 {
15600   {
15601     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15602     GAME_CTRL_ID_STOP,                          NULL,
15603     TRUE, FALSE,                                "stop game"
15604   },
15605   {
15606     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15607     GAME_CTRL_ID_PAUSE,                         NULL,
15608     TRUE, FALSE,                                "pause game"
15609   },
15610   {
15611     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15612     GAME_CTRL_ID_PLAY,                          NULL,
15613     TRUE, FALSE,                                "play game"
15614   },
15615   {
15616     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15617     GAME_CTRL_ID_UNDO,                          NULL,
15618     TRUE, FALSE,                                "undo step"
15619   },
15620   {
15621     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15622     GAME_CTRL_ID_REDO,                          NULL,
15623     TRUE, FALSE,                                "redo step"
15624   },
15625   {
15626     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15627     GAME_CTRL_ID_SAVE,                          NULL,
15628     TRUE, FALSE,                                "save game"
15629   },
15630   {
15631     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15632     GAME_CTRL_ID_PAUSE2,                        NULL,
15633     TRUE, FALSE,                                "pause game"
15634   },
15635   {
15636     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15637     GAME_CTRL_ID_LOAD,                          NULL,
15638     TRUE, FALSE,                                "load game"
15639   },
15640   {
15641     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15642     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15643     FALSE, FALSE,                               "stop game"
15644   },
15645   {
15646     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15647     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15648     FALSE, FALSE,                               "pause game"
15649   },
15650   {
15651     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15652     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15653     FALSE, FALSE,                               "play game"
15654   },
15655   {
15656     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15657     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15658     FALSE, TRUE,                                "stop game"
15659   },
15660   {
15661     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15662     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15663     FALSE, TRUE,                                "pause game"
15664   },
15665   {
15666     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15667     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15668     TRUE, FALSE,                                "background music on/off"
15669   },
15670   {
15671     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15672     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15673     TRUE, FALSE,                                "sound loops on/off"
15674   },
15675   {
15676     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15677     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15678     TRUE, FALSE,                                "normal sounds on/off"
15679   },
15680   {
15681     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15682     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15683     FALSE, FALSE,                               "background music on/off"
15684   },
15685   {
15686     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15687     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15688     FALSE, FALSE,                               "sound loops on/off"
15689   },
15690   {
15691     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15692     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15693     FALSE, FALSE,                               "normal sounds on/off"
15694   }
15695 };
15696
15697 void CreateGameButtons(void)
15698 {
15699   int i;
15700
15701   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15702   {
15703     int graphic = gamebutton_info[i].graphic;
15704     struct GraphicInfo *gfx = &graphic_info[graphic];
15705     struct XY *pos = gamebutton_info[i].pos;
15706     struct GadgetInfo *gi;
15707     int button_type;
15708     boolean checked;
15709     unsigned int event_mask;
15710     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15711     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15712     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15713     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15714     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15715     int gd_x   = gfx->src_x;
15716     int gd_y   = gfx->src_y;
15717     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15718     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15719     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15720     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15721     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15722     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15723     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15724     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15725     int id = i;
15726
15727     if (gfx->bitmap == NULL)
15728     {
15729       game_gadget[id] = NULL;
15730
15731       continue;
15732     }
15733
15734     if (id == GAME_CTRL_ID_STOP ||
15735         id == GAME_CTRL_ID_PANEL_STOP ||
15736         id == GAME_CTRL_ID_TOUCH_STOP ||
15737         id == GAME_CTRL_ID_PLAY ||
15738         id == GAME_CTRL_ID_PANEL_PLAY ||
15739         id == GAME_CTRL_ID_SAVE ||
15740         id == GAME_CTRL_ID_LOAD)
15741     {
15742       button_type = GD_TYPE_NORMAL_BUTTON;
15743       checked = FALSE;
15744       event_mask = GD_EVENT_RELEASED;
15745     }
15746     else if (id == GAME_CTRL_ID_UNDO ||
15747              id == GAME_CTRL_ID_REDO)
15748     {
15749       button_type = GD_TYPE_NORMAL_BUTTON;
15750       checked = FALSE;
15751       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15752     }
15753     else
15754     {
15755       button_type = GD_TYPE_CHECK_BUTTON;
15756       checked = (gamebutton_info[i].setup_value != NULL ?
15757                  *gamebutton_info[i].setup_value : FALSE);
15758       event_mask = GD_EVENT_PRESSED;
15759     }
15760
15761     gi = CreateGadget(GDI_CUSTOM_ID, id,
15762                       GDI_IMAGE_ID, graphic,
15763                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15764                       GDI_X, base_x + x,
15765                       GDI_Y, base_y + y,
15766                       GDI_WIDTH, gfx->width,
15767                       GDI_HEIGHT, gfx->height,
15768                       GDI_TYPE, button_type,
15769                       GDI_STATE, GD_BUTTON_UNPRESSED,
15770                       GDI_CHECKED, checked,
15771                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15772                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15773                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15774                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15775                       GDI_DIRECT_DRAW, FALSE,
15776                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15777                       GDI_EVENT_MASK, event_mask,
15778                       GDI_CALLBACK_ACTION, HandleGameButtons,
15779                       GDI_END);
15780
15781     if (gi == NULL)
15782       Error(ERR_EXIT, "cannot create gadget");
15783
15784     game_gadget[id] = gi;
15785   }
15786 }
15787
15788 void FreeGameButtons(void)
15789 {
15790   int i;
15791
15792   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15793     FreeGadget(game_gadget[i]);
15794 }
15795
15796 static void UnmapGameButtonsAtSamePosition(int id)
15797 {
15798   int i;
15799
15800   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15801     if (i != id &&
15802         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15803         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15804       UnmapGadget(game_gadget[i]);
15805 }
15806
15807 static void UnmapGameButtonsAtSamePosition_All(void)
15808 {
15809   if (setup.show_snapshot_buttons)
15810   {
15811     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15812     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15813     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15814   }
15815   else
15816   {
15817     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15818     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15819     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15820
15821     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15822     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15823     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15824   }
15825 }
15826
15827 static void MapGameButtonsAtSamePosition(int id)
15828 {
15829   int i;
15830
15831   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15832     if (i != id &&
15833         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15834         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15835       MapGadget(game_gadget[i]);
15836
15837   UnmapGameButtonsAtSamePosition_All();
15838 }
15839
15840 void MapUndoRedoButtons(void)
15841 {
15842   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15843   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15844
15845   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15846   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15847 }
15848
15849 void UnmapUndoRedoButtons(void)
15850 {
15851   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15852   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15853
15854   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15855   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15856 }
15857
15858 void ModifyPauseButtons(void)
15859 {
15860   static int ids[] =
15861   {
15862     GAME_CTRL_ID_PAUSE,
15863     GAME_CTRL_ID_PAUSE2,
15864     GAME_CTRL_ID_PANEL_PAUSE,
15865     GAME_CTRL_ID_TOUCH_PAUSE,
15866     -1
15867   };
15868   int i;
15869
15870   for (i = 0; ids[i] > -1; i++)
15871     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15872 }
15873
15874 static void MapGameButtonsExt(boolean on_tape)
15875 {
15876   int i;
15877
15878   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15879     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15880         i != GAME_CTRL_ID_UNDO &&
15881         i != GAME_CTRL_ID_REDO)
15882       MapGadget(game_gadget[i]);
15883
15884   UnmapGameButtonsAtSamePosition_All();
15885
15886   RedrawGameButtons();
15887 }
15888
15889 static void UnmapGameButtonsExt(boolean on_tape)
15890 {
15891   int i;
15892
15893   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15894     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15895       UnmapGadget(game_gadget[i]);
15896 }
15897
15898 static void RedrawGameButtonsExt(boolean on_tape)
15899 {
15900   int i;
15901
15902   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15903     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15904       RedrawGadget(game_gadget[i]);
15905 }
15906
15907 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15908 {
15909   if (gi == NULL)
15910     return;
15911
15912   gi->checked = state;
15913 }
15914
15915 static void RedrawSoundButtonGadget(int id)
15916 {
15917   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15918              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15919              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15920              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15921              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15922              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15923              id);
15924
15925   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15926   RedrawGadget(game_gadget[id2]);
15927 }
15928
15929 void MapGameButtons(void)
15930 {
15931   MapGameButtonsExt(FALSE);
15932 }
15933
15934 void UnmapGameButtons(void)
15935 {
15936   UnmapGameButtonsExt(FALSE);
15937 }
15938
15939 void RedrawGameButtons(void)
15940 {
15941   RedrawGameButtonsExt(FALSE);
15942 }
15943
15944 void MapGameButtonsOnTape(void)
15945 {
15946   MapGameButtonsExt(TRUE);
15947 }
15948
15949 void UnmapGameButtonsOnTape(void)
15950 {
15951   UnmapGameButtonsExt(TRUE);
15952 }
15953
15954 void RedrawGameButtonsOnTape(void)
15955 {
15956   RedrawGameButtonsExt(TRUE);
15957 }
15958
15959 static void GameUndoRedoExt(void)
15960 {
15961   ClearPlayerAction();
15962
15963   tape.pausing = TRUE;
15964
15965   RedrawPlayfield();
15966   UpdateAndDisplayGameControlValues();
15967
15968   DrawCompleteVideoDisplay();
15969   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15970   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15971   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15972
15973   BackToFront();
15974 }
15975
15976 static void GameUndo(int steps)
15977 {
15978   if (!CheckEngineSnapshotList())
15979     return;
15980
15981   LoadEngineSnapshot_Undo(steps);
15982
15983   GameUndoRedoExt();
15984 }
15985
15986 static void GameRedo(int steps)
15987 {
15988   if (!CheckEngineSnapshotList())
15989     return;
15990
15991   LoadEngineSnapshot_Redo(steps);
15992
15993   GameUndoRedoExt();
15994 }
15995
15996 static void HandleGameButtonsExt(int id, int button)
15997 {
15998   static boolean game_undo_executed = FALSE;
15999   int steps = BUTTON_STEPSIZE(button);
16000   boolean handle_game_buttons =
16001     (game_status == GAME_MODE_PLAYING ||
16002      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16003
16004   if (!handle_game_buttons)
16005     return;
16006
16007   switch (id)
16008   {
16009     case GAME_CTRL_ID_STOP:
16010     case GAME_CTRL_ID_PANEL_STOP:
16011     case GAME_CTRL_ID_TOUCH_STOP:
16012       if (game_status == GAME_MODE_MAIN)
16013         break;
16014
16015       if (tape.playing)
16016         TapeStop();
16017       else
16018         RequestQuitGame(TRUE);
16019
16020       break;
16021
16022     case GAME_CTRL_ID_PAUSE:
16023     case GAME_CTRL_ID_PAUSE2:
16024     case GAME_CTRL_ID_PANEL_PAUSE:
16025     case GAME_CTRL_ID_TOUCH_PAUSE:
16026       if (network.enabled && game_status == GAME_MODE_PLAYING)
16027       {
16028         if (tape.pausing)
16029           SendToServer_ContinuePlaying();
16030         else
16031           SendToServer_PausePlaying();
16032       }
16033       else
16034         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16035
16036       game_undo_executed = FALSE;
16037
16038       break;
16039
16040     case GAME_CTRL_ID_PLAY:
16041     case GAME_CTRL_ID_PANEL_PLAY:
16042       if (game_status == GAME_MODE_MAIN)
16043       {
16044         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16045       }
16046       else if (tape.pausing)
16047       {
16048         if (network.enabled)
16049           SendToServer_ContinuePlaying();
16050         else
16051           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16052       }
16053       break;
16054
16055     case GAME_CTRL_ID_UNDO:
16056       // Important: When using "save snapshot when collecting an item" mode,
16057       // load last (current) snapshot for first "undo" after pressing "pause"
16058       // (else the last-but-one snapshot would be loaded, because the snapshot
16059       // pointer already points to the last snapshot when pressing "pause",
16060       // which is fine for "every step/move" mode, but not for "every collect")
16061       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16062           !game_undo_executed)
16063         steps--;
16064
16065       game_undo_executed = TRUE;
16066
16067       GameUndo(steps);
16068       break;
16069
16070     case GAME_CTRL_ID_REDO:
16071       GameRedo(steps);
16072       break;
16073
16074     case GAME_CTRL_ID_SAVE:
16075       TapeQuickSave();
16076       break;
16077
16078     case GAME_CTRL_ID_LOAD:
16079       TapeQuickLoad();
16080       break;
16081
16082     case SOUND_CTRL_ID_MUSIC:
16083     case SOUND_CTRL_ID_PANEL_MUSIC:
16084       if (setup.sound_music)
16085       { 
16086         setup.sound_music = FALSE;
16087
16088         FadeMusic();
16089       }
16090       else if (audio.music_available)
16091       { 
16092         setup.sound = setup.sound_music = TRUE;
16093
16094         SetAudioMode(setup.sound);
16095
16096         if (game_status == GAME_MODE_PLAYING)
16097           PlayLevelMusic();
16098       }
16099
16100       RedrawSoundButtonGadget(id);
16101
16102       break;
16103
16104     case SOUND_CTRL_ID_LOOPS:
16105     case SOUND_CTRL_ID_PANEL_LOOPS:
16106       if (setup.sound_loops)
16107         setup.sound_loops = FALSE;
16108       else if (audio.loops_available)
16109       {
16110         setup.sound = setup.sound_loops = TRUE;
16111
16112         SetAudioMode(setup.sound);
16113       }
16114
16115       RedrawSoundButtonGadget(id);
16116
16117       break;
16118
16119     case SOUND_CTRL_ID_SIMPLE:
16120     case SOUND_CTRL_ID_PANEL_SIMPLE:
16121       if (setup.sound_simple)
16122         setup.sound_simple = FALSE;
16123       else if (audio.sound_available)
16124       {
16125         setup.sound = setup.sound_simple = TRUE;
16126
16127         SetAudioMode(setup.sound);
16128       }
16129
16130       RedrawSoundButtonGadget(id);
16131
16132       break;
16133
16134     default:
16135       break;
16136   }
16137 }
16138
16139 static void HandleGameButtons(struct GadgetInfo *gi)
16140 {
16141   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16142 }
16143
16144 void HandleSoundButtonKeys(Key key)
16145 {
16146   if (key == setup.shortcut.sound_simple)
16147     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16148   else if (key == setup.shortcut.sound_loops)
16149     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16150   else if (key == setup.shortcut.sound_music)
16151     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16152 }