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