c8c5de48969841d896ffc8ef6e3042847c2266b0
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 // forward declaration for internal use
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic(void);
1089 static void FadeLevelSoundsAndMusic(void);
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 // for detection of endless loops, caused by custom element programming
1123 // (using maximal playfield width x 10 is just a rough approximation)
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 // ----------------------------------------------------------------------------
1153 // definition of elements that automatically change to other elements after
1154 // a specified time, eventually calling a function when changing
1155 // ----------------------------------------------------------------------------
1156
1157 // forward declaration for changer functions
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 // static variables for playfield scan mode (scanning forward or backward)
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite(void)
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars(void)
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   // make sure that stepsize value is always a power of 2
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   // do no immediately change move delay -- the player might just be moving
1623   player->move_delay_value_next = move_delay;
1624
1625   // information if player can move must be set separately
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig(void)
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void IncrementSokobanFieldsNeeded(void)
1687 {
1688   if (level.sb_fields_needed)
1689     game.sokoban_fields_still_needed++;
1690 }
1691
1692 static void IncrementSokobanObjectsNeeded(void)
1693 {
1694   if (level.sb_objects_needed)
1695     game.sokoban_objects_still_needed++;
1696 }
1697
1698 static void DecrementSokobanFieldsNeeded(void)
1699 {
1700   if (game.sokoban_fields_still_needed > 0)
1701     game.sokoban_fields_still_needed--;
1702 }
1703
1704 static void DecrementSokobanObjectsNeeded(void)
1705 {
1706   if (game.sokoban_objects_still_needed > 0)
1707     game.sokoban_objects_still_needed--;
1708 }
1709
1710 static void InitPlayerField(int x, int y, int element, boolean init_game)
1711 {
1712   if (element == EL_SP_MURPHY)
1713   {
1714     if (init_game)
1715     {
1716       if (stored_player[0].present)
1717       {
1718         Feld[x][y] = EL_SP_MURPHY_CLONE;
1719
1720         return;
1721       }
1722       else
1723       {
1724         stored_player[0].initial_element = element;
1725         stored_player[0].use_murphy = TRUE;
1726
1727         if (!level.use_artwork_element[0])
1728           stored_player[0].artwork_element = EL_SP_MURPHY;
1729       }
1730
1731       Feld[x][y] = EL_PLAYER_1;
1732     }
1733   }
1734
1735   if (init_game)
1736   {
1737     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1738     int jx = player->jx, jy = player->jy;
1739
1740     player->present = TRUE;
1741
1742     player->block_last_field = (element == EL_SP_MURPHY ?
1743                                 level.sp_block_last_field :
1744                                 level.block_last_field);
1745
1746     // ---------- initialize player's last field block delay ------------------
1747
1748     // always start with reliable default value (no adjustment needed)
1749     player->block_delay_adjustment = 0;
1750
1751     // special case 1: in Supaplex, Murphy blocks last field one more frame
1752     if (player->block_last_field && element == EL_SP_MURPHY)
1753       player->block_delay_adjustment = 1;
1754
1755     // special case 2: in game engines before 3.1.1, blocking was different
1756     if (game.use_block_last_field_bug)
1757       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1758
1759     if (!network.enabled || player->connected_network)
1760     {
1761       player->active = TRUE;
1762
1763       // remove potentially duplicate players
1764       if (StorePlayer[jx][jy] == Feld[x][y])
1765         StorePlayer[jx][jy] = 0;
1766
1767       StorePlayer[x][y] = Feld[x][y];
1768
1769 #if DEBUG_INIT_PLAYER
1770       if (options.debug)
1771       {
1772         printf("- player element %d activated", player->element_nr);
1773         printf(" (local player is %d and currently %s)\n",
1774                local_player->element_nr,
1775                local_player->active ? "active" : "not active");
1776       }
1777     }
1778 #endif
1779
1780     Feld[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   if (!init_game)
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Feld[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Feld[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementSokobanFieldsNeeded();
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementSokobanObjectsNeeded();
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1828         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878       InitMovDir(x, y);
1879       break;
1880
1881     case EL_AMOEBA_FULL:
1882     case EL_BD_AMOEBA:
1883       InitAmoebaNr(x, y);
1884       break;
1885
1886     case EL_AMOEBA_DROP:
1887       if (y == lev_fieldy - 1)
1888       {
1889         Feld[x][y] = EL_AMOEBA_GROWING;
1890         Store[x][y] = EL_AMOEBA_WET;
1891       }
1892       break;
1893
1894     case EL_DYNAMITE_ACTIVE:
1895     case EL_SP_DISK_RED_ACTIVE:
1896     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1897     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1900       MovDelay[x][y] = 96;
1901       break;
1902
1903     case EL_EM_DYNAMITE_ACTIVE:
1904       MovDelay[x][y] = 32;
1905       break;
1906
1907     case EL_LAMP:
1908       game.lights_still_needed++;
1909       break;
1910
1911     case EL_PENGUIN:
1912       game.friends_still_needed++;
1913       break;
1914
1915     case EL_PIG:
1916     case EL_DRAGON:
1917       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1918       break;
1919
1920     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1921     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1922     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1923     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1924     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1925     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1926     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1927     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1928     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1929     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1930     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1931     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1932       if (init_game)
1933       {
1934         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1935         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1936         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1937
1938         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1939         {
1940           game.belt_dir[belt_nr] = belt_dir;
1941           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1942         }
1943         else    // more than one switch -- set it like the first switch
1944         {
1945           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1946         }
1947       }
1948       break;
1949
1950     case EL_LIGHT_SWITCH_ACTIVE:
1951       if (init_game)
1952         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1953       break;
1954
1955     case EL_INVISIBLE_STEELWALL:
1956     case EL_INVISIBLE_WALL:
1957     case EL_INVISIBLE_SAND:
1958       if (game.light_time_left > 0 ||
1959           game.lenses_time_left > 0)
1960         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1961       break;
1962
1963     case EL_EMC_MAGIC_BALL:
1964       if (game.ball_state)
1965         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1966       break;
1967
1968     case EL_EMC_MAGIC_BALL_SWITCH:
1969       if (game.ball_state)
1970         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1971       break;
1972
1973     case EL_TRIGGER_PLAYER:
1974     case EL_TRIGGER_ELEMENT:
1975     case EL_TRIGGER_CE_VALUE:
1976     case EL_TRIGGER_CE_SCORE:
1977     case EL_SELF:
1978     case EL_ANY_ELEMENT:
1979     case EL_CURRENT_CE_VALUE:
1980     case EL_CURRENT_CE_SCORE:
1981     case EL_PREV_CE_1:
1982     case EL_PREV_CE_2:
1983     case EL_PREV_CE_3:
1984     case EL_PREV_CE_4:
1985     case EL_PREV_CE_5:
1986     case EL_PREV_CE_6:
1987     case EL_PREV_CE_7:
1988     case EL_PREV_CE_8:
1989     case EL_NEXT_CE_1:
1990     case EL_NEXT_CE_2:
1991     case EL_NEXT_CE_3:
1992     case EL_NEXT_CE_4:
1993     case EL_NEXT_CE_5:
1994     case EL_NEXT_CE_6:
1995     case EL_NEXT_CE_7:
1996     case EL_NEXT_CE_8:
1997       // reference elements should not be used on the playfield
1998       Feld[x][y] = EL_EMPTY;
1999       break;
2000
2001     default:
2002       if (IS_CUSTOM_ELEMENT(element))
2003       {
2004         if (CAN_MOVE(element))
2005           InitMovDir(x, y);
2006
2007         if (!element_info[element].use_last_ce_value || init_game)
2008           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2009       }
2010       else if (IS_GROUP_ELEMENT(element))
2011       {
2012         Feld[x][y] = GetElementFromGroupElement(element);
2013
2014         InitField(x, y, init_game);
2015       }
2016
2017       break;
2018   }
2019
2020   if (!init_game)
2021     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2022 }
2023
2024 static void InitField_WithBug1(int x, int y, boolean init_game)
2025 {
2026   InitField(x, y, init_game);
2027
2028   // not needed to call InitMovDir() -- already done by InitField()!
2029   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2030       CAN_MOVE(Feld[x][y]))
2031     InitMovDir(x, y);
2032 }
2033
2034 static void InitField_WithBug2(int x, int y, boolean init_game)
2035 {
2036   int old_element = Feld[x][y];
2037
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(old_element) &&
2043       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2044     InitMovDir(x, y);
2045
2046   /* this case is in fact a combination of not less than three bugs:
2047      first, it calls InitMovDir() for elements that can move, although this is
2048      already done by InitField(); then, it checks the element that was at this
2049      field _before_ the call to InitField() (which can change it); lastly, it
2050      was not called for "mole with direction" elements, which were treated as
2051      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2052   */
2053 }
2054
2055 static int get_key_element_from_nr(int key_nr)
2056 {
2057   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2058                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2059                           EL_EM_KEY_1 : EL_KEY_1);
2060
2061   return key_base_element + key_nr;
2062 }
2063
2064 static int get_next_dropped_element(struct PlayerInfo *player)
2065 {
2066   return (player->inventory_size > 0 ?
2067           player->inventory_element[player->inventory_size - 1] :
2068           player->inventory_infinite_element != EL_UNDEFINED ?
2069           player->inventory_infinite_element :
2070           player->dynabombs_left > 0 ?
2071           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2072           EL_UNDEFINED);
2073 }
2074
2075 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2076 {
2077   // pos >= 0: get element from bottom of the stack;
2078   // pos <  0: get element from top of the stack
2079
2080   if (pos < 0)
2081   {
2082     int min_inventory_size = -pos;
2083     int inventory_pos = player->inventory_size - min_inventory_size;
2084     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2085
2086     return (player->inventory_size >= min_inventory_size ?
2087             player->inventory_element[inventory_pos] :
2088             player->inventory_infinite_element != EL_UNDEFINED ?
2089             player->inventory_infinite_element :
2090             player->dynabombs_left >= min_dynabombs_left ?
2091             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2092             EL_UNDEFINED);
2093   }
2094   else
2095   {
2096     int min_dynabombs_left = pos + 1;
2097     int min_inventory_size = pos + 1 - player->dynabombs_left;
2098     int inventory_pos = pos - player->dynabombs_left;
2099
2100     return (player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             player->inventory_size >= min_inventory_size ?
2105             player->inventory_element[inventory_pos] :
2106             EL_UNDEFINED);
2107   }
2108 }
2109
2110 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2111 {
2112   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2113   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2114   int compare_result;
2115
2116   if (gpo1->sort_priority != gpo2->sort_priority)
2117     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2118   else
2119     compare_result = gpo1->nr - gpo2->nr;
2120
2121   return compare_result;
2122 }
2123
2124 int getPlayerInventorySize(int player_nr)
2125 {
2126   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2127     return level.native_em_level->ply[player_nr]->dynamite;
2128   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2129     return level.native_sp_level->game_sp->red_disk_count;
2130   else
2131     return stored_player[player_nr].inventory_size;
2132 }
2133
2134 static void InitGameControlValues(void)
2135 {
2136   int i;
2137
2138   for (i = 0; game_panel_controls[i].nr != -1; i++)
2139   {
2140     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2141     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2142     struct TextPosInfo *pos = gpc->pos;
2143     int nr = gpc->nr;
2144     int type = gpc->type;
2145
2146     if (nr != i)
2147     {
2148       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2149       Error(ERR_EXIT, "this should not happen -- please debug");
2150     }
2151
2152     // force update of game controls after initialization
2153     gpc->value = gpc->last_value = -1;
2154     gpc->frame = gpc->last_frame = -1;
2155     gpc->gfx_frame = -1;
2156
2157     // determine panel value width for later calculation of alignment
2158     if (type == TYPE_INTEGER || type == TYPE_STRING)
2159     {
2160       pos->width = pos->size * getFontWidth(pos->font);
2161       pos->height = getFontHeight(pos->font);
2162     }
2163     else if (type == TYPE_ELEMENT)
2164     {
2165       pos->width = pos->size;
2166       pos->height = pos->size;
2167     }
2168
2169     // fill structure for game panel draw order
2170     gpo->nr = gpc->nr;
2171     gpo->sort_priority = pos->sort_priority;
2172   }
2173
2174   // sort game panel controls according to sort_priority and control number
2175   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2176         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2177 }
2178
2179 static void UpdatePlayfieldElementCount(void)
2180 {
2181   boolean use_element_count = FALSE;
2182   int i, j, x, y;
2183
2184   // first check if it is needed at all to calculate playfield element count
2185   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2186     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2187       use_element_count = TRUE;
2188
2189   if (!use_element_count)
2190     return;
2191
2192   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2193     element_info[i].element_count = 0;
2194
2195   SCAN_PLAYFIELD(x, y)
2196   {
2197     element_info[Feld[x][y]].element_count++;
2198   }
2199
2200   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2201     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2202       if (IS_IN_GROUP(j, i))
2203         element_info[EL_GROUP_START + i].element_count +=
2204           element_info[j].element_count;
2205 }
2206
2207 static void UpdateGameControlValues(void)
2208 {
2209   int i, k;
2210   int time = (game.LevelSolved ?
2211               game.LevelSolved_CountingTime :
2212               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2213               level.native_em_level->lev->time :
2214               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2215               level.native_sp_level->game_sp->time_played :
2216               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2217               game_mm.energy_left :
2218               game.no_time_limit ? TimePlayed : TimeLeft);
2219   int score = (game.LevelSolved ?
2220                game.LevelSolved_CountingScore :
2221                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2222                level.native_em_level->lev->score :
2223                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2224                level.native_sp_level->game_sp->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2226                game_mm.score :
2227                game.score);
2228   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               level.native_em_level->lev->required :
2230               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231               level.native_sp_level->game_sp->infotrons_still_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2233               game_mm.kettles_still_needed :
2234               game.gems_still_needed);
2235   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236                      level.native_em_level->lev->required > 0 :
2237                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2240                      game_mm.kettles_still_needed > 0 ||
2241                      game_mm.lights_still_needed > 0 :
2242                      game.gems_still_needed > 0 ||
2243                      game.sokoban_fields_still_needed > 0 ||
2244                      game.sokoban_objects_still_needed > 0 ||
2245                      game.lights_still_needed > 0);
2246   int health = (game.LevelSolved ?
2247                 game.LevelSolved_CountingHealth :
2248                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249                 MM_HEALTH(game_mm.laser_overload_value) :
2250                 game.health);
2251
2252   UpdatePlayfieldElementCount();
2253
2254   // update game panel control values
2255
2256   // used instead of "level_nr" (for network games)
2257   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2258   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2259
2260   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261   for (i = 0; i < MAX_NUM_KEYS; i++)
2262     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2265
2266   if (game.centered_player_nr == -1)
2267   {
2268     for (i = 0; i < MAX_PLAYERS; i++)
2269     {
2270       // only one player in Supaplex game engine
2271       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2272         break;
2273
2274       for (k = 0; k < MAX_NUM_KEYS; k++)
2275       {
2276         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277         {
2278           if (level.native_em_level->ply[i]->keys & (1 << k))
2279             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280               get_key_element_from_nr(k);
2281         }
2282         else if (stored_player[i].key[k])
2283           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284             get_key_element_from_nr(k);
2285       }
2286
2287       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288         getPlayerInventorySize(i);
2289
2290       if (stored_player[i].num_white_keys > 0)
2291         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2292           EL_DC_KEY_WHITE;
2293
2294       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2295         stored_player[i].num_white_keys;
2296     }
2297   }
2298   else
2299   {
2300     int player_nr = game.centered_player_nr;
2301
2302     for (k = 0; k < MAX_NUM_KEYS; k++)
2303     {
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2305       {
2306         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2307           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2308             get_key_element_from_nr(k);
2309       }
2310       else if (stored_player[player_nr].key[k])
2311         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2312           get_key_element_from_nr(k);
2313     }
2314
2315     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2316       getPlayerInventorySize(player_nr);
2317
2318     if (stored_player[player_nr].num_white_keys > 0)
2319       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2320
2321     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2322       stored_player[player_nr].num_white_keys;
2323   }
2324
2325   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2326   {
2327     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2328       get_inventory_element_from_pos(local_player, i);
2329     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, -i - 1);
2331   }
2332
2333   game_panel_controls[GAME_PANEL_SCORE].value = score;
2334   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2335
2336   game_panel_controls[GAME_PANEL_TIME].value = time;
2337
2338   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2339   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2340   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2341
2342   if (level.time == 0)
2343     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2344   else
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2346
2347   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2348   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2349
2350   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2351
2352   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2353     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2354      EL_EMPTY);
2355   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2356     local_player->shield_normal_time_left;
2357   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2358     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2359      EL_EMPTY);
2360   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2361     local_player->shield_deadly_time_left;
2362
2363   game_panel_controls[GAME_PANEL_EXIT].value =
2364     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2365
2366   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2367     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2369     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2370      EL_EMC_MAGIC_BALL_SWITCH);
2371
2372   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2373     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2375     game.light_time_left;
2376
2377   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2378     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2380     game.timegate_time_left;
2381
2382   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2383     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2384
2385   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2386     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2387   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2388     game.lenses_time_left;
2389
2390   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2391     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2393     game.magnify_time_left;
2394
2395   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2396     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2397      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2398      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2399      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2400      EL_BALLOON_SWITCH_NONE);
2401
2402   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2403     local_player->dynabomb_count;
2404   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2405     local_player->dynabomb_size;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2407     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2408
2409   game_panel_controls[GAME_PANEL_PENGUINS].value =
2410     game.friends_still_needed;
2411
2412   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2413     game.sokoban_objects_still_needed;
2414   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2415     game.sokoban_fields_still_needed;
2416
2417   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2418     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2419
2420   for (i = 0; i < NUM_BELTS; i++)
2421   {
2422     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2423       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2424        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2425     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2426       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2427   }
2428
2429   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2430     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2432     game.magic_wall_time_left;
2433
2434   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2435     local_player->gravity;
2436
2437   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2438     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2439
2440   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2441     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2442       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2443        game.panel.element[i].id : EL_UNDEFINED);
2444
2445   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2446     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2447       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2448        element_info[game.panel.element_count[i].id].element_count : 0);
2449
2450   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2451     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2452       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2453        element_info[game.panel.ce_score[i].id].collect_score : 0);
2454
2455   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2456     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2457       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2458        element_info[game.panel.ce_score_element[i].id].collect_score :
2459        EL_UNDEFINED);
2460
2461   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2462   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2463   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2464
2465   // update game panel control frames
2466
2467   for (i = 0; game_panel_controls[i].nr != -1; i++)
2468   {
2469     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2470
2471     if (gpc->type == TYPE_ELEMENT)
2472     {
2473       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2474       {
2475         int last_anim_random_frame = gfx.anim_random_frame;
2476         int element = gpc->value;
2477         int graphic = el2panelimg(element);
2478
2479         if (gpc->value != gpc->last_value)
2480         {
2481           gpc->gfx_frame = 0;
2482           gpc->gfx_random = INIT_GFX_RANDOM();
2483         }
2484         else
2485         {
2486           gpc->gfx_frame++;
2487
2488           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2489               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2490             gpc->gfx_random = INIT_GFX_RANDOM();
2491         }
2492
2493         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2494           gfx.anim_random_frame = gpc->gfx_random;
2495
2496         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2497           gpc->gfx_frame = element_info[element].collect_score;
2498
2499         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2500                                               gpc->gfx_frame);
2501
2502         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2503           gfx.anim_random_frame = last_anim_random_frame;
2504       }
2505     }
2506     else if (gpc->type == TYPE_GRAPHIC)
2507     {
2508       if (gpc->graphic != IMG_UNDEFINED)
2509       {
2510         int last_anim_random_frame = gfx.anim_random_frame;
2511         int graphic = gpc->graphic;
2512
2513         if (gpc->value != gpc->last_value)
2514         {
2515           gpc->gfx_frame = 0;
2516           gpc->gfx_random = INIT_GFX_RANDOM();
2517         }
2518         else
2519         {
2520           gpc->gfx_frame++;
2521
2522           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2523               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2524             gpc->gfx_random = INIT_GFX_RANDOM();
2525         }
2526
2527         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2528           gfx.anim_random_frame = gpc->gfx_random;
2529
2530         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2531
2532         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2533           gfx.anim_random_frame = last_anim_random_frame;
2534       }
2535     }
2536   }
2537 }
2538
2539 static void DisplayGameControlValues(void)
2540 {
2541   boolean redraw_panel = FALSE;
2542   int i;
2543
2544   for (i = 0; game_panel_controls[i].nr != -1; i++)
2545   {
2546     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2547
2548     if (PANEL_DEACTIVATED(gpc->pos))
2549       continue;
2550
2551     if (gpc->value == gpc->last_value &&
2552         gpc->frame == gpc->last_frame)
2553       continue;
2554
2555     redraw_panel = TRUE;
2556   }
2557
2558   if (!redraw_panel)
2559     return;
2560
2561   // copy default game door content to main double buffer
2562
2563   // !!! CHECK AGAIN !!!
2564   SetPanelBackground();
2565   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2566   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2567
2568   // redraw game control buttons
2569   RedrawGameButtons();
2570
2571   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2572
2573   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2574   {
2575     int nr = game_panel_order[i].nr;
2576     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2577     struct TextPosInfo *pos = gpc->pos;
2578     int type = gpc->type;
2579     int value = gpc->value;
2580     int frame = gpc->frame;
2581     int size = pos->size;
2582     int font = pos->font;
2583     boolean draw_masked = pos->draw_masked;
2584     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2585
2586     if (PANEL_DEACTIVATED(pos))
2587       continue;
2588
2589     gpc->last_value = value;
2590     gpc->last_frame = frame;
2591
2592     if (type == TYPE_INTEGER)
2593     {
2594       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2595           nr == GAME_PANEL_TIME)
2596       {
2597         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2598
2599         if (use_dynamic_size)           // use dynamic number of digits
2600         {
2601           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2602           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2603           int size2 = size1 + 1;
2604           int font1 = pos->font;
2605           int font2 = pos->font_alt;
2606
2607           size = (value < value_change ? size1 : size2);
2608           font = (value < value_change ? font1 : font2);
2609         }
2610       }
2611
2612       // correct text size if "digits" is zero or less
2613       if (size <= 0)
2614         size = strlen(int2str(value, size));
2615
2616       // dynamically correct text alignment
2617       pos->width = size * getFontWidth(font);
2618
2619       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2620                   int2str(value, size), font, mask_mode);
2621     }
2622     else if (type == TYPE_ELEMENT)
2623     {
2624       int element, graphic;
2625       Bitmap *src_bitmap;
2626       int src_x, src_y;
2627       int width, height;
2628       int dst_x = PANEL_XPOS(pos);
2629       int dst_y = PANEL_YPOS(pos);
2630
2631       if (value != EL_UNDEFINED && value != EL_EMPTY)
2632       {
2633         element = value;
2634         graphic = el2panelimg(value);
2635
2636         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2637
2638         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2639           size = TILESIZE;
2640
2641         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2642                               &src_x, &src_y);
2643
2644         width  = graphic_info[graphic].width  * size / TILESIZE;
2645         height = graphic_info[graphic].height * size / TILESIZE;
2646
2647         if (draw_masked)
2648           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2649                            dst_x, dst_y);
2650         else
2651           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2652                      dst_x, dst_y);
2653       }
2654     }
2655     else if (type == TYPE_GRAPHIC)
2656     {
2657       int graphic        = gpc->graphic;
2658       int graphic_active = gpc->graphic_active;
2659       Bitmap *src_bitmap;
2660       int src_x, src_y;
2661       int width, height;
2662       int dst_x = PANEL_XPOS(pos);
2663       int dst_y = PANEL_YPOS(pos);
2664       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2665                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2666
2667       if (graphic != IMG_UNDEFINED && !skip)
2668       {
2669         if (pos->style == STYLE_REVERSE)
2670           value = 100 - value;
2671
2672         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2673
2674         if (pos->direction & MV_HORIZONTAL)
2675         {
2676           width  = graphic_info[graphic_active].width * value / 100;
2677           height = graphic_info[graphic_active].height;
2678
2679           if (pos->direction == MV_LEFT)
2680           {
2681             src_x += graphic_info[graphic_active].width - width;
2682             dst_x += graphic_info[graphic_active].width - width;
2683           }
2684         }
2685         else
2686         {
2687           width  = graphic_info[graphic_active].width;
2688           height = graphic_info[graphic_active].height * value / 100;
2689
2690           if (pos->direction == MV_UP)
2691           {
2692             src_y += graphic_info[graphic_active].height - height;
2693             dst_y += graphic_info[graphic_active].height - height;
2694           }
2695         }
2696
2697         if (draw_masked)
2698           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2699                            dst_x, dst_y);
2700         else
2701           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2702                      dst_x, dst_y);
2703
2704         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2705
2706         if (pos->direction & MV_HORIZONTAL)
2707         {
2708           if (pos->direction == MV_RIGHT)
2709           {
2710             src_x += width;
2711             dst_x += width;
2712           }
2713           else
2714           {
2715             dst_x = PANEL_XPOS(pos);
2716           }
2717
2718           width = graphic_info[graphic].width - width;
2719         }
2720         else
2721         {
2722           if (pos->direction == MV_DOWN)
2723           {
2724             src_y += height;
2725             dst_y += height;
2726           }
2727           else
2728           {
2729             dst_y = PANEL_YPOS(pos);
2730           }
2731
2732           height = graphic_info[graphic].height - height;
2733         }
2734
2735         if (draw_masked)
2736           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2737                            dst_x, dst_y);
2738         else
2739           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2740                      dst_x, dst_y);
2741       }
2742     }
2743     else if (type == TYPE_STRING)
2744     {
2745       boolean active = (value != 0);
2746       char *state_normal = "off";
2747       char *state_active = "on";
2748       char *state = (active ? state_active : state_normal);
2749       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2750                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2751                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2752                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2753
2754       if (nr == GAME_PANEL_GRAVITY_STATE)
2755       {
2756         int font1 = pos->font;          // (used for normal state)
2757         int font2 = pos->font_alt;      // (used for active state)
2758
2759         font = (active ? font2 : font1);
2760       }
2761
2762       if (s != NULL)
2763       {
2764         char *s_cut;
2765
2766         if (size <= 0)
2767         {
2768           // don't truncate output if "chars" is zero or less
2769           size = strlen(s);
2770
2771           // dynamically correct text alignment
2772           pos->width = size * getFontWidth(font);
2773         }
2774
2775         s_cut = getStringCopyN(s, size);
2776
2777         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2778                     s_cut, font, mask_mode);
2779
2780         free(s_cut);
2781       }
2782     }
2783
2784     redraw_mask |= REDRAW_DOOR_1;
2785   }
2786
2787   SetGameStatus(GAME_MODE_PLAYING);
2788 }
2789
2790 void UpdateAndDisplayGameControlValues(void)
2791 {
2792   if (tape.deactivate_display)
2793     return;
2794
2795   UpdateGameControlValues();
2796   DisplayGameControlValues();
2797 }
2798
2799 #if 0
2800 static void UpdateGameDoorValues(void)
2801 {
2802   UpdateGameControlValues();
2803 }
2804 #endif
2805
2806 void DrawGameDoorValues(void)
2807 {
2808   DisplayGameControlValues();
2809 }
2810
2811
2812 // ============================================================================
2813 // InitGameEngine()
2814 // ----------------------------------------------------------------------------
2815 // initialize game engine due to level / tape version number
2816 // ============================================================================
2817
2818 static void InitGameEngine(void)
2819 {
2820   int i, j, k, l, x, y;
2821
2822   // set game engine from tape file when re-playing, else from level file
2823   game.engine_version = (tape.playing ? tape.engine_version :
2824                          level.game_version);
2825
2826   // set single or multi-player game mode (needed for re-playing tapes)
2827   game.team_mode = setup.team_mode;
2828
2829   if (tape.playing)
2830   {
2831     int num_players = 0;
2832
2833     for (i = 0; i < MAX_PLAYERS; i++)
2834       if (tape.player_participates[i])
2835         num_players++;
2836
2837     // multi-player tapes contain input data for more than one player
2838     game.team_mode = (num_players > 1);
2839   }
2840
2841   // --------------------------------------------------------------------------
2842   // set flags for bugs and changes according to active game engine version
2843   // --------------------------------------------------------------------------
2844
2845   /*
2846     Summary of bugfix/change:
2847     Fixed handling for custom elements that change when pushed by the player.
2848
2849     Fixed/changed in version:
2850     3.1.0
2851
2852     Description:
2853     Before 3.1.0, custom elements that "change when pushing" changed directly
2854     after the player started pushing them (until then handled in "DigField()").
2855     Since 3.1.0, these custom elements are not changed until the "pushing"
2856     move of the element is finished (now handled in "ContinueMoving()").
2857
2858     Affected levels/tapes:
2859     The first condition is generally needed for all levels/tapes before version
2860     3.1.0, which might use the old behaviour before it was changed; known tapes
2861     that are affected are some tapes from the level set "Walpurgis Gardens" by
2862     Jamie Cullen.
2863     The second condition is an exception from the above case and is needed for
2864     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2865     above (including some development versions of 3.1.0), but before it was
2866     known that this change would break tapes like the above and was fixed in
2867     3.1.1, so that the changed behaviour was active although the engine version
2868     while recording maybe was before 3.1.0. There is at least one tape that is
2869     affected by this exception, which is the tape for the one-level set "Bug
2870     Machine" by Juergen Bonhagen.
2871   */
2872
2873   game.use_change_when_pushing_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2875      !(tape.playing &&
2876        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2877        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2878
2879   /*
2880     Summary of bugfix/change:
2881     Fixed handling for blocking the field the player leaves when moving.
2882
2883     Fixed/changed in version:
2884     3.1.1
2885
2886     Description:
2887     Before 3.1.1, when "block last field when moving" was enabled, the field
2888     the player is leaving when moving was blocked for the time of the move,
2889     and was directly unblocked afterwards. This resulted in the last field
2890     being blocked for exactly one less than the number of frames of one player
2891     move. Additionally, even when blocking was disabled, the last field was
2892     blocked for exactly one frame.
2893     Since 3.1.1, due to changes in player movement handling, the last field
2894     is not blocked at all when blocking is disabled. When blocking is enabled,
2895     the last field is blocked for exactly the number of frames of one player
2896     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2897     last field is blocked for exactly one more than the number of frames of
2898     one player move.
2899
2900     Affected levels/tapes:
2901     (!!! yet to be determined -- probably many !!!)
2902   */
2903
2904   game.use_block_last_field_bug =
2905     (game.engine_version < VERSION_IDENT(3,1,1,0));
2906
2907   game_em.use_single_button =
2908     (game.engine_version > VERSION_IDENT(4,0,0,2));
2909
2910   game_em.use_snap_key_bug =
2911     (game.engine_version < VERSION_IDENT(4,0,1,0));
2912
2913   // --------------------------------------------------------------------------
2914
2915   // set maximal allowed number of custom element changes per game frame
2916   game.max_num_changes_per_frame = 1;
2917
2918   // default scan direction: scan playfield from top/left to bottom/right
2919   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2920
2921   // dynamically adjust element properties according to game engine version
2922   InitElementPropertiesEngine(game.engine_version);
2923
2924 #if 0
2925   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2926   printf("          tape version == %06d [%s] [file: %06d]\n",
2927          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2928          tape.file_version);
2929   printf("       => game.engine_version == %06d\n", game.engine_version);
2930 #endif
2931
2932   // ---------- initialize player's initial move delay ------------------------
2933
2934   // dynamically adjust player properties according to level information
2935   for (i = 0; i < MAX_PLAYERS; i++)
2936     game.initial_move_delay_value[i] =
2937       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2938
2939   // dynamically adjust player properties according to game engine version
2940   for (i = 0; i < MAX_PLAYERS; i++)
2941     game.initial_move_delay[i] =
2942       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2943        game.initial_move_delay_value[i] : 0);
2944
2945   // ---------- initialize player's initial push delay ------------------------
2946
2947   // dynamically adjust player properties according to game engine version
2948   game.initial_push_delay_value =
2949     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2950
2951   // ---------- initialize changing elements ----------------------------------
2952
2953   // initialize changing elements information
2954   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2955   {
2956     struct ElementInfo *ei = &element_info[i];
2957
2958     // this pointer might have been changed in the level editor
2959     ei->change = &ei->change_page[0];
2960
2961     if (!IS_CUSTOM_ELEMENT(i))
2962     {
2963       ei->change->target_element = EL_EMPTY_SPACE;
2964       ei->change->delay_fixed = 0;
2965       ei->change->delay_random = 0;
2966       ei->change->delay_frames = 1;
2967     }
2968
2969     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2970     {
2971       ei->has_change_event[j] = FALSE;
2972
2973       ei->event_page_nr[j] = 0;
2974       ei->event_page[j] = &ei->change_page[0];
2975     }
2976   }
2977
2978   // add changing elements from pre-defined list
2979   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2980   {
2981     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2982     struct ElementInfo *ei = &element_info[ch_delay->element];
2983
2984     ei->change->target_element       = ch_delay->target_element;
2985     ei->change->delay_fixed          = ch_delay->change_delay;
2986
2987     ei->change->pre_change_function  = ch_delay->pre_change_function;
2988     ei->change->change_function      = ch_delay->change_function;
2989     ei->change->post_change_function = ch_delay->post_change_function;
2990
2991     ei->change->can_change = TRUE;
2992     ei->change->can_change_or_has_action = TRUE;
2993
2994     ei->has_change_event[CE_DELAY] = TRUE;
2995
2996     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2997     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2998   }
2999
3000   // ---------- initialize internal run-time variables ------------------------
3001
3002   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3003   {
3004     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3005
3006     for (j = 0; j < ei->num_change_pages; j++)
3007     {
3008       ei->change_page[j].can_change_or_has_action =
3009         (ei->change_page[j].can_change |
3010          ei->change_page[j].has_action);
3011     }
3012   }
3013
3014   // add change events from custom element configuration
3015   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3016   {
3017     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3018
3019     for (j = 0; j < ei->num_change_pages; j++)
3020     {
3021       if (!ei->change_page[j].can_change_or_has_action)
3022         continue;
3023
3024       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3025       {
3026         // only add event page for the first page found with this event
3027         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3028         {
3029           ei->has_change_event[k] = TRUE;
3030
3031           ei->event_page_nr[k] = j;
3032           ei->event_page[k] = &ei->change_page[j];
3033         }
3034       }
3035     }
3036   }
3037
3038   // ---------- initialize reference elements in change conditions ------------
3039
3040   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3041   {
3042     int element = EL_CUSTOM_START + i;
3043     struct ElementInfo *ei = &element_info[element];
3044
3045     for (j = 0; j < ei->num_change_pages; j++)
3046     {
3047       int trigger_element = ei->change_page[j].initial_trigger_element;
3048
3049       if (trigger_element >= EL_PREV_CE_8 &&
3050           trigger_element <= EL_NEXT_CE_8)
3051         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3052
3053       ei->change_page[j].trigger_element = trigger_element;
3054     }
3055   }
3056
3057   // ---------- initialize run-time trigger player and element ----------------
3058
3059   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3060   {
3061     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3062
3063     for (j = 0; j < ei->num_change_pages; j++)
3064     {
3065       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3066       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3067       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3068       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3069       ei->change_page[j].actual_trigger_ce_value = 0;
3070       ei->change_page[j].actual_trigger_ce_score = 0;
3071     }
3072   }
3073
3074   // ---------- initialize trigger events -------------------------------------
3075
3076   // initialize trigger events information
3077   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3078     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3079       trigger_events[i][j] = FALSE;
3080
3081   // add trigger events from element change event properties
3082   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3083   {
3084     struct ElementInfo *ei = &element_info[i];
3085
3086     for (j = 0; j < ei->num_change_pages; j++)
3087     {
3088       if (!ei->change_page[j].can_change_or_has_action)
3089         continue;
3090
3091       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3092       {
3093         int trigger_element = ei->change_page[j].trigger_element;
3094
3095         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3096         {
3097           if (ei->change_page[j].has_event[k])
3098           {
3099             if (IS_GROUP_ELEMENT(trigger_element))
3100             {
3101               struct ElementGroupInfo *group =
3102                 element_info[trigger_element].group;
3103
3104               for (l = 0; l < group->num_elements_resolved; l++)
3105                 trigger_events[group->element_resolved[l]][k] = TRUE;
3106             }
3107             else if (trigger_element == EL_ANY_ELEMENT)
3108               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3109                 trigger_events[l][k] = TRUE;
3110             else
3111               trigger_events[trigger_element][k] = TRUE;
3112           }
3113         }
3114       }
3115     }
3116   }
3117
3118   // ---------- initialize push delay -----------------------------------------
3119
3120   // initialize push delay values to default
3121   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122   {
3123     if (!IS_CUSTOM_ELEMENT(i))
3124     {
3125       // set default push delay values (corrected since version 3.0.7-1)
3126       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3127       {
3128         element_info[i].push_delay_fixed = 2;
3129         element_info[i].push_delay_random = 8;
3130       }
3131       else
3132       {
3133         element_info[i].push_delay_fixed = 8;
3134         element_info[i].push_delay_random = 8;
3135       }
3136     }
3137   }
3138
3139   // set push delay value for certain elements from pre-defined list
3140   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3141   {
3142     int e = push_delay_list[i].element;
3143
3144     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3145     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3146   }
3147
3148   // set push delay value for Supaplex elements for newer engine versions
3149   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3150   {
3151     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     {
3153       if (IS_SP_ELEMENT(i))
3154       {
3155         // set SP push delay to just enough to push under a falling zonk
3156         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3157
3158         element_info[i].push_delay_fixed  = delay;
3159         element_info[i].push_delay_random = 0;
3160       }
3161     }
3162   }
3163
3164   // ---------- initialize move stepsize --------------------------------------
3165
3166   // initialize move stepsize values to default
3167   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3168     if (!IS_CUSTOM_ELEMENT(i))
3169       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3170
3171   // set move stepsize value for certain elements from pre-defined list
3172   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3173   {
3174     int e = move_stepsize_list[i].element;
3175
3176     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3177   }
3178
3179   // ---------- initialize collect score --------------------------------------
3180
3181   // initialize collect score values for custom elements from initial value
3182   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183     if (IS_CUSTOM_ELEMENT(i))
3184       element_info[i].collect_score = element_info[i].collect_score_initial;
3185
3186   // ---------- initialize collect count --------------------------------------
3187
3188   // initialize collect count values for non-custom elements
3189   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3190     if (!IS_CUSTOM_ELEMENT(i))
3191       element_info[i].collect_count_initial = 0;
3192
3193   // add collect count values for all elements from pre-defined list
3194   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3195     element_info[collect_count_list[i].element].collect_count_initial =
3196       collect_count_list[i].count;
3197
3198   // ---------- initialize access direction -----------------------------------
3199
3200   // initialize access direction values to default (access from every side)
3201   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3202     if (!IS_CUSTOM_ELEMENT(i))
3203       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3204
3205   // set access direction value for certain elements from pre-defined list
3206   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3207     element_info[access_direction_list[i].element].access_direction =
3208       access_direction_list[i].direction;
3209
3210   // ---------- initialize explosion content ----------------------------------
3211   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3212   {
3213     if (IS_CUSTOM_ELEMENT(i))
3214       continue;
3215
3216     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3217     {
3218       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3219
3220       element_info[i].content.e[x][y] =
3221         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3222          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3223          i == EL_PLAYER_3 ? EL_EMERALD :
3224          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3225          i == EL_MOLE ? EL_EMERALD_RED :
3226          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3227          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3228          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3229          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3230          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3231          i == EL_WALL_EMERALD ? EL_EMERALD :
3232          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3233          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3234          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3235          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3236          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3237          i == EL_WALL_PEARL ? EL_PEARL :
3238          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3239          EL_EMPTY);
3240     }
3241   }
3242
3243   // ---------- initialize recursion detection --------------------------------
3244   recursion_loop_depth = 0;
3245   recursion_loop_detected = FALSE;
3246   recursion_loop_element = EL_UNDEFINED;
3247
3248   // ---------- initialize graphics engine ------------------------------------
3249   game.scroll_delay_value =
3250     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3251      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3252   game.scroll_delay_value =
3253     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3254
3255   // ---------- initialize game engine snapshots ------------------------------
3256   for (i = 0; i < MAX_PLAYERS; i++)
3257     game.snapshot.last_action[i] = 0;
3258   game.snapshot.changed_action = FALSE;
3259   game.snapshot.collected_item = FALSE;
3260   game.snapshot.mode =
3261     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3262      SNAPSHOT_MODE_EVERY_STEP :
3263      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3264      SNAPSHOT_MODE_EVERY_MOVE :
3265      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3266      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3267   game.snapshot.save_snapshot = FALSE;
3268
3269   // ---------- initialize level time for Supaplex engine ---------------------
3270   // Supaplex levels with time limit currently unsupported -- should be added
3271   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3272     level.time = 0;
3273 }
3274
3275 static int get_num_special_action(int element, int action_first,
3276                                   int action_last)
3277 {
3278   int num_special_action = 0;
3279   int i, j;
3280
3281   for (i = action_first; i <= action_last; i++)
3282   {
3283     boolean found = FALSE;
3284
3285     for (j = 0; j < NUM_DIRECTIONS; j++)
3286       if (el_act_dir2img(element, i, j) !=
3287           el_act_dir2img(element, ACTION_DEFAULT, j))
3288         found = TRUE;
3289
3290     if (found)
3291       num_special_action++;
3292     else
3293       break;
3294   }
3295
3296   return num_special_action;
3297 }
3298
3299
3300 // ============================================================================
3301 // InitGame()
3302 // ----------------------------------------------------------------------------
3303 // initialize and start new game
3304 // ============================================================================
3305
3306 #if DEBUG_INIT_PLAYER
3307 static void DebugPrintPlayerStatus(char *message)
3308 {
3309   int i;
3310
3311   if (!options.debug)
3312     return;
3313
3314   printf("%s:\n", message);
3315
3316   for (i = 0; i < MAX_PLAYERS; i++)
3317   {
3318     struct PlayerInfo *player = &stored_player[i];
3319
3320     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3321            i + 1,
3322            player->present,
3323            player->connected,
3324            player->connected_locally,
3325            player->connected_network,
3326            player->active);
3327
3328     if (local_player == player)
3329       printf(" (local player)");
3330
3331     printf("\n");
3332   }
3333 }
3334 #endif
3335
3336 void InitGame(void)
3337 {
3338   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3339   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3340   int fade_mask = REDRAW_FIELD;
3341
3342   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3343   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3344   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3345   int initial_move_dir = MV_DOWN;
3346   int i, j, x, y;
3347
3348   // required here to update video display before fading (FIX THIS)
3349   DrawMaskedBorder(REDRAW_DOOR_2);
3350
3351   if (!game.restart_level)
3352     CloseDoor(DOOR_CLOSE_1);
3353
3354   SetGameStatus(GAME_MODE_PLAYING);
3355
3356   if (level_editor_test_game)
3357     FadeSkipNextFadeIn();
3358   else
3359     FadeSetEnterScreen();
3360
3361   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3362     fade_mask = REDRAW_ALL;
3363
3364   FadeLevelSoundsAndMusic();
3365
3366   ExpireSoundLoops(TRUE);
3367
3368   FadeOut(fade_mask);
3369
3370   // needed if different viewport properties defined for playing
3371   ChangeViewportPropertiesIfNeeded();
3372
3373   ClearField();
3374
3375   DrawCompleteVideoDisplay();
3376
3377   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3378
3379   InitGameEngine();
3380   InitGameControlValues();
3381
3382   // don't play tapes over network
3383   network_playing = (network.enabled && !tape.playing);
3384
3385   for (i = 0; i < MAX_PLAYERS; i++)
3386   {
3387     struct PlayerInfo *player = &stored_player[i];
3388
3389     player->index_nr = i;
3390     player->index_bit = (1 << i);
3391     player->element_nr = EL_PLAYER_1 + i;
3392
3393     player->present = FALSE;
3394     player->active = FALSE;
3395     player->mapped = FALSE;
3396
3397     player->killed = FALSE;
3398     player->reanimated = FALSE;
3399     player->buried = FALSE;
3400
3401     player->action = 0;
3402     player->effective_action = 0;
3403     player->programmed_action = 0;
3404
3405     player->mouse_action.lx = 0;
3406     player->mouse_action.ly = 0;
3407     player->mouse_action.button = 0;
3408     player->mouse_action.button_hint = 0;
3409
3410     player->effective_mouse_action.lx = 0;
3411     player->effective_mouse_action.ly = 0;
3412     player->effective_mouse_action.button = 0;
3413     player->effective_mouse_action.button_hint = 0;
3414
3415     for (j = 0; j < MAX_NUM_KEYS; j++)
3416       player->key[j] = FALSE;
3417
3418     player->num_white_keys = 0;
3419
3420     player->dynabomb_count = 0;
3421     player->dynabomb_size = 1;
3422     player->dynabombs_left = 0;
3423     player->dynabomb_xl = FALSE;
3424
3425     player->MovDir = initial_move_dir;
3426     player->MovPos = 0;
3427     player->GfxPos = 0;
3428     player->GfxDir = initial_move_dir;
3429     player->GfxAction = ACTION_DEFAULT;
3430     player->Frame = 0;
3431     player->StepFrame = 0;
3432
3433     player->initial_element = player->element_nr;
3434     player->artwork_element =
3435       (level.use_artwork_element[i] ? level.artwork_element[i] :
3436        player->element_nr);
3437     player->use_murphy = FALSE;
3438
3439     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3440     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3441
3442     player->gravity = level.initial_player_gravity[i];
3443
3444     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3445
3446     player->actual_frame_counter = 0;
3447
3448     player->step_counter = 0;
3449
3450     player->last_move_dir = initial_move_dir;
3451
3452     player->is_active = FALSE;
3453
3454     player->is_waiting = FALSE;
3455     player->is_moving = FALSE;
3456     player->is_auto_moving = FALSE;
3457     player->is_digging = FALSE;
3458     player->is_snapping = FALSE;
3459     player->is_collecting = FALSE;
3460     player->is_pushing = FALSE;
3461     player->is_switching = FALSE;
3462     player->is_dropping = FALSE;
3463     player->is_dropping_pressed = FALSE;
3464
3465     player->is_bored = FALSE;
3466     player->is_sleeping = FALSE;
3467
3468     player->was_waiting = TRUE;
3469     player->was_moving = FALSE;
3470     player->was_snapping = FALSE;
3471     player->was_dropping = FALSE;
3472
3473     player->force_dropping = FALSE;
3474
3475     player->frame_counter_bored = -1;
3476     player->frame_counter_sleeping = -1;
3477
3478     player->anim_delay_counter = 0;
3479     player->post_delay_counter = 0;
3480
3481     player->dir_waiting = initial_move_dir;
3482     player->action_waiting = ACTION_DEFAULT;
3483     player->last_action_waiting = ACTION_DEFAULT;
3484     player->special_action_bored = ACTION_DEFAULT;
3485     player->special_action_sleeping = ACTION_DEFAULT;
3486
3487     player->switch_x = -1;
3488     player->switch_y = -1;
3489
3490     player->drop_x = -1;
3491     player->drop_y = -1;
3492
3493     player->show_envelope = 0;
3494
3495     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3496
3497     player->push_delay       = -1;      // initialized when pushing starts
3498     player->push_delay_value = game.initial_push_delay_value;
3499
3500     player->drop_delay = 0;
3501     player->drop_pressed_delay = 0;
3502
3503     player->last_jx = -1;
3504     player->last_jy = -1;
3505     player->jx = -1;
3506     player->jy = -1;
3507
3508     player->shield_normal_time_left = 0;
3509     player->shield_deadly_time_left = 0;
3510
3511     player->inventory_infinite_element = EL_UNDEFINED;
3512     player->inventory_size = 0;
3513
3514     if (level.use_initial_inventory[i])
3515     {
3516       for (j = 0; j < level.initial_inventory_size[i]; j++)
3517       {
3518         int element = level.initial_inventory_content[i][j];
3519         int collect_count = element_info[element].collect_count_initial;
3520         int k;
3521
3522         if (!IS_CUSTOM_ELEMENT(element))
3523           collect_count = 1;
3524
3525         if (collect_count == 0)
3526           player->inventory_infinite_element = element;
3527         else
3528           for (k = 0; k < collect_count; k++)
3529             if (player->inventory_size < MAX_INVENTORY_SIZE)
3530               player->inventory_element[player->inventory_size++] = element;
3531       }
3532     }
3533
3534     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3535     SnapField(player, 0, 0);
3536
3537     map_player_action[i] = i;
3538   }
3539
3540   network_player_action_received = FALSE;
3541
3542   // initial null action
3543   if (network_playing)
3544     SendToServer_MovePlayer(MV_NONE);
3545
3546   ZX = ZY = -1;
3547   ExitX = ExitY = -1;
3548
3549   FrameCounter = 0;
3550   TimeFrames = 0;
3551   TimePlayed = 0;
3552   TimeLeft = level.time;
3553   TapeTime = 0;
3554
3555   ScreenMovDir = MV_NONE;
3556   ScreenMovPos = 0;
3557   ScreenGfxPos = 0;
3558
3559   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3560
3561   game.all_players_gone = FALSE;
3562
3563   game.LevelSolved = FALSE;
3564   game.GameOver = FALSE;
3565
3566   game.LevelSolved_GameWon = FALSE;
3567   game.LevelSolved_GameEnd = FALSE;
3568   game.LevelSolved_SaveTape = FALSE;
3569   game.LevelSolved_SaveScore = FALSE;
3570
3571   game.LevelSolved_CountingTime = 0;
3572   game.LevelSolved_CountingScore = 0;
3573   game.LevelSolved_CountingHealth = 0;
3574
3575   game.panel.active = TRUE;
3576
3577   game.no_time_limit = (level.time == 0);
3578
3579   game.yamyam_content_nr = 0;
3580   game.robot_wheel_active = FALSE;
3581   game.magic_wall_active = FALSE;
3582   game.magic_wall_time_left = 0;
3583   game.light_time_left = 0;
3584   game.timegate_time_left = 0;
3585   game.switchgate_pos = 0;
3586   game.wind_direction = level.wind_direction_initial;
3587
3588   game.score = 0;
3589   game.score_final = 0;
3590
3591   game.health = MAX_HEALTH;
3592   game.health_final = MAX_HEALTH;
3593
3594   game.gems_still_needed = level.gems_needed;
3595   game.sokoban_fields_still_needed = 0;
3596   game.sokoban_objects_still_needed = 0;
3597   game.lights_still_needed = 0;
3598   game.players_still_needed = 0;
3599   game.friends_still_needed = 0;
3600
3601   game.lenses_time_left = 0;
3602   game.magnify_time_left = 0;
3603
3604   game.ball_state = level.ball_state_initial;
3605   game.ball_content_nr = 0;
3606
3607   game.explosions_delayed = TRUE;
3608
3609   game.envelope_active = FALSE;
3610
3611   for (i = 0; i < NUM_BELTS; i++)
3612   {
3613     game.belt_dir[i] = MV_NONE;
3614     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3615   }
3616
3617   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3618     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3619
3620 #if DEBUG_INIT_PLAYER
3621   DebugPrintPlayerStatus("Player status at level initialization");
3622 #endif
3623
3624   SCAN_PLAYFIELD(x, y)
3625   {
3626     Feld[x][y] = Last[x][y] = level.field[x][y];
3627     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3628     ChangeDelay[x][y] = 0;
3629     ChangePage[x][y] = -1;
3630     CustomValue[x][y] = 0;              // initialized in InitField()
3631     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3632     AmoebaNr[x][y] = 0;
3633     WasJustMoving[x][y] = 0;
3634     WasJustFalling[x][y] = 0;
3635     CheckCollision[x][y] = 0;
3636     CheckImpact[x][y] = 0;
3637     Stop[x][y] = FALSE;
3638     Pushed[x][y] = FALSE;
3639
3640     ChangeCount[x][y] = 0;
3641     ChangeEvent[x][y] = -1;
3642
3643     ExplodePhase[x][y] = 0;
3644     ExplodeDelay[x][y] = 0;
3645     ExplodeField[x][y] = EX_TYPE_NONE;
3646
3647     RunnerVisit[x][y] = 0;
3648     PlayerVisit[x][y] = 0;
3649
3650     GfxFrame[x][y] = 0;
3651     GfxRandom[x][y] = INIT_GFX_RANDOM();
3652     GfxElement[x][y] = EL_UNDEFINED;
3653     GfxAction[x][y] = ACTION_DEFAULT;
3654     GfxDir[x][y] = MV_NONE;
3655     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3656   }
3657
3658   SCAN_PLAYFIELD(x, y)
3659   {
3660     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3661       emulate_bd = FALSE;
3662     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3663       emulate_sb = FALSE;
3664     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3665       emulate_sp = FALSE;
3666
3667     InitField(x, y, TRUE);
3668
3669     ResetGfxAnimation(x, y);
3670   }
3671
3672   InitBeltMovement();
3673
3674   for (i = 0; i < MAX_PLAYERS; i++)
3675   {
3676     struct PlayerInfo *player = &stored_player[i];
3677
3678     // set number of special actions for bored and sleeping animation
3679     player->num_special_action_bored =
3680       get_num_special_action(player->artwork_element,
3681                              ACTION_BORING_1, ACTION_BORING_LAST);
3682     player->num_special_action_sleeping =
3683       get_num_special_action(player->artwork_element,
3684                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3685   }
3686
3687   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3688                     emulate_sb ? EMU_SOKOBAN :
3689                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3690
3691   // initialize type of slippery elements
3692   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3693   {
3694     if (!IS_CUSTOM_ELEMENT(i))
3695     {
3696       // default: elements slip down either to the left or right randomly
3697       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3698
3699       // SP style elements prefer to slip down on the left side
3700       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3701         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3702
3703       // BD style elements prefer to slip down on the left side
3704       if (game.emulation == EMU_BOULDERDASH)
3705         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3706     }
3707   }
3708
3709   // initialize explosion and ignition delay
3710   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3711   {
3712     if (!IS_CUSTOM_ELEMENT(i))
3713     {
3714       int num_phase = 8;
3715       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3716                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3717                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3718       int last_phase = (num_phase + 1) * delay;
3719       int half_phase = (num_phase / 2) * delay;
3720
3721       element_info[i].explosion_delay = last_phase - 1;
3722       element_info[i].ignition_delay = half_phase;
3723
3724       if (i == EL_BLACK_ORB)
3725         element_info[i].ignition_delay = 1;
3726     }
3727   }
3728
3729   // correct non-moving belts to start moving left
3730   for (i = 0; i < NUM_BELTS; i++)
3731     if (game.belt_dir[i] == MV_NONE)
3732       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3733
3734 #if USE_NEW_PLAYER_ASSIGNMENTS
3735   for (i = 0; i < MAX_PLAYERS; i++)
3736   {
3737     stored_player[i].connected = FALSE;
3738
3739     // in network game mode, the local player might not be the first player
3740     if (stored_player[i].connected_locally)
3741       local_player = &stored_player[i];
3742   }
3743
3744   if (!network.enabled)
3745     local_player->connected = TRUE;
3746
3747   if (tape.playing)
3748   {
3749     for (i = 0; i < MAX_PLAYERS; i++)
3750       stored_player[i].connected = tape.player_participates[i];
3751   }
3752   else if (network.enabled)
3753   {
3754     // add team mode players connected over the network (needed for correct
3755     // assignment of player figures from level to locally playing players)
3756
3757     for (i = 0; i < MAX_PLAYERS; i++)
3758       if (stored_player[i].connected_network)
3759         stored_player[i].connected = TRUE;
3760   }
3761   else if (game.team_mode)
3762   {
3763     // try to guess locally connected team mode players (needed for correct
3764     // assignment of player figures from level to locally playing players)
3765
3766     for (i = 0; i < MAX_PLAYERS; i++)
3767       if (setup.input[i].use_joystick ||
3768           setup.input[i].key.left != KSYM_UNDEFINED)
3769         stored_player[i].connected = TRUE;
3770   }
3771
3772 #if DEBUG_INIT_PLAYER
3773   DebugPrintPlayerStatus("Player status after level initialization");
3774 #endif
3775
3776 #if DEBUG_INIT_PLAYER
3777   if (options.debug)
3778     printf("Reassigning players ...\n");
3779 #endif
3780
3781   // check if any connected player was not found in playfield
3782   for (i = 0; i < MAX_PLAYERS; i++)
3783   {
3784     struct PlayerInfo *player = &stored_player[i];
3785
3786     if (player->connected && !player->present)
3787     {
3788       struct PlayerInfo *field_player = NULL;
3789
3790 #if DEBUG_INIT_PLAYER
3791       if (options.debug)
3792         printf("- looking for field player for player %d ...\n", i + 1);
3793 #endif
3794
3795       // assign first free player found that is present in the playfield
3796
3797       // first try: look for unmapped playfield player that is not connected
3798       for (j = 0; j < MAX_PLAYERS; j++)
3799         if (field_player == NULL &&
3800             stored_player[j].present &&
3801             !stored_player[j].mapped &&
3802             !stored_player[j].connected)
3803           field_player = &stored_player[j];
3804
3805       // second try: look for *any* unmapped playfield player
3806       for (j = 0; j < MAX_PLAYERS; j++)
3807         if (field_player == NULL &&
3808             stored_player[j].present &&
3809             !stored_player[j].mapped)
3810           field_player = &stored_player[j];
3811
3812       if (field_player != NULL)
3813       {
3814         int jx = field_player->jx, jy = field_player->jy;
3815
3816 #if DEBUG_INIT_PLAYER
3817         if (options.debug)
3818           printf("- found player %d\n", field_player->index_nr + 1);
3819 #endif
3820
3821         player->present = FALSE;
3822         player->active = FALSE;
3823
3824         field_player->present = TRUE;
3825         field_player->active = TRUE;
3826
3827         /*
3828         player->initial_element = field_player->initial_element;
3829         player->artwork_element = field_player->artwork_element;
3830
3831         player->block_last_field       = field_player->block_last_field;
3832         player->block_delay_adjustment = field_player->block_delay_adjustment;
3833         */
3834
3835         StorePlayer[jx][jy] = field_player->element_nr;
3836
3837         field_player->jx = field_player->last_jx = jx;
3838         field_player->jy = field_player->last_jy = jy;
3839
3840         if (local_player == player)
3841           local_player = field_player;
3842
3843         map_player_action[field_player->index_nr] = i;
3844
3845         field_player->mapped = TRUE;
3846
3847 #if DEBUG_INIT_PLAYER
3848         if (options.debug)
3849           printf("- map_player_action[%d] == %d\n",
3850                  field_player->index_nr + 1, i + 1);
3851 #endif
3852       }
3853     }
3854
3855     if (player->connected && player->present)
3856       player->mapped = TRUE;
3857   }
3858
3859 #if DEBUG_INIT_PLAYER
3860   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3861 #endif
3862
3863 #else
3864
3865   // check if any connected player was not found in playfield
3866   for (i = 0; i < MAX_PLAYERS; i++)
3867   {
3868     struct PlayerInfo *player = &stored_player[i];
3869
3870     if (player->connected && !player->present)
3871     {
3872       for (j = 0; j < MAX_PLAYERS; j++)
3873       {
3874         struct PlayerInfo *field_player = &stored_player[j];
3875         int jx = field_player->jx, jy = field_player->jy;
3876
3877         // assign first free player found that is present in the playfield
3878         if (field_player->present && !field_player->connected)
3879         {
3880           player->present = TRUE;
3881           player->active = TRUE;
3882
3883           field_player->present = FALSE;
3884           field_player->active = FALSE;
3885
3886           player->initial_element = field_player->initial_element;
3887           player->artwork_element = field_player->artwork_element;
3888
3889           player->block_last_field       = field_player->block_last_field;
3890           player->block_delay_adjustment = field_player->block_delay_adjustment;
3891
3892           StorePlayer[jx][jy] = player->element_nr;
3893
3894           player->jx = player->last_jx = jx;
3895           player->jy = player->last_jy = jy;
3896
3897           break;
3898         }
3899       }
3900     }
3901   }
3902 #endif
3903
3904 #if 0
3905   printf("::: local_player->present == %d\n", local_player->present);
3906 #endif
3907
3908   // set focus to local player for network games, else to all players
3909   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3910   game.centered_player_nr_next = game.centered_player_nr;
3911   game.set_centered_player = FALSE;
3912
3913   if (network_playing && tape.recording)
3914   {
3915     // store client dependent player focus when recording network games
3916     tape.centered_player_nr_next = game.centered_player_nr_next;
3917     tape.set_centered_player = TRUE;
3918   }
3919
3920   if (tape.playing)
3921   {
3922     // when playing a tape, eliminate all players who do not participate
3923
3924 #if USE_NEW_PLAYER_ASSIGNMENTS
3925
3926     if (!game.team_mode)
3927     {
3928       for (i = 0; i < MAX_PLAYERS; i++)
3929       {
3930         if (stored_player[i].active &&
3931             !tape.player_participates[map_player_action[i]])
3932         {
3933           struct PlayerInfo *player = &stored_player[i];
3934           int jx = player->jx, jy = player->jy;
3935
3936 #if DEBUG_INIT_PLAYER
3937           if (options.debug)
3938             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3939 #endif
3940
3941           player->active = FALSE;
3942           StorePlayer[jx][jy] = 0;
3943           Feld[jx][jy] = EL_EMPTY;
3944         }
3945       }
3946     }
3947
3948 #else
3949
3950     for (i = 0; i < MAX_PLAYERS; i++)
3951     {
3952       if (stored_player[i].active &&
3953           !tape.player_participates[i])
3954       {
3955         struct PlayerInfo *player = &stored_player[i];
3956         int jx = player->jx, jy = player->jy;
3957
3958         player->active = FALSE;
3959         StorePlayer[jx][jy] = 0;
3960         Feld[jx][jy] = EL_EMPTY;
3961       }
3962     }
3963 #endif
3964   }
3965   else if (!network.enabled && !game.team_mode)         // && !tape.playing
3966   {
3967     // when in single player mode, eliminate all but the local player
3968
3969     for (i = 0; i < MAX_PLAYERS; i++)
3970     {
3971       struct PlayerInfo *player = &stored_player[i];
3972
3973       if (player->active && player != local_player)
3974       {
3975         int jx = player->jx, jy = player->jy;
3976
3977         player->active = FALSE;
3978         player->present = FALSE;
3979
3980         StorePlayer[jx][jy] = 0;
3981         Feld[jx][jy] = EL_EMPTY;
3982       }
3983     }
3984   }
3985
3986   for (i = 0; i < MAX_PLAYERS; i++)
3987     if (stored_player[i].active)
3988       game.players_still_needed++;
3989
3990   if (level.solved_by_one_player)
3991     game.players_still_needed = 1;
3992
3993   // when recording the game, store which players take part in the game
3994   if (tape.recording)
3995   {
3996 #if USE_NEW_PLAYER_ASSIGNMENTS
3997     for (i = 0; i < MAX_PLAYERS; i++)
3998       if (stored_player[i].connected)
3999         tape.player_participates[i] = TRUE;
4000 #else
4001     for (i = 0; i < MAX_PLAYERS; i++)
4002       if (stored_player[i].active)
4003         tape.player_participates[i] = TRUE;
4004 #endif
4005   }
4006
4007 #if DEBUG_INIT_PLAYER
4008   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4009 #endif
4010
4011   if (BorderElement == EL_EMPTY)
4012   {
4013     SBX_Left = 0;
4014     SBX_Right = lev_fieldx - SCR_FIELDX;
4015     SBY_Upper = 0;
4016     SBY_Lower = lev_fieldy - SCR_FIELDY;
4017   }
4018   else
4019   {
4020     SBX_Left = -1;
4021     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4022     SBY_Upper = -1;
4023     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4024   }
4025
4026   if (full_lev_fieldx <= SCR_FIELDX)
4027     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4028   if (full_lev_fieldy <= SCR_FIELDY)
4029     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4030
4031   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4032     SBX_Left--;
4033   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4034     SBY_Upper--;
4035
4036   // if local player not found, look for custom element that might create
4037   // the player (make some assumptions about the right custom element)
4038   if (!local_player->present)
4039   {
4040     int start_x = 0, start_y = 0;
4041     int found_rating = 0;
4042     int found_element = EL_UNDEFINED;
4043     int player_nr = local_player->index_nr;
4044
4045     SCAN_PLAYFIELD(x, y)
4046     {
4047       int element = Feld[x][y];
4048       int content;
4049       int xx, yy;
4050       boolean is_player;
4051
4052       if (level.use_start_element[player_nr] &&
4053           level.start_element[player_nr] == element &&
4054           found_rating < 4)
4055       {
4056         start_x = x;
4057         start_y = y;
4058
4059         found_rating = 4;
4060         found_element = element;
4061       }
4062
4063       if (!IS_CUSTOM_ELEMENT(element))
4064         continue;
4065
4066       if (CAN_CHANGE(element))
4067       {
4068         for (i = 0; i < element_info[element].num_change_pages; i++)
4069         {
4070           // check for player created from custom element as single target
4071           content = element_info[element].change_page[i].target_element;
4072           is_player = ELEM_IS_PLAYER(content);
4073
4074           if (is_player && (found_rating < 3 ||
4075                             (found_rating == 3 && element < found_element)))
4076           {
4077             start_x = x;
4078             start_y = y;
4079
4080             found_rating = 3;
4081             found_element = element;
4082           }
4083         }
4084       }
4085
4086       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4087       {
4088         // check for player created from custom element as explosion content
4089         content = element_info[element].content.e[xx][yy];
4090         is_player = ELEM_IS_PLAYER(content);
4091
4092         if (is_player && (found_rating < 2 ||
4093                           (found_rating == 2 && element < found_element)))
4094         {
4095           start_x = x + xx - 1;
4096           start_y = y + yy - 1;
4097
4098           found_rating = 2;
4099           found_element = element;
4100         }
4101
4102         if (!CAN_CHANGE(element))
4103           continue;
4104
4105         for (i = 0; i < element_info[element].num_change_pages; i++)
4106         {
4107           // check for player created from custom element as extended target
4108           content =
4109             element_info[element].change_page[i].target_content.e[xx][yy];
4110
4111           is_player = ELEM_IS_PLAYER(content);
4112
4113           if (is_player && (found_rating < 1 ||
4114                             (found_rating == 1 && element < found_element)))
4115           {
4116             start_x = x + xx - 1;
4117             start_y = y + yy - 1;
4118
4119             found_rating = 1;
4120             found_element = element;
4121           }
4122         }
4123       }
4124     }
4125
4126     scroll_x = SCROLL_POSITION_X(start_x);
4127     scroll_y = SCROLL_POSITION_Y(start_y);
4128   }
4129   else
4130   {
4131     scroll_x = SCROLL_POSITION_X(local_player->jx);
4132     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4133   }
4134
4135   // !!! FIX THIS (START) !!!
4136   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4137   {
4138     InitGameEngine_EM();
4139   }
4140   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4141   {
4142     InitGameEngine_SP();
4143   }
4144   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4145   {
4146     InitGameEngine_MM();
4147   }
4148   else
4149   {
4150     DrawLevel(REDRAW_FIELD);
4151     DrawAllPlayers();
4152
4153     // after drawing the level, correct some elements
4154     if (game.timegate_time_left == 0)
4155       CloseAllOpenTimegates();
4156   }
4157
4158   // blit playfield from scroll buffer to normal back buffer for fading in
4159   BlitScreenToBitmap(backbuffer);
4160   // !!! FIX THIS (END) !!!
4161
4162   DrawMaskedBorder(fade_mask);
4163
4164   FadeIn(fade_mask);
4165
4166 #if 1
4167   // full screen redraw is required at this point in the following cases:
4168   // - special editor door undrawn when game was started from level editor
4169   // - drawing area (playfield) was changed and has to be removed completely
4170   redraw_mask = REDRAW_ALL;
4171   BackToFront();
4172 #endif
4173
4174   if (!game.restart_level)
4175   {
4176     // copy default game door content to main double buffer
4177
4178     // !!! CHECK AGAIN !!!
4179     SetPanelBackground();
4180     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4181     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4182   }
4183
4184   SetPanelBackground();
4185   SetDrawBackgroundMask(REDRAW_DOOR_1);
4186
4187   UpdateAndDisplayGameControlValues();
4188
4189   if (!game.restart_level)
4190   {
4191     UnmapGameButtons();
4192     UnmapTapeButtons();
4193
4194     FreeGameButtons();
4195     CreateGameButtons();
4196
4197     MapGameButtons();
4198     MapTapeButtons();
4199
4200     // copy actual game door content to door double buffer for OpenDoor()
4201     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4202
4203     OpenDoor(DOOR_OPEN_ALL);
4204
4205     KeyboardAutoRepeatOffUnlessAutoplay();
4206
4207 #if DEBUG_INIT_PLAYER
4208     DebugPrintPlayerStatus("Player status (final)");
4209 #endif
4210   }
4211
4212   UnmapAllGadgets();
4213
4214   MapGameButtons();
4215   MapTapeButtons();
4216
4217   if (!game.restart_level && !tape.playing)
4218   {
4219     LevelStats_incPlayed(level_nr);
4220
4221     SaveLevelSetup_SeriesInfo();
4222   }
4223
4224   game.restart_level = FALSE;
4225   game.restart_game_message = NULL;
4226   game.request_active = FALSE;
4227
4228   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4229     InitGameActions_MM();
4230
4231   SaveEngineSnapshotToListInitial();
4232
4233   if (!game.restart_level)
4234   {
4235     PlaySound(SND_GAME_STARTING);
4236
4237     if (setup.sound_music)
4238       PlayLevelMusic();
4239   }
4240 }
4241
4242 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4243                         int actual_player_x, int actual_player_y)
4244 {
4245   // this is used for non-R'n'D game engines to update certain engine values
4246
4247   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4248   {
4249     actual_player_x = correctLevelPosX_EM(actual_player_x);
4250     actual_player_y = correctLevelPosY_EM(actual_player_y);
4251   }
4252
4253   // needed to determine if sounds are played within the visible screen area
4254   scroll_x = actual_scroll_x;
4255   scroll_y = actual_scroll_y;
4256
4257   // needed to get player position for "follow finger" playing input method
4258   local_player->jx = actual_player_x;
4259   local_player->jy = actual_player_y;
4260 }
4261
4262 void InitMovDir(int x, int y)
4263 {
4264   int i, element = Feld[x][y];
4265   static int xy[4][2] =
4266   {
4267     {  0, +1 },
4268     { +1,  0 },
4269     {  0, -1 },
4270     { -1,  0 }
4271   };
4272   static int direction[3][4] =
4273   {
4274     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4275     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4276     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4277   };
4278
4279   switch (element)
4280   {
4281     case EL_BUG_RIGHT:
4282     case EL_BUG_UP:
4283     case EL_BUG_LEFT:
4284     case EL_BUG_DOWN:
4285       Feld[x][y] = EL_BUG;
4286       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4287       break;
4288
4289     case EL_SPACESHIP_RIGHT:
4290     case EL_SPACESHIP_UP:
4291     case EL_SPACESHIP_LEFT:
4292     case EL_SPACESHIP_DOWN:
4293       Feld[x][y] = EL_SPACESHIP;
4294       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4295       break;
4296
4297     case EL_BD_BUTTERFLY_RIGHT:
4298     case EL_BD_BUTTERFLY_UP:
4299     case EL_BD_BUTTERFLY_LEFT:
4300     case EL_BD_BUTTERFLY_DOWN:
4301       Feld[x][y] = EL_BD_BUTTERFLY;
4302       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4303       break;
4304
4305     case EL_BD_FIREFLY_RIGHT:
4306     case EL_BD_FIREFLY_UP:
4307     case EL_BD_FIREFLY_LEFT:
4308     case EL_BD_FIREFLY_DOWN:
4309       Feld[x][y] = EL_BD_FIREFLY;
4310       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4311       break;
4312
4313     case EL_PACMAN_RIGHT:
4314     case EL_PACMAN_UP:
4315     case EL_PACMAN_LEFT:
4316     case EL_PACMAN_DOWN:
4317       Feld[x][y] = EL_PACMAN;
4318       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4319       break;
4320
4321     case EL_YAMYAM_LEFT:
4322     case EL_YAMYAM_RIGHT:
4323     case EL_YAMYAM_UP:
4324     case EL_YAMYAM_DOWN:
4325       Feld[x][y] = EL_YAMYAM;
4326       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4327       break;
4328
4329     case EL_SP_SNIKSNAK:
4330       MovDir[x][y] = MV_UP;
4331       break;
4332
4333     case EL_SP_ELECTRON:
4334       MovDir[x][y] = MV_LEFT;
4335       break;
4336
4337     case EL_MOLE_LEFT:
4338     case EL_MOLE_RIGHT:
4339     case EL_MOLE_UP:
4340     case EL_MOLE_DOWN:
4341       Feld[x][y] = EL_MOLE;
4342       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4343       break;
4344
4345     default:
4346       if (IS_CUSTOM_ELEMENT(element))
4347       {
4348         struct ElementInfo *ei = &element_info[element];
4349         int move_direction_initial = ei->move_direction_initial;
4350         int move_pattern = ei->move_pattern;
4351
4352         if (move_direction_initial == MV_START_PREVIOUS)
4353         {
4354           if (MovDir[x][y] != MV_NONE)
4355             return;
4356
4357           move_direction_initial = MV_START_AUTOMATIC;
4358         }
4359
4360         if (move_direction_initial == MV_START_RANDOM)
4361           MovDir[x][y] = 1 << RND(4);
4362         else if (move_direction_initial & MV_ANY_DIRECTION)
4363           MovDir[x][y] = move_direction_initial;
4364         else if (move_pattern == MV_ALL_DIRECTIONS ||
4365                  move_pattern == MV_TURNING_LEFT ||
4366                  move_pattern == MV_TURNING_RIGHT ||
4367                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4368                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4369                  move_pattern == MV_TURNING_RANDOM)
4370           MovDir[x][y] = 1 << RND(4);
4371         else if (move_pattern == MV_HORIZONTAL)
4372           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4373         else if (move_pattern == MV_VERTICAL)
4374           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4375         else if (move_pattern & MV_ANY_DIRECTION)
4376           MovDir[x][y] = element_info[element].move_pattern;
4377         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4378                  move_pattern == MV_ALONG_RIGHT_SIDE)
4379         {
4380           // use random direction as default start direction
4381           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4382             MovDir[x][y] = 1 << RND(4);
4383
4384           for (i = 0; i < NUM_DIRECTIONS; i++)
4385           {
4386             int x1 = x + xy[i][0];
4387             int y1 = y + xy[i][1];
4388
4389             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4390             {
4391               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4392                 MovDir[x][y] = direction[0][i];
4393               else
4394                 MovDir[x][y] = direction[1][i];
4395
4396               break;
4397             }
4398           }
4399         }                
4400       }
4401       else
4402       {
4403         MovDir[x][y] = 1 << RND(4);
4404
4405         if (element != EL_BUG &&
4406             element != EL_SPACESHIP &&
4407             element != EL_BD_BUTTERFLY &&
4408             element != EL_BD_FIREFLY)
4409           break;
4410
4411         for (i = 0; i < NUM_DIRECTIONS; i++)
4412         {
4413           int x1 = x + xy[i][0];
4414           int y1 = y + xy[i][1];
4415
4416           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4417           {
4418             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4419             {
4420               MovDir[x][y] = direction[0][i];
4421               break;
4422             }
4423             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4424                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4425             {
4426               MovDir[x][y] = direction[1][i];
4427               break;
4428             }
4429           }
4430         }
4431       }
4432       break;
4433   }
4434
4435   GfxDir[x][y] = MovDir[x][y];
4436 }
4437
4438 void InitAmoebaNr(int x, int y)
4439 {
4440   int i;
4441   int group_nr = AmoebeNachbarNr(x, y);
4442
4443   if (group_nr == 0)
4444   {
4445     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4446     {
4447       if (AmoebaCnt[i] == 0)
4448       {
4449         group_nr = i;
4450         break;
4451       }
4452     }
4453   }
4454
4455   AmoebaNr[x][y] = group_nr;
4456   AmoebaCnt[group_nr]++;
4457   AmoebaCnt2[group_nr]++;
4458 }
4459
4460 static void LevelSolved(void)
4461 {
4462   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4463       game.players_still_needed > 0)
4464     return;
4465
4466   game.LevelSolved = TRUE;
4467   game.GameOver = TRUE;
4468
4469   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4470                       level.native_em_level->lev->score :
4471                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4472                       game_mm.score :
4473                       game.score);
4474   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4475                        MM_HEALTH(game_mm.laser_overload_value) :
4476                        game.health);
4477
4478   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4479   game.LevelSolved_CountingScore = game.score_final;
4480   game.LevelSolved_CountingHealth = game.health_final;
4481 }
4482
4483 void GameWon(void)
4484 {
4485   static int time_count_steps;
4486   static int time, time_final;
4487   static int score, score_final;
4488   static int health, health_final;
4489   static int game_over_delay_1 = 0;
4490   static int game_over_delay_2 = 0;
4491   static int game_over_delay_3 = 0;
4492   int game_over_delay_value_1 = 50;
4493   int game_over_delay_value_2 = 25;
4494   int game_over_delay_value_3 = 50;
4495
4496   if (!game.LevelSolved_GameWon)
4497   {
4498     int i;
4499
4500     // do not start end game actions before the player stops moving (to exit)
4501     if (local_player->MovPos)
4502       return;
4503
4504     game.LevelSolved_GameWon = TRUE;
4505     game.LevelSolved_SaveTape = tape.recording;
4506     game.LevelSolved_SaveScore = !tape.playing;
4507
4508     if (!tape.playing)
4509     {
4510       LevelStats_incSolved(level_nr);
4511
4512       SaveLevelSetup_SeriesInfo();
4513     }
4514
4515     if (tape.auto_play)         // tape might already be stopped here
4516       tape.auto_play_level_solved = TRUE;
4517
4518     TapeStop();
4519
4520     game_over_delay_1 = 0;
4521     game_over_delay_2 = 0;
4522     game_over_delay_3 = game_over_delay_value_3;
4523
4524     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4525     score = score_final = game.score_final;
4526     health = health_final = game.health_final;
4527
4528     if (level.score[SC_TIME_BONUS] > 0)
4529     {
4530       if (TimeLeft > 0)
4531       {
4532         time_final = 0;
4533         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4534       }
4535       else if (game.no_time_limit && TimePlayed < 999)
4536       {
4537         time_final = 999;
4538         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4539       }
4540
4541       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4542
4543       game_over_delay_1 = game_over_delay_value_1;
4544
4545       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4546       {
4547         health_final = 0;
4548         score_final += health * level.score[SC_TIME_BONUS];
4549
4550         game_over_delay_2 = game_over_delay_value_2;
4551       }
4552
4553       game.score_final = score_final;
4554       game.health_final = health_final;
4555     }
4556
4557     if (level_editor_test_game)
4558     {
4559       time = time_final;
4560       score = score_final;
4561
4562       game.LevelSolved_CountingTime = time;
4563       game.LevelSolved_CountingScore = score;
4564
4565       game_panel_controls[GAME_PANEL_TIME].value = time;
4566       game_panel_controls[GAME_PANEL_SCORE].value = score;
4567
4568       DisplayGameControlValues();
4569     }
4570
4571     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4572     {
4573       if (ExitX >= 0 && ExitY >= 0)     // local player has left the level
4574       {
4575         // close exit door after last player
4576         if ((game.all_players_gone &&
4577              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4578               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4579               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4580             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4581             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4582         {
4583           int element = Feld[ExitX][ExitY];
4584
4585           Feld[ExitX][ExitY] =
4586             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4587              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4588              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4589              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4590              EL_EM_STEEL_EXIT_CLOSING);
4591
4592           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4593         }
4594
4595         // player disappears
4596         DrawLevelField(ExitX, ExitY);
4597       }
4598
4599       for (i = 0; i < MAX_PLAYERS; i++)
4600       {
4601         struct PlayerInfo *player = &stored_player[i];
4602
4603         if (player->present)
4604         {
4605           RemovePlayer(player);
4606
4607           // player disappears
4608           DrawLevelField(player->jx, player->jy);
4609         }
4610       }
4611     }
4612
4613     PlaySound(SND_GAME_WINNING);
4614   }
4615
4616   if (game_over_delay_1 > 0)
4617   {
4618     game_over_delay_1--;
4619
4620     return;
4621   }
4622
4623   if (time != time_final)
4624   {
4625     int time_to_go = ABS(time_final - time);
4626     int time_count_dir = (time < time_final ? +1 : -1);
4627
4628     if (time_to_go < time_count_steps)
4629       time_count_steps = 1;
4630
4631     time  += time_count_steps * time_count_dir;
4632     score += time_count_steps * level.score[SC_TIME_BONUS];
4633
4634     game.LevelSolved_CountingTime = time;
4635     game.LevelSolved_CountingScore = score;
4636
4637     game_panel_controls[GAME_PANEL_TIME].value = time;
4638     game_panel_controls[GAME_PANEL_SCORE].value = score;
4639
4640     DisplayGameControlValues();
4641
4642     if (time == time_final)
4643       StopSound(SND_GAME_LEVELTIME_BONUS);
4644     else if (setup.sound_loops)
4645       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4646     else
4647       PlaySound(SND_GAME_LEVELTIME_BONUS);
4648
4649     return;
4650   }
4651
4652   if (game_over_delay_2 > 0)
4653   {
4654     game_over_delay_2--;
4655
4656     return;
4657   }
4658
4659   if (health != health_final)
4660   {
4661     int health_count_dir = (health < health_final ? +1 : -1);
4662
4663     health += health_count_dir;
4664     score  += level.score[SC_TIME_BONUS];
4665
4666     game.LevelSolved_CountingHealth = health;
4667     game.LevelSolved_CountingScore = score;
4668
4669     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4670     game_panel_controls[GAME_PANEL_SCORE].value = score;
4671
4672     DisplayGameControlValues();
4673
4674     if (health == health_final)
4675       StopSound(SND_GAME_LEVELTIME_BONUS);
4676     else if (setup.sound_loops)
4677       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4678     else
4679       PlaySound(SND_GAME_LEVELTIME_BONUS);
4680
4681     return;
4682   }
4683
4684   game.panel.active = FALSE;
4685
4686   if (game_over_delay_3 > 0)
4687   {
4688     game_over_delay_3--;
4689
4690     return;
4691   }
4692
4693   GameEnd();
4694 }
4695
4696 void GameEnd(void)
4697 {
4698   // used instead of "level_nr" (needed for network games)
4699   int last_level_nr = levelset.level_nr;
4700   int hi_pos;
4701
4702   game.LevelSolved_GameEnd = TRUE;
4703
4704   if (game.LevelSolved_SaveTape)
4705   {
4706     // make sure that request dialog to save tape does not open door again
4707     if (!global.use_envelope_request)
4708       CloseDoor(DOOR_CLOSE_1);
4709
4710     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4711   }
4712
4713   // if no tape is to be saved, close both doors simultaneously
4714   CloseDoor(DOOR_CLOSE_ALL);
4715
4716   if (level_editor_test_game)
4717   {
4718     SetGameStatus(GAME_MODE_MAIN);
4719
4720     DrawMainMenu();
4721
4722     return;
4723   }
4724
4725   if (!game.LevelSolved_SaveScore)
4726   {
4727     SetGameStatus(GAME_MODE_MAIN);
4728
4729     DrawMainMenu();
4730
4731     return;
4732   }
4733
4734   if (level_nr == leveldir_current->handicap_level)
4735   {
4736     leveldir_current->handicap_level++;
4737
4738     SaveLevelSetup_SeriesInfo();
4739   }
4740
4741   if (setup.increment_levels &&
4742       level_nr < leveldir_current->last_level &&
4743       !network_playing)
4744   {
4745     level_nr++;         // advance to next level
4746     TapeErase();        // start with empty tape
4747
4748     if (setup.auto_play_next_level)
4749     {
4750       LoadLevel(level_nr);
4751
4752       SaveLevelSetup_SeriesInfo();
4753     }
4754   }
4755
4756   hi_pos = NewHiScore(last_level_nr);
4757
4758   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4759   {
4760     SetGameStatus(GAME_MODE_SCORES);
4761
4762     DrawHallOfFame(last_level_nr, hi_pos);
4763   }
4764   else if (setup.auto_play_next_level && setup.increment_levels &&
4765            last_level_nr < leveldir_current->last_level &&
4766            !network_playing)
4767   {
4768     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4769   }
4770   else
4771   {
4772     SetGameStatus(GAME_MODE_MAIN);
4773
4774     DrawMainMenu();
4775   }
4776 }
4777
4778 int NewHiScore(int level_nr)
4779 {
4780   int k, l;
4781   int position = -1;
4782   boolean one_score_entry_per_name = !program.many_scores_per_name;
4783
4784   LoadScore(level_nr);
4785
4786   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4787       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4788     return -1;
4789
4790   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4791   {
4792     if (game.score_final > highscore[k].Score)
4793     {
4794       // player has made it to the hall of fame
4795
4796       if (k < MAX_SCORE_ENTRIES - 1)
4797       {
4798         int m = MAX_SCORE_ENTRIES - 1;
4799
4800         if (one_score_entry_per_name)
4801         {
4802           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4803             if (strEqual(setup.player_name, highscore[l].Name))
4804               m = l;
4805
4806           if (m == k)   // player's new highscore overwrites his old one
4807             goto put_into_list;
4808         }
4809
4810         for (l = m; l > k; l--)
4811         {
4812           strcpy(highscore[l].Name, highscore[l - 1].Name);
4813           highscore[l].Score = highscore[l - 1].Score;
4814         }
4815       }
4816
4817       put_into_list:
4818
4819       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4820       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4821       highscore[k].Score = game.score_final;
4822       position = k;
4823
4824       break;
4825     }
4826     else if (one_score_entry_per_name &&
4827              !strncmp(setup.player_name, highscore[k].Name,
4828                       MAX_PLAYER_NAME_LEN))
4829       break;    // player already there with a higher score
4830   }
4831
4832   if (position >= 0) 
4833     SaveScore(level_nr);
4834
4835   return position;
4836 }
4837
4838 static int getElementMoveStepsizeExt(int x, int y, int direction)
4839 {
4840   int element = Feld[x][y];
4841   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4842   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4843   int horiz_move = (dx != 0);
4844   int sign = (horiz_move ? dx : dy);
4845   int step = sign * element_info[element].move_stepsize;
4846
4847   // special values for move stepsize for spring and things on conveyor belt
4848   if (horiz_move)
4849   {
4850     if (CAN_FALL(element) &&
4851         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4852       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4853     else if (element == EL_SPRING)
4854       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4855   }
4856
4857   return step;
4858 }
4859
4860 static int getElementMoveStepsize(int x, int y)
4861 {
4862   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4863 }
4864
4865 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4866 {
4867   if (player->GfxAction != action || player->GfxDir != dir)
4868   {
4869     player->GfxAction = action;
4870     player->GfxDir = dir;
4871     player->Frame = 0;
4872     player->StepFrame = 0;
4873   }
4874 }
4875
4876 static void ResetGfxFrame(int x, int y)
4877 {
4878   // profiling showed that "autotest" spends 10~20% of its time in this function
4879   if (DrawingDeactivatedField())
4880     return;
4881
4882   int element = Feld[x][y];
4883   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4884
4885   if (graphic_info[graphic].anim_global_sync)
4886     GfxFrame[x][y] = FrameCounter;
4887   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4888     GfxFrame[x][y] = CustomValue[x][y];
4889   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4890     GfxFrame[x][y] = element_info[element].collect_score;
4891   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4892     GfxFrame[x][y] = ChangeDelay[x][y];
4893 }
4894
4895 static void ResetGfxAnimation(int x, int y)
4896 {
4897   GfxAction[x][y] = ACTION_DEFAULT;
4898   GfxDir[x][y] = MovDir[x][y];
4899   GfxFrame[x][y] = 0;
4900
4901   ResetGfxFrame(x, y);
4902 }
4903
4904 static void ResetRandomAnimationValue(int x, int y)
4905 {
4906   GfxRandom[x][y] = INIT_GFX_RANDOM();
4907 }
4908
4909 static void InitMovingField(int x, int y, int direction)
4910 {
4911   int element = Feld[x][y];
4912   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4913   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4914   int newx = x + dx;
4915   int newy = y + dy;
4916   boolean is_moving_before, is_moving_after;
4917
4918   // check if element was/is moving or being moved before/after mode change
4919   is_moving_before = (WasJustMoving[x][y] != 0);
4920   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4921
4922   // reset animation only for moving elements which change direction of moving
4923   // or which just started or stopped moving
4924   // (else CEs with property "can move" / "not moving" are reset each frame)
4925   if (is_moving_before != is_moving_after ||
4926       direction != MovDir[x][y])
4927     ResetGfxAnimation(x, y);
4928
4929   MovDir[x][y] = direction;
4930   GfxDir[x][y] = direction;
4931
4932   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4933                      direction == MV_DOWN && CAN_FALL(element) ?
4934                      ACTION_FALLING : ACTION_MOVING);
4935
4936   // this is needed for CEs with property "can move" / "not moving"
4937
4938   if (is_moving_after)
4939   {
4940     if (Feld[newx][newy] == EL_EMPTY)
4941       Feld[newx][newy] = EL_BLOCKED;
4942
4943     MovDir[newx][newy] = MovDir[x][y];
4944
4945     CustomValue[newx][newy] = CustomValue[x][y];
4946
4947     GfxFrame[newx][newy] = GfxFrame[x][y];
4948     GfxRandom[newx][newy] = GfxRandom[x][y];
4949     GfxAction[newx][newy] = GfxAction[x][y];
4950     GfxDir[newx][newy] = GfxDir[x][y];
4951   }
4952 }
4953
4954 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4955 {
4956   int direction = MovDir[x][y];
4957   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4958   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4959
4960   *goes_to_x = newx;
4961   *goes_to_y = newy;
4962 }
4963
4964 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4965 {
4966   int oldx = x, oldy = y;
4967   int direction = MovDir[x][y];
4968
4969   if (direction == MV_LEFT)
4970     oldx++;
4971   else if (direction == MV_RIGHT)
4972     oldx--;
4973   else if (direction == MV_UP)
4974     oldy++;
4975   else if (direction == MV_DOWN)
4976     oldy--;
4977
4978   *comes_from_x = oldx;
4979   *comes_from_y = oldy;
4980 }
4981
4982 static int MovingOrBlocked2Element(int x, int y)
4983 {
4984   int element = Feld[x][y];
4985
4986   if (element == EL_BLOCKED)
4987   {
4988     int oldx, oldy;
4989
4990     Blocked2Moving(x, y, &oldx, &oldy);
4991     return Feld[oldx][oldy];
4992   }
4993   else
4994     return element;
4995 }
4996
4997 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4998 {
4999   // like MovingOrBlocked2Element(), but if element is moving
5000   // and (x,y) is the field the moving element is just leaving,
5001   // return EL_BLOCKED instead of the element value
5002   int element = Feld[x][y];
5003
5004   if (IS_MOVING(x, y))
5005   {
5006     if (element == EL_BLOCKED)
5007     {
5008       int oldx, oldy;
5009
5010       Blocked2Moving(x, y, &oldx, &oldy);
5011       return Feld[oldx][oldy];
5012     }
5013     else
5014       return EL_BLOCKED;
5015   }
5016   else
5017     return element;
5018 }
5019
5020 static void RemoveField(int x, int y)
5021 {
5022   Feld[x][y] = EL_EMPTY;
5023
5024   MovPos[x][y] = 0;
5025   MovDir[x][y] = 0;
5026   MovDelay[x][y] = 0;
5027
5028   CustomValue[x][y] = 0;
5029
5030   AmoebaNr[x][y] = 0;
5031   ChangeDelay[x][y] = 0;
5032   ChangePage[x][y] = -1;
5033   Pushed[x][y] = FALSE;
5034
5035   GfxElement[x][y] = EL_UNDEFINED;
5036   GfxAction[x][y] = ACTION_DEFAULT;
5037   GfxDir[x][y] = MV_NONE;
5038 }
5039
5040 static void RemoveMovingField(int x, int y)
5041 {
5042   int oldx = x, oldy = y, newx = x, newy = y;
5043   int element = Feld[x][y];
5044   int next_element = EL_UNDEFINED;
5045
5046   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5047     return;
5048
5049   if (IS_MOVING(x, y))
5050   {
5051     Moving2Blocked(x, y, &newx, &newy);
5052
5053     if (Feld[newx][newy] != EL_BLOCKED)
5054     {
5055       // element is moving, but target field is not free (blocked), but
5056       // already occupied by something different (example: acid pool);
5057       // in this case, only remove the moving field, but not the target
5058
5059       RemoveField(oldx, oldy);
5060
5061       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5062
5063       TEST_DrawLevelField(oldx, oldy);
5064
5065       return;
5066     }
5067   }
5068   else if (element == EL_BLOCKED)
5069   {
5070     Blocked2Moving(x, y, &oldx, &oldy);
5071     if (!IS_MOVING(oldx, oldy))
5072       return;
5073   }
5074
5075   if (element == EL_BLOCKED &&
5076       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5077        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5078        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5079        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5080        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5081        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5082     next_element = get_next_element(Feld[oldx][oldy]);
5083
5084   RemoveField(oldx, oldy);
5085   RemoveField(newx, newy);
5086
5087   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5088
5089   if (next_element != EL_UNDEFINED)
5090     Feld[oldx][oldy] = next_element;
5091
5092   TEST_DrawLevelField(oldx, oldy);
5093   TEST_DrawLevelField(newx, newy);
5094 }
5095
5096 void DrawDynamite(int x, int y)
5097 {
5098   int sx = SCREENX(x), sy = SCREENY(y);
5099   int graphic = el2img(Feld[x][y]);
5100   int frame;
5101
5102   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5103     return;
5104
5105   if (IS_WALKABLE_INSIDE(Back[x][y]))
5106     return;
5107
5108   if (Back[x][y])
5109     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5110   else if (Store[x][y])
5111     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5112
5113   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5114
5115   if (Back[x][y] || Store[x][y])
5116     DrawGraphicThruMask(sx, sy, graphic, frame);
5117   else
5118     DrawGraphic(sx, sy, graphic, frame);
5119 }
5120
5121 static void CheckDynamite(int x, int y)
5122 {
5123   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5124   {
5125     MovDelay[x][y]--;
5126
5127     if (MovDelay[x][y] != 0)
5128     {
5129       DrawDynamite(x, y);
5130       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5131
5132       return;
5133     }
5134   }
5135
5136   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5137
5138   Bang(x, y);
5139 }
5140
5141 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5142 {
5143   boolean num_checked_players = 0;
5144   int i;
5145
5146   for (i = 0; i < MAX_PLAYERS; i++)
5147   {
5148     if (stored_player[i].active)
5149     {
5150       int sx = stored_player[i].jx;
5151       int sy = stored_player[i].jy;
5152
5153       if (num_checked_players == 0)
5154       {
5155         *sx1 = *sx2 = sx;
5156         *sy1 = *sy2 = sy;
5157       }
5158       else
5159       {
5160         *sx1 = MIN(*sx1, sx);
5161         *sy1 = MIN(*sy1, sy);
5162         *sx2 = MAX(*sx2, sx);
5163         *sy2 = MAX(*sy2, sy);
5164       }
5165
5166       num_checked_players++;
5167     }
5168   }
5169 }
5170
5171 static boolean checkIfAllPlayersFitToScreen_RND(void)
5172 {
5173   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5174
5175   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5176
5177   return (sx2 - sx1 < SCR_FIELDX &&
5178           sy2 - sy1 < SCR_FIELDY);
5179 }
5180
5181 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5182 {
5183   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5184
5185   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5186
5187   *sx = (sx1 + sx2) / 2;
5188   *sy = (sy1 + sy2) / 2;
5189 }
5190
5191 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5192                                boolean center_screen, boolean quick_relocation)
5193 {
5194   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5195   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5196   boolean no_delay = (tape.warp_forward);
5197   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5198   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5199   int new_scroll_x, new_scroll_y;
5200
5201   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5202   {
5203     // case 1: quick relocation inside visible screen (without scrolling)
5204
5205     RedrawPlayfield();
5206
5207     return;
5208   }
5209
5210   if (!level.shifted_relocation || center_screen)
5211   {
5212     // relocation _with_ centering of screen
5213
5214     new_scroll_x = SCROLL_POSITION_X(x);
5215     new_scroll_y = SCROLL_POSITION_Y(y);
5216   }
5217   else
5218   {
5219     // relocation _without_ centering of screen
5220
5221     int center_scroll_x = SCROLL_POSITION_X(old_x);
5222     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5223     int offset_x = x + (scroll_x - center_scroll_x);
5224     int offset_y = y + (scroll_y - center_scroll_y);
5225
5226     // for new screen position, apply previous offset to center position
5227     new_scroll_x = SCROLL_POSITION_X(offset_x);
5228     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5229   }
5230
5231   if (quick_relocation)
5232   {
5233     // case 2: quick relocation (redraw without visible scrolling)
5234
5235     scroll_x = new_scroll_x;
5236     scroll_y = new_scroll_y;
5237
5238     RedrawPlayfield();
5239
5240     return;
5241   }
5242
5243   // case 3: visible relocation (with scrolling to new position)
5244
5245   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5246
5247   SetVideoFrameDelay(wait_delay_value);
5248
5249   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5250   {
5251     int dx = 0, dy = 0;
5252     int fx = FX, fy = FY;
5253
5254     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5255     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5256
5257     if (dx == 0 && dy == 0)             // no scrolling needed at all
5258       break;
5259
5260     scroll_x -= dx;
5261     scroll_y -= dy;
5262
5263     fx += dx * TILEX / 2;
5264     fy += dy * TILEY / 2;
5265
5266     ScrollLevel(dx, dy);
5267     DrawAllPlayers();
5268
5269     // scroll in two steps of half tile size to make things smoother
5270     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5271
5272     // scroll second step to align at full tile size
5273     BlitScreenToBitmap(window);
5274   }
5275
5276   DrawAllPlayers();
5277   BackToFront();
5278
5279   SetVideoFrameDelay(frame_delay_value_old);
5280 }
5281
5282 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5283 {
5284   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5285   int player_nr = GET_PLAYER_NR(el_player);
5286   struct PlayerInfo *player = &stored_player[player_nr];
5287   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5288   boolean no_delay = (tape.warp_forward);
5289   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5290   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5291   int old_jx = player->jx;
5292   int old_jy = player->jy;
5293   int old_element = Feld[old_jx][old_jy];
5294   int element = Feld[jx][jy];
5295   boolean player_relocated = (old_jx != jx || old_jy != jy);
5296
5297   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5298   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5299   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5300   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5301   int leave_side_horiz = move_dir_horiz;
5302   int leave_side_vert  = move_dir_vert;
5303   int enter_side = enter_side_horiz | enter_side_vert;
5304   int leave_side = leave_side_horiz | leave_side_vert;
5305
5306   if (player->buried)           // do not reanimate dead player
5307     return;
5308
5309   if (!player_relocated)        // no need to relocate the player
5310     return;
5311
5312   if (IS_PLAYER(jx, jy))        // player already placed at new position
5313   {
5314     RemoveField(jx, jy);        // temporarily remove newly placed player
5315     DrawLevelField(jx, jy);
5316   }
5317
5318   if (player->present)
5319   {
5320     while (player->MovPos)
5321     {
5322       ScrollPlayer(player, SCROLL_GO_ON);
5323       ScrollScreen(NULL, SCROLL_GO_ON);
5324
5325       AdvanceFrameAndPlayerCounters(player->index_nr);
5326
5327       DrawPlayer(player);
5328
5329       BackToFront_WithFrameDelay(wait_delay_value);
5330     }
5331
5332     DrawPlayer(player);         // needed here only to cleanup last field
5333     DrawLevelField(player->jx, player->jy);     // remove player graphic
5334
5335     player->is_moving = FALSE;
5336   }
5337
5338   if (IS_CUSTOM_ELEMENT(old_element))
5339     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5340                                CE_LEFT_BY_PLAYER,
5341                                player->index_bit, leave_side);
5342
5343   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5344                                       CE_PLAYER_LEAVES_X,
5345                                       player->index_bit, leave_side);
5346
5347   Feld[jx][jy] = el_player;
5348   InitPlayerField(jx, jy, el_player, TRUE);
5349
5350   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5351      possible that the relocation target field did not contain a player element,
5352      but a walkable element, to which the new player was relocated -- in this
5353      case, restore that (already initialized!) element on the player field */
5354   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5355   {
5356     Feld[jx][jy] = element;     // restore previously existing element
5357   }
5358
5359   // only visually relocate centered player
5360   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5361                      FALSE, level.instant_relocation);
5362
5363   TestIfPlayerTouchesBadThing(jx, jy);
5364   TestIfPlayerTouchesCustomElement(jx, jy);
5365
5366   if (IS_CUSTOM_ELEMENT(element))
5367     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5368                                player->index_bit, enter_side);
5369
5370   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5371                                       player->index_bit, enter_side);
5372
5373   if (player->is_switching)
5374   {
5375     /* ensure that relocation while still switching an element does not cause
5376        a new element to be treated as also switched directly after relocation
5377        (this is important for teleporter switches that teleport the player to
5378        a place where another teleporter switch is in the same direction, which
5379        would then incorrectly be treated as immediately switched before the
5380        direction key that caused the switch was released) */
5381
5382     player->switch_x += jx - old_jx;
5383     player->switch_y += jy - old_jy;
5384   }
5385 }
5386
5387 static void Explode(int ex, int ey, int phase, int mode)
5388 {
5389   int x, y;
5390   int last_phase;
5391   int border_element;
5392
5393   // !!! eliminate this variable !!!
5394   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5395
5396   if (game.explosions_delayed)
5397   {
5398     ExplodeField[ex][ey] = mode;
5399     return;
5400   }
5401
5402   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5403   {
5404     int center_element = Feld[ex][ey];
5405     int artwork_element, explosion_element;     // set these values later
5406
5407     // remove things displayed in background while burning dynamite
5408     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5409       Back[ex][ey] = 0;
5410
5411     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5412     {
5413       // put moving element to center field (and let it explode there)
5414       center_element = MovingOrBlocked2Element(ex, ey);
5415       RemoveMovingField(ex, ey);
5416       Feld[ex][ey] = center_element;
5417     }
5418
5419     // now "center_element" is finally determined -- set related values now
5420     artwork_element = center_element;           // for custom player artwork
5421     explosion_element = center_element;         // for custom player artwork
5422
5423     if (IS_PLAYER(ex, ey))
5424     {
5425       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5426
5427       artwork_element = stored_player[player_nr].artwork_element;
5428
5429       if (level.use_explosion_element[player_nr])
5430       {
5431         explosion_element = level.explosion_element[player_nr];
5432         artwork_element = explosion_element;
5433       }
5434     }
5435
5436     if (mode == EX_TYPE_NORMAL ||
5437         mode == EX_TYPE_CENTER ||
5438         mode == EX_TYPE_CROSS)
5439       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5440
5441     last_phase = element_info[explosion_element].explosion_delay + 1;
5442
5443     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5444     {
5445       int xx = x - ex + 1;
5446       int yy = y - ey + 1;
5447       int element;
5448
5449       if (!IN_LEV_FIELD(x, y) ||
5450           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5451           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5452         continue;
5453
5454       element = Feld[x][y];
5455
5456       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5457       {
5458         element = MovingOrBlocked2Element(x, y);
5459
5460         if (!IS_EXPLOSION_PROOF(element))
5461           RemoveMovingField(x, y);
5462       }
5463
5464       // indestructible elements can only explode in center (but not flames)
5465       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5466                                            mode == EX_TYPE_BORDER)) ||
5467           element == EL_FLAMES)
5468         continue;
5469
5470       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5471          behaviour, for example when touching a yamyam that explodes to rocks
5472          with active deadly shield, a rock is created under the player !!! */
5473       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5474 #if 0
5475       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5476           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5477            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5478 #else
5479       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5480 #endif
5481       {
5482         if (IS_ACTIVE_BOMB(element))
5483         {
5484           // re-activate things under the bomb like gate or penguin
5485           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5486           Back[x][y] = 0;
5487         }
5488
5489         continue;
5490       }
5491
5492       // save walkable background elements while explosion on same tile
5493       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5494           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5495         Back[x][y] = element;
5496
5497       // ignite explodable elements reached by other explosion
5498       if (element == EL_EXPLOSION)
5499         element = Store2[x][y];
5500
5501       if (AmoebaNr[x][y] &&
5502           (element == EL_AMOEBA_FULL ||
5503            element == EL_BD_AMOEBA ||
5504            element == EL_AMOEBA_GROWING))
5505       {
5506         AmoebaCnt[AmoebaNr[x][y]]--;
5507         AmoebaCnt2[AmoebaNr[x][y]]--;
5508       }
5509
5510       RemoveField(x, y);
5511
5512       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5513       {
5514         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5515
5516         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5517
5518         if (PLAYERINFO(ex, ey)->use_murphy)
5519           Store[x][y] = EL_EMPTY;
5520       }
5521
5522       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5523       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5524       else if (ELEM_IS_PLAYER(center_element))
5525         Store[x][y] = EL_EMPTY;
5526       else if (center_element == EL_YAMYAM)
5527         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5528       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5529         Store[x][y] = element_info[center_element].content.e[xx][yy];
5530 #if 1
5531       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5532       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5533       // otherwise) -- FIX THIS !!!
5534       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5535         Store[x][y] = element_info[element].content.e[1][1];
5536 #else
5537       else if (!CAN_EXPLODE(element))
5538         Store[x][y] = element_info[element].content.e[1][1];
5539 #endif
5540       else
5541         Store[x][y] = EL_EMPTY;
5542
5543       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5544           center_element == EL_AMOEBA_TO_DIAMOND)
5545         Store2[x][y] = element;
5546
5547       Feld[x][y] = EL_EXPLOSION;
5548       GfxElement[x][y] = artwork_element;
5549
5550       ExplodePhase[x][y] = 1;
5551       ExplodeDelay[x][y] = last_phase;
5552
5553       Stop[x][y] = TRUE;
5554     }
5555
5556     if (center_element == EL_YAMYAM)
5557       game.yamyam_content_nr =
5558         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5559
5560     return;
5561   }
5562
5563   if (Stop[ex][ey])
5564     return;
5565
5566   x = ex;
5567   y = ey;
5568
5569   if (phase == 1)
5570     GfxFrame[x][y] = 0;         // restart explosion animation
5571
5572   last_phase = ExplodeDelay[x][y];
5573
5574   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5575
5576   // this can happen if the player leaves an explosion just in time
5577   if (GfxElement[x][y] == EL_UNDEFINED)
5578     GfxElement[x][y] = EL_EMPTY;
5579
5580   border_element = Store2[x][y];
5581   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5582     border_element = StorePlayer[x][y];
5583
5584   if (phase == element_info[border_element].ignition_delay ||
5585       phase == last_phase)
5586   {
5587     boolean border_explosion = FALSE;
5588
5589     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5590         !PLAYER_EXPLOSION_PROTECTED(x, y))
5591     {
5592       KillPlayerUnlessExplosionProtected(x, y);
5593       border_explosion = TRUE;
5594     }
5595     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5596     {
5597       Feld[x][y] = Store2[x][y];
5598       Store2[x][y] = 0;
5599       Bang(x, y);
5600       border_explosion = TRUE;
5601     }
5602     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5603     {
5604       AmoebeUmwandeln(x, y);
5605       Store2[x][y] = 0;
5606       border_explosion = TRUE;
5607     }
5608
5609     // if an element just explodes due to another explosion (chain-reaction),
5610     // do not immediately end the new explosion when it was the last frame of
5611     // the explosion (as it would be done in the following "if"-statement!)
5612     if (border_explosion && phase == last_phase)
5613       return;
5614   }
5615
5616   if (phase == last_phase)
5617   {
5618     int element;
5619
5620     element = Feld[x][y] = Store[x][y];
5621     Store[x][y] = Store2[x][y] = 0;
5622     GfxElement[x][y] = EL_UNDEFINED;
5623
5624     // player can escape from explosions and might therefore be still alive
5625     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5626         element <= EL_PLAYER_IS_EXPLODING_4)
5627     {
5628       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5629       int explosion_element = EL_PLAYER_1 + player_nr;
5630       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5631       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5632
5633       if (level.use_explosion_element[player_nr])
5634         explosion_element = level.explosion_element[player_nr];
5635
5636       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5637                     element_info[explosion_element].content.e[xx][yy]);
5638     }
5639
5640     // restore probably existing indestructible background element
5641     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5642       element = Feld[x][y] = Back[x][y];
5643     Back[x][y] = 0;
5644
5645     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5646     GfxDir[x][y] = MV_NONE;
5647     ChangeDelay[x][y] = 0;
5648     ChangePage[x][y] = -1;
5649
5650     CustomValue[x][y] = 0;
5651
5652     InitField_WithBug2(x, y, FALSE);
5653
5654     TEST_DrawLevelField(x, y);
5655
5656     TestIfElementTouchesCustomElement(x, y);
5657
5658     if (GFX_CRUMBLED(element))
5659       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5660
5661     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5662       StorePlayer[x][y] = 0;
5663
5664     if (ELEM_IS_PLAYER(element))
5665       RelocatePlayer(x, y, element);
5666   }
5667   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5668   {
5669     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5670     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5671
5672     if (phase == delay)
5673       TEST_DrawLevelFieldCrumbled(x, y);
5674
5675     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5676     {
5677       DrawLevelElement(x, y, Back[x][y]);
5678       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5679     }
5680     else if (IS_WALKABLE_UNDER(Back[x][y]))
5681     {
5682       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5683       DrawLevelElementThruMask(x, y, Back[x][y]);
5684     }
5685     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5686       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5687   }
5688 }
5689
5690 static void DynaExplode(int ex, int ey)
5691 {
5692   int i, j;
5693   int dynabomb_element = Feld[ex][ey];
5694   int dynabomb_size = 1;
5695   boolean dynabomb_xl = FALSE;
5696   struct PlayerInfo *player;
5697   static int xy[4][2] =
5698   {
5699     { 0, -1 },
5700     { -1, 0 },
5701     { +1, 0 },
5702     { 0, +1 }
5703   };
5704
5705   if (IS_ACTIVE_BOMB(dynabomb_element))
5706   {
5707     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5708     dynabomb_size = player->dynabomb_size;
5709     dynabomb_xl = player->dynabomb_xl;
5710     player->dynabombs_left++;
5711   }
5712
5713   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5714
5715   for (i = 0; i < NUM_DIRECTIONS; i++)
5716   {
5717     for (j = 1; j <= dynabomb_size; j++)
5718     {
5719       int x = ex + j * xy[i][0];
5720       int y = ey + j * xy[i][1];
5721       int element;
5722
5723       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5724         break;
5725
5726       element = Feld[x][y];
5727
5728       // do not restart explosions of fields with active bombs
5729       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5730         continue;
5731
5732       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5733
5734       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5735           !IS_DIGGABLE(element) && !dynabomb_xl)
5736         break;
5737     }
5738   }
5739 }
5740
5741 void Bang(int x, int y)
5742 {
5743   int element = MovingOrBlocked2Element(x, y);
5744   int explosion_type = EX_TYPE_NORMAL;
5745
5746   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5747   {
5748     struct PlayerInfo *player = PLAYERINFO(x, y);
5749
5750     element = Feld[x][y] = player->initial_element;
5751
5752     if (level.use_explosion_element[player->index_nr])
5753     {
5754       int explosion_element = level.explosion_element[player->index_nr];
5755
5756       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5757         explosion_type = EX_TYPE_CROSS;
5758       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5759         explosion_type = EX_TYPE_CENTER;
5760     }
5761   }
5762
5763   switch (element)
5764   {
5765     case EL_BUG:
5766     case EL_SPACESHIP:
5767     case EL_BD_BUTTERFLY:
5768     case EL_BD_FIREFLY:
5769     case EL_YAMYAM:
5770     case EL_DARK_YAMYAM:
5771     case EL_ROBOT:
5772     case EL_PACMAN:
5773     case EL_MOLE:
5774       RaiseScoreElement(element);
5775       break;
5776
5777     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5778     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5779     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5780     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5781     case EL_DYNABOMB_INCREASE_NUMBER:
5782     case EL_DYNABOMB_INCREASE_SIZE:
5783     case EL_DYNABOMB_INCREASE_POWER:
5784       explosion_type = EX_TYPE_DYNA;
5785       break;
5786
5787     case EL_DC_LANDMINE:
5788       explosion_type = EX_TYPE_CENTER;
5789       break;
5790
5791     case EL_PENGUIN:
5792     case EL_LAMP:
5793     case EL_LAMP_ACTIVE:
5794     case EL_AMOEBA_TO_DIAMOND:
5795       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5796         explosion_type = EX_TYPE_CENTER;
5797       break;
5798
5799     default:
5800       if (element_info[element].explosion_type == EXPLODES_CROSS)
5801         explosion_type = EX_TYPE_CROSS;
5802       else if (element_info[element].explosion_type == EXPLODES_1X1)
5803         explosion_type = EX_TYPE_CENTER;
5804       break;
5805   }
5806
5807   if (explosion_type == EX_TYPE_DYNA)
5808     DynaExplode(x, y);
5809   else
5810     Explode(x, y, EX_PHASE_START, explosion_type);
5811
5812   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5813 }
5814
5815 static void SplashAcid(int x, int y)
5816 {
5817   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5818       (!IN_LEV_FIELD(x - 1, y - 2) ||
5819        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5820     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5821
5822   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5823       (!IN_LEV_FIELD(x + 1, y - 2) ||
5824        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5825     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5826
5827   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5828 }
5829
5830 static void InitBeltMovement(void)
5831 {
5832   static int belt_base_element[4] =
5833   {
5834     EL_CONVEYOR_BELT_1_LEFT,
5835     EL_CONVEYOR_BELT_2_LEFT,
5836     EL_CONVEYOR_BELT_3_LEFT,
5837     EL_CONVEYOR_BELT_4_LEFT
5838   };
5839   static int belt_base_active_element[4] =
5840   {
5841     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5842     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5843     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5844     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5845   };
5846
5847   int x, y, i, j;
5848
5849   // set frame order for belt animation graphic according to belt direction
5850   for (i = 0; i < NUM_BELTS; i++)
5851   {
5852     int belt_nr = i;
5853
5854     for (j = 0; j < NUM_BELT_PARTS; j++)
5855     {
5856       int element = belt_base_active_element[belt_nr] + j;
5857       int graphic_1 = el2img(element);
5858       int graphic_2 = el2panelimg(element);
5859
5860       if (game.belt_dir[i] == MV_LEFT)
5861       {
5862         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5863         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5864       }
5865       else
5866       {
5867         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5868         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5869       }
5870     }
5871   }
5872
5873   SCAN_PLAYFIELD(x, y)
5874   {
5875     int element = Feld[x][y];
5876
5877     for (i = 0; i < NUM_BELTS; i++)
5878     {
5879       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5880       {
5881         int e_belt_nr = getBeltNrFromBeltElement(element);
5882         int belt_nr = i;
5883
5884         if (e_belt_nr == belt_nr)
5885         {
5886           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5887
5888           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5889         }
5890       }
5891     }
5892   }
5893 }
5894
5895 static void ToggleBeltSwitch(int x, int y)
5896 {
5897   static int belt_base_element[4] =
5898   {
5899     EL_CONVEYOR_BELT_1_LEFT,
5900     EL_CONVEYOR_BELT_2_LEFT,
5901     EL_CONVEYOR_BELT_3_LEFT,
5902     EL_CONVEYOR_BELT_4_LEFT
5903   };
5904   static int belt_base_active_element[4] =
5905   {
5906     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5907     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5908     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5909     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5910   };
5911   static int belt_base_switch_element[4] =
5912   {
5913     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5914     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5915     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5916     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5917   };
5918   static int belt_move_dir[4] =
5919   {
5920     MV_LEFT,
5921     MV_NONE,
5922     MV_RIGHT,
5923     MV_NONE,
5924   };
5925
5926   int element = Feld[x][y];
5927   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5928   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5929   int belt_dir = belt_move_dir[belt_dir_nr];
5930   int xx, yy, i;
5931
5932   if (!IS_BELT_SWITCH(element))
5933     return;
5934
5935   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5936   game.belt_dir[belt_nr] = belt_dir;
5937
5938   if (belt_dir_nr == 3)
5939     belt_dir_nr = 1;
5940
5941   // set frame order for belt animation graphic according to belt direction
5942   for (i = 0; i < NUM_BELT_PARTS; i++)
5943   {
5944     int element = belt_base_active_element[belt_nr] + i;
5945     int graphic_1 = el2img(element);
5946     int graphic_2 = el2panelimg(element);
5947
5948     if (belt_dir == MV_LEFT)
5949     {
5950       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5951       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5952     }
5953     else
5954     {
5955       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5956       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5957     }
5958   }
5959
5960   SCAN_PLAYFIELD(xx, yy)
5961   {
5962     int element = Feld[xx][yy];
5963
5964     if (IS_BELT_SWITCH(element))
5965     {
5966       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5967
5968       if (e_belt_nr == belt_nr)
5969       {
5970         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5971         TEST_DrawLevelField(xx, yy);
5972       }
5973     }
5974     else if (IS_BELT(element) && belt_dir != MV_NONE)
5975     {
5976       int e_belt_nr = getBeltNrFromBeltElement(element);
5977
5978       if (e_belt_nr == belt_nr)
5979       {
5980         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5981
5982         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5983         TEST_DrawLevelField(xx, yy);
5984       }
5985     }
5986     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5987     {
5988       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5989
5990       if (e_belt_nr == belt_nr)
5991       {
5992         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5993
5994         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5995         TEST_DrawLevelField(xx, yy);
5996       }
5997     }
5998   }
5999 }
6000
6001 static void ToggleSwitchgateSwitch(int x, int y)
6002 {
6003   int xx, yy;
6004
6005   game.switchgate_pos = !game.switchgate_pos;
6006
6007   SCAN_PLAYFIELD(xx, yy)
6008   {
6009     int element = Feld[xx][yy];
6010
6011     if (element == EL_SWITCHGATE_SWITCH_UP)
6012     {
6013       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6014       TEST_DrawLevelField(xx, yy);
6015     }
6016     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6017     {
6018       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6019       TEST_DrawLevelField(xx, yy);
6020     }
6021     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6022     {
6023       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6024       TEST_DrawLevelField(xx, yy);
6025     }
6026     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6027     {
6028       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6029       TEST_DrawLevelField(xx, yy);
6030     }
6031     else if (element == EL_SWITCHGATE_OPEN ||
6032              element == EL_SWITCHGATE_OPENING)
6033     {
6034       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6035
6036       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6037     }
6038     else if (element == EL_SWITCHGATE_CLOSED ||
6039              element == EL_SWITCHGATE_CLOSING)
6040     {
6041       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6042
6043       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6044     }
6045   }
6046 }
6047
6048 static int getInvisibleActiveFromInvisibleElement(int element)
6049 {
6050   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6051           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6052           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6053           element);
6054 }
6055
6056 static int getInvisibleFromInvisibleActiveElement(int element)
6057 {
6058   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6059           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6060           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6061           element);
6062 }
6063
6064 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6065 {
6066   int x, y;
6067
6068   SCAN_PLAYFIELD(x, y)
6069   {
6070     int element = Feld[x][y];
6071
6072     if (element == EL_LIGHT_SWITCH &&
6073         game.light_time_left > 0)
6074     {
6075       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6076       TEST_DrawLevelField(x, y);
6077     }
6078     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6079              game.light_time_left == 0)
6080     {
6081       Feld[x][y] = EL_LIGHT_SWITCH;
6082       TEST_DrawLevelField(x, y);
6083     }
6084     else if (element == EL_EMC_DRIPPER &&
6085              game.light_time_left > 0)
6086     {
6087       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6088       TEST_DrawLevelField(x, y);
6089     }
6090     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6091              game.light_time_left == 0)
6092     {
6093       Feld[x][y] = EL_EMC_DRIPPER;
6094       TEST_DrawLevelField(x, y);
6095     }
6096     else if (element == EL_INVISIBLE_STEELWALL ||
6097              element == EL_INVISIBLE_WALL ||
6098              element == EL_INVISIBLE_SAND)
6099     {
6100       if (game.light_time_left > 0)
6101         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6102
6103       TEST_DrawLevelField(x, y);
6104
6105       // uncrumble neighbour fields, if needed
6106       if (element == EL_INVISIBLE_SAND)
6107         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6108     }
6109     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6110              element == EL_INVISIBLE_WALL_ACTIVE ||
6111              element == EL_INVISIBLE_SAND_ACTIVE)
6112     {
6113       if (game.light_time_left == 0)
6114         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6115
6116       TEST_DrawLevelField(x, y);
6117
6118       // re-crumble neighbour fields, if needed
6119       if (element == EL_INVISIBLE_SAND)
6120         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6121     }
6122   }
6123 }
6124
6125 static void RedrawAllInvisibleElementsForLenses(void)
6126 {
6127   int x, y;
6128
6129   SCAN_PLAYFIELD(x, y)
6130   {
6131     int element = Feld[x][y];
6132
6133     if (element == EL_EMC_DRIPPER &&
6134         game.lenses_time_left > 0)
6135     {
6136       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6137       TEST_DrawLevelField(x, y);
6138     }
6139     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6140              game.lenses_time_left == 0)
6141     {
6142       Feld[x][y] = EL_EMC_DRIPPER;
6143       TEST_DrawLevelField(x, y);
6144     }
6145     else if (element == EL_INVISIBLE_STEELWALL ||
6146              element == EL_INVISIBLE_WALL ||
6147              element == EL_INVISIBLE_SAND)
6148     {
6149       if (game.lenses_time_left > 0)
6150         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6151
6152       TEST_DrawLevelField(x, y);
6153
6154       // uncrumble neighbour fields, if needed
6155       if (element == EL_INVISIBLE_SAND)
6156         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6157     }
6158     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6159              element == EL_INVISIBLE_WALL_ACTIVE ||
6160              element == EL_INVISIBLE_SAND_ACTIVE)
6161     {
6162       if (game.lenses_time_left == 0)
6163         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6164
6165       TEST_DrawLevelField(x, y);
6166
6167       // re-crumble neighbour fields, if needed
6168       if (element == EL_INVISIBLE_SAND)
6169         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6170     }
6171   }
6172 }
6173
6174 static void RedrawAllInvisibleElementsForMagnifier(void)
6175 {
6176   int x, y;
6177
6178   SCAN_PLAYFIELD(x, y)
6179   {
6180     int element = Feld[x][y];
6181
6182     if (element == EL_EMC_FAKE_GRASS &&
6183         game.magnify_time_left > 0)
6184     {
6185       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6186       TEST_DrawLevelField(x, y);
6187     }
6188     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6189              game.magnify_time_left == 0)
6190     {
6191       Feld[x][y] = EL_EMC_FAKE_GRASS;
6192       TEST_DrawLevelField(x, y);
6193     }
6194     else if (IS_GATE_GRAY(element) &&
6195              game.magnify_time_left > 0)
6196     {
6197       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6198                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6199                     IS_EM_GATE_GRAY(element) ?
6200                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6201                     IS_EMC_GATE_GRAY(element) ?
6202                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6203                     IS_DC_GATE_GRAY(element) ?
6204                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6205                     element);
6206       TEST_DrawLevelField(x, y);
6207     }
6208     else if (IS_GATE_GRAY_ACTIVE(element) &&
6209              game.magnify_time_left == 0)
6210     {
6211       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6212                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6213                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6214                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6215                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6216                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6217                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6218                     EL_DC_GATE_WHITE_GRAY :
6219                     element);
6220       TEST_DrawLevelField(x, y);
6221     }
6222   }
6223 }
6224
6225 static void ToggleLightSwitch(int x, int y)
6226 {
6227   int element = Feld[x][y];
6228
6229   game.light_time_left =
6230     (element == EL_LIGHT_SWITCH ?
6231      level.time_light * FRAMES_PER_SECOND : 0);
6232
6233   RedrawAllLightSwitchesAndInvisibleElements();
6234 }
6235
6236 static void ActivateTimegateSwitch(int x, int y)
6237 {
6238   int xx, yy;
6239
6240   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6241
6242   SCAN_PLAYFIELD(xx, yy)
6243   {
6244     int element = Feld[xx][yy];
6245
6246     if (element == EL_TIMEGATE_CLOSED ||
6247         element == EL_TIMEGATE_CLOSING)
6248     {
6249       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6250       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6251     }
6252
6253     /*
6254     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6255     {
6256       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6257       TEST_DrawLevelField(xx, yy);
6258     }
6259     */
6260
6261   }
6262
6263   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6264                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6265 }
6266
6267 static void Impact(int x, int y)
6268 {
6269   boolean last_line = (y == lev_fieldy - 1);
6270   boolean object_hit = FALSE;
6271   boolean impact = (last_line || object_hit);
6272   int element = Feld[x][y];
6273   int smashed = EL_STEELWALL;
6274
6275   if (!last_line)       // check if element below was hit
6276   {
6277     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6278       return;
6279
6280     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6281                                          MovDir[x][y + 1] != MV_DOWN ||
6282                                          MovPos[x][y + 1] <= TILEY / 2));
6283
6284     // do not smash moving elements that left the smashed field in time
6285     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6286         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6287       object_hit = FALSE;
6288
6289 #if USE_QUICKSAND_IMPACT_BUGFIX
6290     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6291     {
6292       RemoveMovingField(x, y + 1);
6293       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6294       Feld[x][y + 2] = EL_ROCK;
6295       TEST_DrawLevelField(x, y + 2);
6296
6297       object_hit = TRUE;
6298     }
6299
6300     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6301     {
6302       RemoveMovingField(x, y + 1);
6303       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6304       Feld[x][y + 2] = EL_ROCK;
6305       TEST_DrawLevelField(x, y + 2);
6306
6307       object_hit = TRUE;
6308     }
6309 #endif
6310
6311     if (object_hit)
6312       smashed = MovingOrBlocked2Element(x, y + 1);
6313
6314     impact = (last_line || object_hit);
6315   }
6316
6317   if (!last_line && smashed == EL_ACID) // element falls into acid
6318   {
6319     SplashAcid(x, y + 1);
6320     return;
6321   }
6322
6323   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6324   // only reset graphic animation if graphic really changes after impact
6325   if (impact &&
6326       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6327   {
6328     ResetGfxAnimation(x, y);
6329     TEST_DrawLevelField(x, y);
6330   }
6331
6332   if (impact && CAN_EXPLODE_IMPACT(element))
6333   {
6334     Bang(x, y);
6335     return;
6336   }
6337   else if (impact && element == EL_PEARL &&
6338            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6339   {
6340     ResetGfxAnimation(x, y);
6341
6342     Feld[x][y] = EL_PEARL_BREAKING;
6343     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6344     return;
6345   }
6346   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6347   {
6348     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6349
6350     return;
6351   }
6352
6353   if (impact && element == EL_AMOEBA_DROP)
6354   {
6355     if (object_hit && IS_PLAYER(x, y + 1))
6356       KillPlayerUnlessEnemyProtected(x, y + 1);
6357     else if (object_hit && smashed == EL_PENGUIN)
6358       Bang(x, y + 1);
6359     else
6360     {
6361       Feld[x][y] = EL_AMOEBA_GROWING;
6362       Store[x][y] = EL_AMOEBA_WET;
6363
6364       ResetRandomAnimationValue(x, y);
6365     }
6366     return;
6367   }
6368
6369   if (object_hit)               // check which object was hit
6370   {
6371     if ((CAN_PASS_MAGIC_WALL(element) && 
6372          (smashed == EL_MAGIC_WALL ||
6373           smashed == EL_BD_MAGIC_WALL)) ||
6374         (CAN_PASS_DC_MAGIC_WALL(element) &&
6375          smashed == EL_DC_MAGIC_WALL))
6376     {
6377       int xx, yy;
6378       int activated_magic_wall =
6379         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6380          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6381          EL_DC_MAGIC_WALL_ACTIVE);
6382
6383       // activate magic wall / mill
6384       SCAN_PLAYFIELD(xx, yy)
6385       {
6386         if (Feld[xx][yy] == smashed)
6387           Feld[xx][yy] = activated_magic_wall;
6388       }
6389
6390       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6391       game.magic_wall_active = TRUE;
6392
6393       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6394                             SND_MAGIC_WALL_ACTIVATING :
6395                             smashed == EL_BD_MAGIC_WALL ?
6396                             SND_BD_MAGIC_WALL_ACTIVATING :
6397                             SND_DC_MAGIC_WALL_ACTIVATING));
6398     }
6399
6400     if (IS_PLAYER(x, y + 1))
6401     {
6402       if (CAN_SMASH_PLAYER(element))
6403       {
6404         KillPlayerUnlessEnemyProtected(x, y + 1);
6405         return;
6406       }
6407     }
6408     else if (smashed == EL_PENGUIN)
6409     {
6410       if (CAN_SMASH_PLAYER(element))
6411       {
6412         Bang(x, y + 1);
6413         return;
6414       }
6415     }
6416     else if (element == EL_BD_DIAMOND)
6417     {
6418       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6419       {
6420         Bang(x, y + 1);
6421         return;
6422       }
6423     }
6424     else if (((element == EL_SP_INFOTRON ||
6425                element == EL_SP_ZONK) &&
6426               (smashed == EL_SP_SNIKSNAK ||
6427                smashed == EL_SP_ELECTRON ||
6428                smashed == EL_SP_DISK_ORANGE)) ||
6429              (element == EL_SP_INFOTRON &&
6430               smashed == EL_SP_DISK_YELLOW))
6431     {
6432       Bang(x, y + 1);
6433       return;
6434     }
6435     else if (CAN_SMASH_EVERYTHING(element))
6436     {
6437       if (IS_CLASSIC_ENEMY(smashed) ||
6438           CAN_EXPLODE_SMASHED(smashed))
6439       {
6440         Bang(x, y + 1);
6441         return;
6442       }
6443       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6444       {
6445         if (smashed == EL_LAMP ||
6446             smashed == EL_LAMP_ACTIVE)
6447         {
6448           Bang(x, y + 1);
6449           return;
6450         }
6451         else if (smashed == EL_NUT)
6452         {
6453           Feld[x][y + 1] = EL_NUT_BREAKING;
6454           PlayLevelSound(x, y, SND_NUT_BREAKING);
6455           RaiseScoreElement(EL_NUT);
6456           return;
6457         }
6458         else if (smashed == EL_PEARL)
6459         {
6460           ResetGfxAnimation(x, y);
6461
6462           Feld[x][y + 1] = EL_PEARL_BREAKING;
6463           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6464           return;
6465         }
6466         else if (smashed == EL_DIAMOND)
6467         {
6468           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6469           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6470           return;
6471         }
6472         else if (IS_BELT_SWITCH(smashed))
6473         {
6474           ToggleBeltSwitch(x, y + 1);
6475         }
6476         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6477                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6478                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6479                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6480         {
6481           ToggleSwitchgateSwitch(x, y + 1);
6482         }
6483         else if (smashed == EL_LIGHT_SWITCH ||
6484                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6485         {
6486           ToggleLightSwitch(x, y + 1);
6487         }
6488         else
6489         {
6490           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6491
6492           CheckElementChangeBySide(x, y + 1, smashed, element,
6493                                    CE_SWITCHED, CH_SIDE_TOP);
6494           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6495                                             CH_SIDE_TOP);
6496         }
6497       }
6498       else
6499       {
6500         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6501       }
6502     }
6503   }
6504
6505   // play sound of magic wall / mill
6506   if (!last_line &&
6507       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6508        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6509        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6510   {
6511     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6512       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6513     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6514       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6515     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6516       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6517
6518     return;
6519   }
6520
6521   // play sound of object that hits the ground
6522   if (last_line || object_hit)
6523     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6524 }
6525
6526 static void TurnRoundExt(int x, int y)
6527 {
6528   static struct
6529   {
6530     int dx, dy;
6531   } move_xy[] =
6532   {
6533     {  0,  0 },
6534     { -1,  0 },
6535     { +1,  0 },
6536     {  0,  0 },
6537     {  0, -1 },
6538     {  0,  0 }, { 0, 0 }, { 0, 0 },
6539     {  0, +1 }
6540   };
6541   static struct
6542   {
6543     int left, right, back;
6544   } turn[] =
6545   {
6546     { 0,        0,              0        },
6547     { MV_DOWN,  MV_UP,          MV_RIGHT },
6548     { MV_UP,    MV_DOWN,        MV_LEFT  },
6549     { 0,        0,              0        },
6550     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6551     { 0,        0,              0        },
6552     { 0,        0,              0        },
6553     { 0,        0,              0        },
6554     { MV_RIGHT, MV_LEFT,        MV_UP    }
6555   };
6556
6557   int element = Feld[x][y];
6558   int move_pattern = element_info[element].move_pattern;
6559
6560   int old_move_dir = MovDir[x][y];
6561   int left_dir  = turn[old_move_dir].left;
6562   int right_dir = turn[old_move_dir].right;
6563   int back_dir  = turn[old_move_dir].back;
6564
6565   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6566   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6567   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6568   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6569
6570   int left_x  = x + left_dx,  left_y  = y + left_dy;
6571   int right_x = x + right_dx, right_y = y + right_dy;
6572   int move_x  = x + move_dx,  move_y  = y + move_dy;
6573
6574   int xx, yy;
6575
6576   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6577   {
6578     TestIfBadThingTouchesOtherBadThing(x, y);
6579
6580     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6581       MovDir[x][y] = right_dir;
6582     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6583       MovDir[x][y] = left_dir;
6584
6585     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6586       MovDelay[x][y] = 9;
6587     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6588       MovDelay[x][y] = 1;
6589   }
6590   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6591   {
6592     TestIfBadThingTouchesOtherBadThing(x, y);
6593
6594     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6595       MovDir[x][y] = left_dir;
6596     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6597       MovDir[x][y] = right_dir;
6598
6599     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6600       MovDelay[x][y] = 9;
6601     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6602       MovDelay[x][y] = 1;
6603   }
6604   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6605   {
6606     TestIfBadThingTouchesOtherBadThing(x, y);
6607
6608     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6609       MovDir[x][y] = left_dir;
6610     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6611       MovDir[x][y] = right_dir;
6612
6613     if (MovDir[x][y] != old_move_dir)
6614       MovDelay[x][y] = 9;
6615   }
6616   else if (element == EL_YAMYAM)
6617   {
6618     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6619     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6620
6621     if (can_turn_left && can_turn_right)
6622       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6623     else if (can_turn_left)
6624       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6625     else if (can_turn_right)
6626       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6627     else
6628       MovDir[x][y] = back_dir;
6629
6630     MovDelay[x][y] = 16 + 16 * RND(3);
6631   }
6632   else if (element == EL_DARK_YAMYAM)
6633   {
6634     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6635                                                          left_x, left_y);
6636     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6637                                                          right_x, right_y);
6638
6639     if (can_turn_left && can_turn_right)
6640       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6641     else if (can_turn_left)
6642       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6643     else if (can_turn_right)
6644       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6645     else
6646       MovDir[x][y] = back_dir;
6647
6648     MovDelay[x][y] = 16 + 16 * RND(3);
6649   }
6650   else if (element == EL_PACMAN)
6651   {
6652     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6653     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6654
6655     if (can_turn_left && can_turn_right)
6656       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6657     else if (can_turn_left)
6658       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6659     else if (can_turn_right)
6660       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6661     else
6662       MovDir[x][y] = back_dir;
6663
6664     MovDelay[x][y] = 6 + RND(40);
6665   }
6666   else if (element == EL_PIG)
6667   {
6668     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6669     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6670     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6671     boolean should_turn_left, should_turn_right, should_move_on;
6672     int rnd_value = 24;
6673     int rnd = RND(rnd_value);
6674
6675     should_turn_left = (can_turn_left &&
6676                         (!can_move_on ||
6677                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6678                                                    y + back_dy + left_dy)));
6679     should_turn_right = (can_turn_right &&
6680                          (!can_move_on ||
6681                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6682                                                     y + back_dy + right_dy)));
6683     should_move_on = (can_move_on &&
6684                       (!can_turn_left ||
6685                        !can_turn_right ||
6686                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6687                                                  y + move_dy + left_dy) ||
6688                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6689                                                  y + move_dy + right_dy)));
6690
6691     if (should_turn_left || should_turn_right || should_move_on)
6692     {
6693       if (should_turn_left && should_turn_right && should_move_on)
6694         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6695                         rnd < 2 * rnd_value / 3 ? right_dir :
6696                         old_move_dir);
6697       else if (should_turn_left && should_turn_right)
6698         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6699       else if (should_turn_left && should_move_on)
6700         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6701       else if (should_turn_right && should_move_on)
6702         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6703       else if (should_turn_left)
6704         MovDir[x][y] = left_dir;
6705       else if (should_turn_right)
6706         MovDir[x][y] = right_dir;
6707       else if (should_move_on)
6708         MovDir[x][y] = old_move_dir;
6709     }
6710     else if (can_move_on && rnd > rnd_value / 8)
6711       MovDir[x][y] = old_move_dir;
6712     else if (can_turn_left && can_turn_right)
6713       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6714     else if (can_turn_left && rnd > rnd_value / 8)
6715       MovDir[x][y] = left_dir;
6716     else if (can_turn_right && rnd > rnd_value/8)
6717       MovDir[x][y] = right_dir;
6718     else
6719       MovDir[x][y] = back_dir;
6720
6721     xx = x + move_xy[MovDir[x][y]].dx;
6722     yy = y + move_xy[MovDir[x][y]].dy;
6723
6724     if (!IN_LEV_FIELD(xx, yy) ||
6725         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6726       MovDir[x][y] = old_move_dir;
6727
6728     MovDelay[x][y] = 0;
6729   }
6730   else if (element == EL_DRAGON)
6731   {
6732     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6733     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6734     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6735     int rnd_value = 24;
6736     int rnd = RND(rnd_value);
6737
6738     if (can_move_on && rnd > rnd_value / 8)
6739       MovDir[x][y] = old_move_dir;
6740     else if (can_turn_left && can_turn_right)
6741       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6742     else if (can_turn_left && rnd > rnd_value / 8)
6743       MovDir[x][y] = left_dir;
6744     else if (can_turn_right && rnd > rnd_value / 8)
6745       MovDir[x][y] = right_dir;
6746     else
6747       MovDir[x][y] = back_dir;
6748
6749     xx = x + move_xy[MovDir[x][y]].dx;
6750     yy = y + move_xy[MovDir[x][y]].dy;
6751
6752     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6753       MovDir[x][y] = old_move_dir;
6754
6755     MovDelay[x][y] = 0;
6756   }
6757   else if (element == EL_MOLE)
6758   {
6759     boolean can_move_on =
6760       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6761                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6762                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6763     if (!can_move_on)
6764     {
6765       boolean can_turn_left =
6766         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6767                               IS_AMOEBOID(Feld[left_x][left_y])));
6768
6769       boolean can_turn_right =
6770         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6771                               IS_AMOEBOID(Feld[right_x][right_y])));
6772
6773       if (can_turn_left && can_turn_right)
6774         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6775       else if (can_turn_left)
6776         MovDir[x][y] = left_dir;
6777       else
6778         MovDir[x][y] = right_dir;
6779     }
6780
6781     if (MovDir[x][y] != old_move_dir)
6782       MovDelay[x][y] = 9;
6783   }
6784   else if (element == EL_BALLOON)
6785   {
6786     MovDir[x][y] = game.wind_direction;
6787     MovDelay[x][y] = 0;
6788   }
6789   else if (element == EL_SPRING)
6790   {
6791     if (MovDir[x][y] & MV_HORIZONTAL)
6792     {
6793       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6794           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6795       {
6796         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6797         ResetGfxAnimation(move_x, move_y);
6798         TEST_DrawLevelField(move_x, move_y);
6799
6800         MovDir[x][y] = back_dir;
6801       }
6802       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6803                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6804         MovDir[x][y] = MV_NONE;
6805     }
6806
6807     MovDelay[x][y] = 0;
6808   }
6809   else if (element == EL_ROBOT ||
6810            element == EL_SATELLITE ||
6811            element == EL_PENGUIN ||
6812            element == EL_EMC_ANDROID)
6813   {
6814     int attr_x = -1, attr_y = -1;
6815
6816     if (game.all_players_gone)
6817     {
6818       attr_x = ExitX;
6819       attr_y = ExitY;
6820     }
6821     else
6822     {
6823       int i;
6824
6825       for (i = 0; i < MAX_PLAYERS; i++)
6826       {
6827         struct PlayerInfo *player = &stored_player[i];
6828         int jx = player->jx, jy = player->jy;
6829
6830         if (!player->active)
6831           continue;
6832
6833         if (attr_x == -1 ||
6834             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6835         {
6836           attr_x = jx;
6837           attr_y = jy;
6838         }
6839       }
6840     }
6841
6842     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6843         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6844          game.engine_version < VERSION_IDENT(3,1,0,0)))
6845     {
6846       attr_x = ZX;
6847       attr_y = ZY;
6848     }
6849
6850     if (element == EL_PENGUIN)
6851     {
6852       int i;
6853       static int xy[4][2] =
6854       {
6855         { 0, -1 },
6856         { -1, 0 },
6857         { +1, 0 },
6858         { 0, +1 }
6859       };
6860
6861       for (i = 0; i < NUM_DIRECTIONS; i++)
6862       {
6863         int ex = x + xy[i][0];
6864         int ey = y + xy[i][1];
6865
6866         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6867                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6868                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6869                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6870         {
6871           attr_x = ex;
6872           attr_y = ey;
6873           break;
6874         }
6875       }
6876     }
6877
6878     MovDir[x][y] = MV_NONE;
6879     if (attr_x < x)
6880       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
6881     else if (attr_x > x)
6882       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
6883     if (attr_y < y)
6884       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
6885     else if (attr_y > y)
6886       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
6887
6888     if (element == EL_ROBOT)
6889     {
6890       int newx, newy;
6891
6892       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6893         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6894       Moving2Blocked(x, y, &newx, &newy);
6895
6896       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6897         MovDelay[x][y] = 8 + 8 * !RND(3);
6898       else
6899         MovDelay[x][y] = 16;
6900     }
6901     else if (element == EL_PENGUIN)
6902     {
6903       int newx, newy;
6904
6905       MovDelay[x][y] = 1;
6906
6907       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6908       {
6909         boolean first_horiz = RND(2);
6910         int new_move_dir = MovDir[x][y];
6911
6912         MovDir[x][y] =
6913           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6914         Moving2Blocked(x, y, &newx, &newy);
6915
6916         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6917           return;
6918
6919         MovDir[x][y] =
6920           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6921         Moving2Blocked(x, y, &newx, &newy);
6922
6923         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6924           return;
6925
6926         MovDir[x][y] = old_move_dir;
6927         return;
6928       }
6929     }
6930     else if (element == EL_SATELLITE)
6931     {
6932       int newx, newy;
6933
6934       MovDelay[x][y] = 1;
6935
6936       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6937       {
6938         boolean first_horiz = RND(2);
6939         int new_move_dir = MovDir[x][y];
6940
6941         MovDir[x][y] =
6942           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6943         Moving2Blocked(x, y, &newx, &newy);
6944
6945         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6946           return;
6947
6948         MovDir[x][y] =
6949           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6950         Moving2Blocked(x, y, &newx, &newy);
6951
6952         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6953           return;
6954
6955         MovDir[x][y] = old_move_dir;
6956         return;
6957       }
6958     }
6959     else if (element == EL_EMC_ANDROID)
6960     {
6961       static int check_pos[16] =
6962       {
6963         -1,             //  0 => (invalid)
6964         7,              //  1 => MV_LEFT
6965         3,              //  2 => MV_RIGHT
6966         -1,             //  3 => (invalid)
6967         1,              //  4 =>            MV_UP
6968         0,              //  5 => MV_LEFT  | MV_UP
6969         2,              //  6 => MV_RIGHT | MV_UP
6970         -1,             //  7 => (invalid)
6971         5,              //  8 =>            MV_DOWN
6972         6,              //  9 => MV_LEFT  | MV_DOWN
6973         4,              // 10 => MV_RIGHT | MV_DOWN
6974         -1,             // 11 => (invalid)
6975         -1,             // 12 => (invalid)
6976         -1,             // 13 => (invalid)
6977         -1,             // 14 => (invalid)
6978         -1,             // 15 => (invalid)
6979       };
6980       static struct
6981       {
6982         int dx, dy;
6983         int dir;
6984       } check_xy[8] =
6985       {
6986         { -1, -1,       MV_LEFT  | MV_UP   },
6987         {  0, -1,                  MV_UP   },
6988         { +1, -1,       MV_RIGHT | MV_UP   },
6989         { +1,  0,       MV_RIGHT           },
6990         { +1, +1,       MV_RIGHT | MV_DOWN },
6991         {  0, +1,                  MV_DOWN },
6992         { -1, +1,       MV_LEFT  | MV_DOWN },
6993         { -1,  0,       MV_LEFT            },
6994       };
6995       int start_pos, check_order;
6996       boolean can_clone = FALSE;
6997       int i;
6998
6999       // check if there is any free field around current position
7000       for (i = 0; i < 8; i++)
7001       {
7002         int newx = x + check_xy[i].dx;
7003         int newy = y + check_xy[i].dy;
7004
7005         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7006         {
7007           can_clone = TRUE;
7008
7009           break;
7010         }
7011       }
7012
7013       if (can_clone)            // randomly find an element to clone
7014       {
7015         can_clone = FALSE;
7016
7017         start_pos = check_pos[RND(8)];
7018         check_order = (RND(2) ? -1 : +1);
7019
7020         for (i = 0; i < 8; i++)
7021         {
7022           int pos_raw = start_pos + i * check_order;
7023           int pos = (pos_raw + 8) % 8;
7024           int newx = x + check_xy[pos].dx;
7025           int newy = y + check_xy[pos].dy;
7026
7027           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7028           {
7029             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7030             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7031
7032             Store[x][y] = Feld[newx][newy];
7033
7034             can_clone = TRUE;
7035
7036             break;
7037           }
7038         }
7039       }
7040
7041       if (can_clone)            // randomly find a direction to move
7042       {
7043         can_clone = FALSE;
7044
7045         start_pos = check_pos[RND(8)];
7046         check_order = (RND(2) ? -1 : +1);
7047
7048         for (i = 0; i < 8; i++)
7049         {
7050           int pos_raw = start_pos + i * check_order;
7051           int pos = (pos_raw + 8) % 8;
7052           int newx = x + check_xy[pos].dx;
7053           int newy = y + check_xy[pos].dy;
7054           int new_move_dir = check_xy[pos].dir;
7055
7056           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7057           {
7058             MovDir[x][y] = new_move_dir;
7059             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7060
7061             can_clone = TRUE;
7062
7063             break;
7064           }
7065         }
7066       }
7067
7068       if (can_clone)            // cloning and moving successful
7069         return;
7070
7071       // cannot clone -- try to move towards player
7072
7073       start_pos = check_pos[MovDir[x][y] & 0x0f];
7074       check_order = (RND(2) ? -1 : +1);
7075
7076       for (i = 0; i < 3; i++)
7077       {
7078         // first check start_pos, then previous/next or (next/previous) pos
7079         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7080         int pos = (pos_raw + 8) % 8;
7081         int newx = x + check_xy[pos].dx;
7082         int newy = y + check_xy[pos].dy;
7083         int new_move_dir = check_xy[pos].dir;
7084
7085         if (IS_PLAYER(newx, newy))
7086           break;
7087
7088         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7089         {
7090           MovDir[x][y] = new_move_dir;
7091           MovDelay[x][y] = level.android_move_time * 8 + 1;
7092
7093           break;
7094         }
7095       }
7096     }
7097   }
7098   else if (move_pattern == MV_TURNING_LEFT ||
7099            move_pattern == MV_TURNING_RIGHT ||
7100            move_pattern == MV_TURNING_LEFT_RIGHT ||
7101            move_pattern == MV_TURNING_RIGHT_LEFT ||
7102            move_pattern == MV_TURNING_RANDOM ||
7103            move_pattern == MV_ALL_DIRECTIONS)
7104   {
7105     boolean can_turn_left =
7106       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7107     boolean can_turn_right =
7108       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7109
7110     if (element_info[element].move_stepsize == 0)       // "not moving"
7111       return;
7112
7113     if (move_pattern == MV_TURNING_LEFT)
7114       MovDir[x][y] = left_dir;
7115     else if (move_pattern == MV_TURNING_RIGHT)
7116       MovDir[x][y] = right_dir;
7117     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7118       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7119     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7120       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7121     else if (move_pattern == MV_TURNING_RANDOM)
7122       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7123                       can_turn_right && !can_turn_left ? right_dir :
7124                       RND(2) ? left_dir : right_dir);
7125     else if (can_turn_left && can_turn_right)
7126       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7127     else if (can_turn_left)
7128       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7129     else if (can_turn_right)
7130       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7131     else
7132       MovDir[x][y] = back_dir;
7133
7134     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7135   }
7136   else if (move_pattern == MV_HORIZONTAL ||
7137            move_pattern == MV_VERTICAL)
7138   {
7139     if (move_pattern & old_move_dir)
7140       MovDir[x][y] = back_dir;
7141     else if (move_pattern == MV_HORIZONTAL)
7142       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7143     else if (move_pattern == MV_VERTICAL)
7144       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7145
7146     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7147   }
7148   else if (move_pattern & MV_ANY_DIRECTION)
7149   {
7150     MovDir[x][y] = move_pattern;
7151     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7152   }
7153   else if (move_pattern & MV_WIND_DIRECTION)
7154   {
7155     MovDir[x][y] = game.wind_direction;
7156     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7157   }
7158   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7159   {
7160     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7161       MovDir[x][y] = left_dir;
7162     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7163       MovDir[x][y] = right_dir;
7164
7165     if (MovDir[x][y] != old_move_dir)
7166       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7167   }
7168   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7169   {
7170     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7171       MovDir[x][y] = right_dir;
7172     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7173       MovDir[x][y] = left_dir;
7174
7175     if (MovDir[x][y] != old_move_dir)
7176       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7177   }
7178   else if (move_pattern == MV_TOWARDS_PLAYER ||
7179            move_pattern == MV_AWAY_FROM_PLAYER)
7180   {
7181     int attr_x = -1, attr_y = -1;
7182     int newx, newy;
7183     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7184
7185     if (game.all_players_gone)
7186     {
7187       attr_x = ExitX;
7188       attr_y = ExitY;
7189     }
7190     else
7191     {
7192       int i;
7193
7194       for (i = 0; i < MAX_PLAYERS; i++)
7195       {
7196         struct PlayerInfo *player = &stored_player[i];
7197         int jx = player->jx, jy = player->jy;
7198
7199         if (!player->active)
7200           continue;
7201
7202         if (attr_x == -1 ||
7203             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7204         {
7205           attr_x = jx;
7206           attr_y = jy;
7207         }
7208       }
7209     }
7210
7211     MovDir[x][y] = MV_NONE;
7212     if (attr_x < x)
7213       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7214     else if (attr_x > x)
7215       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7216     if (attr_y < y)
7217       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7218     else if (attr_y > y)
7219       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7220
7221     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7222
7223     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7224     {
7225       boolean first_horiz = RND(2);
7226       int new_move_dir = MovDir[x][y];
7227
7228       if (element_info[element].move_stepsize == 0)     // "not moving"
7229       {
7230         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7231         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7232
7233         return;
7234       }
7235
7236       MovDir[x][y] =
7237         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7238       Moving2Blocked(x, y, &newx, &newy);
7239
7240       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7241         return;
7242
7243       MovDir[x][y] =
7244         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7245       Moving2Blocked(x, y, &newx, &newy);
7246
7247       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7248         return;
7249
7250       MovDir[x][y] = old_move_dir;
7251     }
7252   }
7253   else if (move_pattern == MV_WHEN_PUSHED ||
7254            move_pattern == MV_WHEN_DROPPED)
7255   {
7256     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7257       MovDir[x][y] = MV_NONE;
7258
7259     MovDelay[x][y] = 0;
7260   }
7261   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7262   {
7263     static int test_xy[7][2] =
7264     {
7265       { 0, -1 },
7266       { -1, 0 },
7267       { +1, 0 },
7268       { 0, +1 },
7269       { 0, -1 },
7270       { -1, 0 },
7271       { +1, 0 },
7272     };
7273     static int test_dir[7] =
7274     {
7275       MV_UP,
7276       MV_LEFT,
7277       MV_RIGHT,
7278       MV_DOWN,
7279       MV_UP,
7280       MV_LEFT,
7281       MV_RIGHT,
7282     };
7283     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7284     int move_preference = -1000000;     // start with very low preference
7285     int new_move_dir = MV_NONE;
7286     int start_test = RND(4);
7287     int i;
7288
7289     for (i = 0; i < NUM_DIRECTIONS; i++)
7290     {
7291       int move_dir = test_dir[start_test + i];
7292       int move_dir_preference;
7293
7294       xx = x + test_xy[start_test + i][0];
7295       yy = y + test_xy[start_test + i][1];
7296
7297       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7298           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7299       {
7300         new_move_dir = move_dir;
7301
7302         break;
7303       }
7304
7305       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7306         continue;
7307
7308       move_dir_preference = -1 * RunnerVisit[xx][yy];
7309       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7310         move_dir_preference = PlayerVisit[xx][yy];
7311
7312       if (move_dir_preference > move_preference)
7313       {
7314         // prefer field that has not been visited for the longest time
7315         move_preference = move_dir_preference;
7316         new_move_dir = move_dir;
7317       }
7318       else if (move_dir_preference == move_preference &&
7319                move_dir == old_move_dir)
7320       {
7321         // prefer last direction when all directions are preferred equally
7322         move_preference = move_dir_preference;
7323         new_move_dir = move_dir;
7324       }
7325     }
7326
7327     MovDir[x][y] = new_move_dir;
7328     if (old_move_dir != new_move_dir)
7329       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7330   }
7331 }
7332
7333 static void TurnRound(int x, int y)
7334 {
7335   int direction = MovDir[x][y];
7336
7337   TurnRoundExt(x, y);
7338
7339   GfxDir[x][y] = MovDir[x][y];
7340
7341   if (direction != MovDir[x][y])
7342     GfxFrame[x][y] = 0;
7343
7344   if (MovDelay[x][y])
7345     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7346
7347   ResetGfxFrame(x, y);
7348 }
7349
7350 static boolean JustBeingPushed(int x, int y)
7351 {
7352   int i;
7353
7354   for (i = 0; i < MAX_PLAYERS; i++)
7355   {
7356     struct PlayerInfo *player = &stored_player[i];
7357
7358     if (player->active && player->is_pushing && player->MovPos)
7359     {
7360       int next_jx = player->jx + (player->jx - player->last_jx);
7361       int next_jy = player->jy + (player->jy - player->last_jy);
7362
7363       if (x == next_jx && y == next_jy)
7364         return TRUE;
7365     }
7366   }
7367
7368   return FALSE;
7369 }
7370
7371 static void StartMoving(int x, int y)
7372 {
7373   boolean started_moving = FALSE;       // some elements can fall _and_ move
7374   int element = Feld[x][y];
7375
7376   if (Stop[x][y])
7377     return;
7378
7379   if (MovDelay[x][y] == 0)
7380     GfxAction[x][y] = ACTION_DEFAULT;
7381
7382   if (CAN_FALL(element) && y < lev_fieldy - 1)
7383   {
7384     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7385         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7386       if (JustBeingPushed(x, y))
7387         return;
7388
7389     if (element == EL_QUICKSAND_FULL)
7390     {
7391       if (IS_FREE(x, y + 1))
7392       {
7393         InitMovingField(x, y, MV_DOWN);
7394         started_moving = TRUE;
7395
7396         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7397 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7398         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7399           Store[x][y] = EL_ROCK;
7400 #else
7401         Store[x][y] = EL_ROCK;
7402 #endif
7403
7404         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7405       }
7406       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7407       {
7408         if (!MovDelay[x][y])
7409         {
7410           MovDelay[x][y] = TILEY + 1;
7411
7412           ResetGfxAnimation(x, y);
7413           ResetGfxAnimation(x, y + 1);
7414         }
7415
7416         if (MovDelay[x][y])
7417         {
7418           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7419           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7420
7421           MovDelay[x][y]--;
7422           if (MovDelay[x][y])
7423             return;
7424         }
7425
7426         Feld[x][y] = EL_QUICKSAND_EMPTY;
7427         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7428         Store[x][y + 1] = Store[x][y];
7429         Store[x][y] = 0;
7430
7431         PlayLevelSoundAction(x, y, ACTION_FILLING);
7432       }
7433       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7434       {
7435         if (!MovDelay[x][y])
7436         {
7437           MovDelay[x][y] = TILEY + 1;
7438
7439           ResetGfxAnimation(x, y);
7440           ResetGfxAnimation(x, y + 1);
7441         }
7442
7443         if (MovDelay[x][y])
7444         {
7445           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7446           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7447
7448           MovDelay[x][y]--;
7449           if (MovDelay[x][y])
7450             return;
7451         }
7452
7453         Feld[x][y] = EL_QUICKSAND_EMPTY;
7454         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7455         Store[x][y + 1] = Store[x][y];
7456         Store[x][y] = 0;
7457
7458         PlayLevelSoundAction(x, y, ACTION_FILLING);
7459       }
7460     }
7461     else if (element == EL_QUICKSAND_FAST_FULL)
7462     {
7463       if (IS_FREE(x, y + 1))
7464       {
7465         InitMovingField(x, y, MV_DOWN);
7466         started_moving = TRUE;
7467
7468         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7469 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7470         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7471           Store[x][y] = EL_ROCK;
7472 #else
7473         Store[x][y] = EL_ROCK;
7474 #endif
7475
7476         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7477       }
7478       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7479       {
7480         if (!MovDelay[x][y])
7481         {
7482           MovDelay[x][y] = TILEY + 1;
7483
7484           ResetGfxAnimation(x, y);
7485           ResetGfxAnimation(x, y + 1);
7486         }
7487
7488         if (MovDelay[x][y])
7489         {
7490           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7491           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7492
7493           MovDelay[x][y]--;
7494           if (MovDelay[x][y])
7495             return;
7496         }
7497
7498         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7499         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7500         Store[x][y + 1] = Store[x][y];
7501         Store[x][y] = 0;
7502
7503         PlayLevelSoundAction(x, y, ACTION_FILLING);
7504       }
7505       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7506       {
7507         if (!MovDelay[x][y])
7508         {
7509           MovDelay[x][y] = TILEY + 1;
7510
7511           ResetGfxAnimation(x, y);
7512           ResetGfxAnimation(x, y + 1);
7513         }
7514
7515         if (MovDelay[x][y])
7516         {
7517           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7518           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7519
7520           MovDelay[x][y]--;
7521           if (MovDelay[x][y])
7522             return;
7523         }
7524
7525         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7526         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7527         Store[x][y + 1] = Store[x][y];
7528         Store[x][y] = 0;
7529
7530         PlayLevelSoundAction(x, y, ACTION_FILLING);
7531       }
7532     }
7533     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7534              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7535     {
7536       InitMovingField(x, y, MV_DOWN);
7537       started_moving = TRUE;
7538
7539       Feld[x][y] = EL_QUICKSAND_FILLING;
7540       Store[x][y] = element;
7541
7542       PlayLevelSoundAction(x, y, ACTION_FILLING);
7543     }
7544     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7545              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7546     {
7547       InitMovingField(x, y, MV_DOWN);
7548       started_moving = TRUE;
7549
7550       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7551       Store[x][y] = element;
7552
7553       PlayLevelSoundAction(x, y, ACTION_FILLING);
7554     }
7555     else if (element == EL_MAGIC_WALL_FULL)
7556     {
7557       if (IS_FREE(x, y + 1))
7558       {
7559         InitMovingField(x, y, MV_DOWN);
7560         started_moving = TRUE;
7561
7562         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7563         Store[x][y] = EL_CHANGED(Store[x][y]);
7564       }
7565       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7566       {
7567         if (!MovDelay[x][y])
7568           MovDelay[x][y] = TILEY / 4 + 1;
7569
7570         if (MovDelay[x][y])
7571         {
7572           MovDelay[x][y]--;
7573           if (MovDelay[x][y])
7574             return;
7575         }
7576
7577         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7578         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7579         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7580         Store[x][y] = 0;
7581       }
7582     }
7583     else if (element == EL_BD_MAGIC_WALL_FULL)
7584     {
7585       if (IS_FREE(x, y + 1))
7586       {
7587         InitMovingField(x, y, MV_DOWN);
7588         started_moving = TRUE;
7589
7590         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7591         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7592       }
7593       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7594       {
7595         if (!MovDelay[x][y])
7596           MovDelay[x][y] = TILEY / 4 + 1;
7597
7598         if (MovDelay[x][y])
7599         {
7600           MovDelay[x][y]--;
7601           if (MovDelay[x][y])
7602             return;
7603         }
7604
7605         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7606         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7607         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7608         Store[x][y] = 0;
7609       }
7610     }
7611     else if (element == EL_DC_MAGIC_WALL_FULL)
7612     {
7613       if (IS_FREE(x, y + 1))
7614       {
7615         InitMovingField(x, y, MV_DOWN);
7616         started_moving = TRUE;
7617
7618         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7619         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7620       }
7621       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7622       {
7623         if (!MovDelay[x][y])
7624           MovDelay[x][y] = TILEY / 4 + 1;
7625
7626         if (MovDelay[x][y])
7627         {
7628           MovDelay[x][y]--;
7629           if (MovDelay[x][y])
7630             return;
7631         }
7632
7633         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7634         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7635         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7636         Store[x][y] = 0;
7637       }
7638     }
7639     else if ((CAN_PASS_MAGIC_WALL(element) &&
7640               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7641                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7642              (CAN_PASS_DC_MAGIC_WALL(element) &&
7643               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7644
7645     {
7646       InitMovingField(x, y, MV_DOWN);
7647       started_moving = TRUE;
7648
7649       Feld[x][y] =
7650         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7651          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7652          EL_DC_MAGIC_WALL_FILLING);
7653       Store[x][y] = element;
7654     }
7655     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7656     {
7657       SplashAcid(x, y + 1);
7658
7659       InitMovingField(x, y, MV_DOWN);
7660       started_moving = TRUE;
7661
7662       Store[x][y] = EL_ACID;
7663     }
7664     else if (
7665              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7666               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7667              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7668               CAN_FALL(element) && WasJustFalling[x][y] &&
7669               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7670
7671              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7672               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7673               (Feld[x][y + 1] == EL_BLOCKED)))
7674     {
7675       /* this is needed for a special case not covered by calling "Impact()"
7676          from "ContinueMoving()": if an element moves to a tile directly below
7677          another element which was just falling on that tile (which was empty
7678          in the previous frame), the falling element above would just stop
7679          instead of smashing the element below (in previous version, the above
7680          element was just checked for "moving" instead of "falling", resulting
7681          in incorrect smashes caused by horizontal movement of the above
7682          element; also, the case of the player being the element to smash was
7683          simply not covered here... :-/ ) */
7684
7685       CheckCollision[x][y] = 0;
7686       CheckImpact[x][y] = 0;
7687
7688       Impact(x, y);
7689     }
7690     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7691     {
7692       if (MovDir[x][y] == MV_NONE)
7693       {
7694         InitMovingField(x, y, MV_DOWN);
7695         started_moving = TRUE;
7696       }
7697     }
7698     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7699     {
7700       if (WasJustFalling[x][y]) // prevent animation from being restarted
7701         MovDir[x][y] = MV_DOWN;
7702
7703       InitMovingField(x, y, MV_DOWN);
7704       started_moving = TRUE;
7705     }
7706     else if (element == EL_AMOEBA_DROP)
7707     {
7708       Feld[x][y] = EL_AMOEBA_GROWING;
7709       Store[x][y] = EL_AMOEBA_WET;
7710     }
7711     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7712               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7713              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7714              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7715     {
7716       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7717                                 (IS_FREE(x - 1, y + 1) ||
7718                                  Feld[x - 1][y + 1] == EL_ACID));
7719       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7720                                 (IS_FREE(x + 1, y + 1) ||
7721                                  Feld[x + 1][y + 1] == EL_ACID));
7722       boolean can_fall_any  = (can_fall_left || can_fall_right);
7723       boolean can_fall_both = (can_fall_left && can_fall_right);
7724       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7725
7726       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7727       {
7728         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7729           can_fall_right = FALSE;
7730         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7731           can_fall_left = FALSE;
7732         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7733           can_fall_right = FALSE;
7734         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7735           can_fall_left = FALSE;
7736
7737         can_fall_any  = (can_fall_left || can_fall_right);
7738         can_fall_both = FALSE;
7739       }
7740
7741       if (can_fall_both)
7742       {
7743         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7744           can_fall_right = FALSE;       // slip down on left side
7745         else
7746           can_fall_left = !(can_fall_right = RND(2));
7747
7748         can_fall_both = FALSE;
7749       }
7750
7751       if (can_fall_any)
7752       {
7753         // if not determined otherwise, prefer left side for slipping down
7754         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7755         started_moving = TRUE;
7756       }
7757     }
7758     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7759     {
7760       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7761       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7762       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7763       int belt_dir = game.belt_dir[belt_nr];
7764
7765       if ((belt_dir == MV_LEFT  && left_is_free) ||
7766           (belt_dir == MV_RIGHT && right_is_free))
7767       {
7768         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7769
7770         InitMovingField(x, y, belt_dir);
7771         started_moving = TRUE;
7772
7773         Pushed[x][y] = TRUE;
7774         Pushed[nextx][y] = TRUE;
7775
7776         GfxAction[x][y] = ACTION_DEFAULT;
7777       }
7778       else
7779       {
7780         MovDir[x][y] = 0;       // if element was moving, stop it
7781       }
7782     }
7783   }
7784
7785   // not "else if" because of elements that can fall and move (EL_SPRING)
7786   if (CAN_MOVE(element) && !started_moving)
7787   {
7788     int move_pattern = element_info[element].move_pattern;
7789     int newx, newy;
7790
7791     Moving2Blocked(x, y, &newx, &newy);
7792
7793     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7794       return;
7795
7796     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7797         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7798     {
7799       WasJustMoving[x][y] = 0;
7800       CheckCollision[x][y] = 0;
7801
7802       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7803
7804       if (Feld[x][y] != element)        // element has changed
7805         return;
7806     }
7807
7808     if (!MovDelay[x][y])        // start new movement phase
7809     {
7810       // all objects that can change their move direction after each step
7811       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7812
7813       if (element != EL_YAMYAM &&
7814           element != EL_DARK_YAMYAM &&
7815           element != EL_PACMAN &&
7816           !(move_pattern & MV_ANY_DIRECTION) &&
7817           move_pattern != MV_TURNING_LEFT &&
7818           move_pattern != MV_TURNING_RIGHT &&
7819           move_pattern != MV_TURNING_LEFT_RIGHT &&
7820           move_pattern != MV_TURNING_RIGHT_LEFT &&
7821           move_pattern != MV_TURNING_RANDOM)
7822       {
7823         TurnRound(x, y);
7824
7825         if (MovDelay[x][y] && (element == EL_BUG ||
7826                                element == EL_SPACESHIP ||
7827                                element == EL_SP_SNIKSNAK ||
7828                                element == EL_SP_ELECTRON ||
7829                                element == EL_MOLE))
7830           TEST_DrawLevelField(x, y);
7831       }
7832     }
7833
7834     if (MovDelay[x][y])         // wait some time before next movement
7835     {
7836       MovDelay[x][y]--;
7837
7838       if (element == EL_ROBOT ||
7839           element == EL_YAMYAM ||
7840           element == EL_DARK_YAMYAM)
7841       {
7842         DrawLevelElementAnimationIfNeeded(x, y, element);
7843         PlayLevelSoundAction(x, y, ACTION_WAITING);
7844       }
7845       else if (element == EL_SP_ELECTRON)
7846         DrawLevelElementAnimationIfNeeded(x, y, element);
7847       else if (element == EL_DRAGON)
7848       {
7849         int i;
7850         int dir = MovDir[x][y];
7851         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7852         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7853         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7854                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7855                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7856                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7857         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7858
7859         GfxAction[x][y] = ACTION_ATTACKING;
7860
7861         if (IS_PLAYER(x, y))
7862           DrawPlayerField(x, y);
7863         else
7864           TEST_DrawLevelField(x, y);
7865
7866         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7867
7868         for (i = 1; i <= 3; i++)
7869         {
7870           int xx = x + i * dx;
7871           int yy = y + i * dy;
7872           int sx = SCREENX(xx);
7873           int sy = SCREENY(yy);
7874           int flame_graphic = graphic + (i - 1);
7875
7876           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7877             break;
7878
7879           if (MovDelay[x][y])
7880           {
7881             int flamed = MovingOrBlocked2Element(xx, yy);
7882
7883             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7884               Bang(xx, yy);
7885             else
7886               RemoveMovingField(xx, yy);
7887
7888             ChangeDelay[xx][yy] = 0;
7889
7890             Feld[xx][yy] = EL_FLAMES;
7891
7892             if (IN_SCR_FIELD(sx, sy))
7893             {
7894               TEST_DrawLevelFieldCrumbled(xx, yy);
7895               DrawGraphic(sx, sy, flame_graphic, frame);
7896             }
7897           }
7898           else
7899           {
7900             if (Feld[xx][yy] == EL_FLAMES)
7901               Feld[xx][yy] = EL_EMPTY;
7902             TEST_DrawLevelField(xx, yy);
7903           }
7904         }
7905       }
7906
7907       if (MovDelay[x][y])       // element still has to wait some time
7908       {
7909         PlayLevelSoundAction(x, y, ACTION_WAITING);
7910
7911         return;
7912       }
7913     }
7914
7915     // now make next step
7916
7917     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7918
7919     if (DONT_COLLIDE_WITH(element) &&
7920         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7921         !PLAYER_ENEMY_PROTECTED(newx, newy))
7922     {
7923       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7924
7925       return;
7926     }
7927
7928     else if (CAN_MOVE_INTO_ACID(element) &&
7929              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7930              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7931              (MovDir[x][y] == MV_DOWN ||
7932               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7933     {
7934       SplashAcid(newx, newy);
7935       Store[x][y] = EL_ACID;
7936     }
7937     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7938     {
7939       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7940           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7941           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7942           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7943       {
7944         RemoveField(x, y);
7945         TEST_DrawLevelField(x, y);
7946
7947         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7948         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7949           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7950
7951         game.friends_still_needed--;
7952         if (!game.friends_still_needed &&
7953             !game.GameOver &&
7954             game.all_players_gone)
7955           LevelSolved();
7956
7957         return;
7958       }
7959       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7960       {
7961         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7962           TEST_DrawLevelField(newx, newy);
7963         else
7964           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7965       }
7966       else if (!IS_FREE(newx, newy))
7967       {
7968         GfxAction[x][y] = ACTION_WAITING;
7969
7970         if (IS_PLAYER(x, y))
7971           DrawPlayerField(x, y);
7972         else
7973           TEST_DrawLevelField(x, y);
7974
7975         return;
7976       }
7977     }
7978     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7979     {
7980       if (IS_FOOD_PIG(Feld[newx][newy]))
7981       {
7982         if (IS_MOVING(newx, newy))
7983           RemoveMovingField(newx, newy);
7984         else
7985         {
7986           Feld[newx][newy] = EL_EMPTY;
7987           TEST_DrawLevelField(newx, newy);
7988         }
7989
7990         PlayLevelSound(x, y, SND_PIG_DIGGING);
7991       }
7992       else if (!IS_FREE(newx, newy))
7993       {
7994         if (IS_PLAYER(x, y))
7995           DrawPlayerField(x, y);
7996         else
7997           TEST_DrawLevelField(x, y);
7998
7999         return;
8000       }
8001     }
8002     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8003     {
8004       if (Store[x][y] != EL_EMPTY)
8005       {
8006         boolean can_clone = FALSE;
8007         int xx, yy;
8008
8009         // check if element to clone is still there
8010         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8011         {
8012           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8013           {
8014             can_clone = TRUE;
8015
8016             break;
8017           }
8018         }
8019
8020         // cannot clone or target field not free anymore -- do not clone
8021         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8022           Store[x][y] = EL_EMPTY;
8023       }
8024
8025       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8026       {
8027         if (IS_MV_DIAGONAL(MovDir[x][y]))
8028         {
8029           int diagonal_move_dir = MovDir[x][y];
8030           int stored = Store[x][y];
8031           int change_delay = 8;
8032           int graphic;
8033
8034           // android is moving diagonally
8035
8036           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8037
8038           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8039           GfxElement[x][y] = EL_EMC_ANDROID;
8040           GfxAction[x][y] = ACTION_SHRINKING;
8041           GfxDir[x][y] = diagonal_move_dir;
8042           ChangeDelay[x][y] = change_delay;
8043
8044           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8045                                    GfxDir[x][y]);
8046
8047           DrawLevelGraphicAnimation(x, y, graphic);
8048           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8049
8050           if (Feld[newx][newy] == EL_ACID)
8051           {
8052             SplashAcid(newx, newy);
8053
8054             return;
8055           }
8056
8057           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8058
8059           Store[newx][newy] = EL_EMC_ANDROID;
8060           GfxElement[newx][newy] = EL_EMC_ANDROID;
8061           GfxAction[newx][newy] = ACTION_GROWING;
8062           GfxDir[newx][newy] = diagonal_move_dir;
8063           ChangeDelay[newx][newy] = change_delay;
8064
8065           graphic = el_act_dir2img(GfxElement[newx][newy],
8066                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8067
8068           DrawLevelGraphicAnimation(newx, newy, graphic);
8069           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8070
8071           return;
8072         }
8073         else
8074         {
8075           Feld[newx][newy] = EL_EMPTY;
8076           TEST_DrawLevelField(newx, newy);
8077
8078           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8079         }
8080       }
8081       else if (!IS_FREE(newx, newy))
8082       {
8083         return;
8084       }
8085     }
8086     else if (IS_CUSTOM_ELEMENT(element) &&
8087              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8088     {
8089       if (!DigFieldByCE(newx, newy, element))
8090         return;
8091
8092       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8093       {
8094         RunnerVisit[x][y] = FrameCounter;
8095         PlayerVisit[x][y] /= 8;         // expire player visit path
8096       }
8097     }
8098     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8099     {
8100       if (!IS_FREE(newx, newy))
8101       {
8102         if (IS_PLAYER(x, y))
8103           DrawPlayerField(x, y);
8104         else
8105           TEST_DrawLevelField(x, y);
8106
8107         return;
8108       }
8109       else
8110       {
8111         boolean wanna_flame = !RND(10);
8112         int dx = newx - x, dy = newy - y;
8113         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8114         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8115         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8116                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8117         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8118                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8119
8120         if ((wanna_flame ||
8121              IS_CLASSIC_ENEMY(element1) ||
8122              IS_CLASSIC_ENEMY(element2)) &&
8123             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8124             element1 != EL_FLAMES && element2 != EL_FLAMES)
8125         {
8126           ResetGfxAnimation(x, y);
8127           GfxAction[x][y] = ACTION_ATTACKING;
8128
8129           if (IS_PLAYER(x, y))
8130             DrawPlayerField(x, y);
8131           else
8132             TEST_DrawLevelField(x, y);
8133
8134           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8135
8136           MovDelay[x][y] = 50;
8137
8138           Feld[newx][newy] = EL_FLAMES;
8139           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8140             Feld[newx1][newy1] = EL_FLAMES;
8141           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8142             Feld[newx2][newy2] = EL_FLAMES;
8143
8144           return;
8145         }
8146       }
8147     }
8148     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8149              Feld[newx][newy] == EL_DIAMOND)
8150     {
8151       if (IS_MOVING(newx, newy))
8152         RemoveMovingField(newx, newy);
8153       else
8154       {
8155         Feld[newx][newy] = EL_EMPTY;
8156         TEST_DrawLevelField(newx, newy);
8157       }
8158
8159       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8160     }
8161     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8162              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8163     {
8164       if (AmoebaNr[newx][newy])
8165       {
8166         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8167         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8168             Feld[newx][newy] == EL_BD_AMOEBA)
8169           AmoebaCnt[AmoebaNr[newx][newy]]--;
8170       }
8171
8172       if (IS_MOVING(newx, newy))
8173       {
8174         RemoveMovingField(newx, newy);
8175       }
8176       else
8177       {
8178         Feld[newx][newy] = EL_EMPTY;
8179         TEST_DrawLevelField(newx, newy);
8180       }
8181
8182       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8183     }
8184     else if ((element == EL_PACMAN || element == EL_MOLE)
8185              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8186     {
8187       if (AmoebaNr[newx][newy])
8188       {
8189         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8190         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8191             Feld[newx][newy] == EL_BD_AMOEBA)
8192           AmoebaCnt[AmoebaNr[newx][newy]]--;
8193       }
8194
8195       if (element == EL_MOLE)
8196       {
8197         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8198         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8199
8200         ResetGfxAnimation(x, y);
8201         GfxAction[x][y] = ACTION_DIGGING;
8202         TEST_DrawLevelField(x, y);
8203
8204         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8205
8206         return;                         // wait for shrinking amoeba
8207       }
8208       else      // element == EL_PACMAN
8209       {
8210         Feld[newx][newy] = EL_EMPTY;
8211         TEST_DrawLevelField(newx, newy);
8212         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8213       }
8214     }
8215     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8216              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8217               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8218     {
8219       // wait for shrinking amoeba to completely disappear
8220       return;
8221     }
8222     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8223     {
8224       // object was running against a wall
8225
8226       TurnRound(x, y);
8227
8228       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8229         DrawLevelElementAnimation(x, y, element);
8230
8231       if (DONT_TOUCH(element))
8232         TestIfBadThingTouchesPlayer(x, y);
8233
8234       return;
8235     }
8236
8237     InitMovingField(x, y, MovDir[x][y]);
8238
8239     PlayLevelSoundAction(x, y, ACTION_MOVING);
8240   }
8241
8242   if (MovDir[x][y])
8243     ContinueMoving(x, y);
8244 }
8245
8246 void ContinueMoving(int x, int y)
8247 {
8248   int element = Feld[x][y];
8249   struct ElementInfo *ei = &element_info[element];
8250   int direction = MovDir[x][y];
8251   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8252   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8253   int newx = x + dx, newy = y + dy;
8254   int stored = Store[x][y];
8255   int stored_new = Store[newx][newy];
8256   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8257   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8258   boolean last_line = (newy == lev_fieldy - 1);
8259
8260   MovPos[x][y] += getElementMoveStepsize(x, y);
8261
8262   if (pushed_by_player) // special case: moving object pushed by player
8263     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8264
8265   if (ABS(MovPos[x][y]) < TILEX)
8266   {
8267     TEST_DrawLevelField(x, y);
8268
8269     return;     // element is still moving
8270   }
8271
8272   // element reached destination field
8273
8274   Feld[x][y] = EL_EMPTY;
8275   Feld[newx][newy] = element;
8276   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8277
8278   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8279   {
8280     element = Feld[newx][newy] = EL_ACID;
8281   }
8282   else if (element == EL_MOLE)
8283   {
8284     Feld[x][y] = EL_SAND;
8285
8286     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8287   }
8288   else if (element == EL_QUICKSAND_FILLING)
8289   {
8290     element = Feld[newx][newy] = get_next_element(element);
8291     Store[newx][newy] = Store[x][y];
8292   }
8293   else if (element == EL_QUICKSAND_EMPTYING)
8294   {
8295     Feld[x][y] = get_next_element(element);
8296     element = Feld[newx][newy] = Store[x][y];
8297   }
8298   else if (element == EL_QUICKSAND_FAST_FILLING)
8299   {
8300     element = Feld[newx][newy] = get_next_element(element);
8301     Store[newx][newy] = Store[x][y];
8302   }
8303   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8304   {
8305     Feld[x][y] = get_next_element(element);
8306     element = Feld[newx][newy] = Store[x][y];
8307   }
8308   else if (element == EL_MAGIC_WALL_FILLING)
8309   {
8310     element = Feld[newx][newy] = get_next_element(element);
8311     if (!game.magic_wall_active)
8312       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8313     Store[newx][newy] = Store[x][y];
8314   }
8315   else if (element == EL_MAGIC_WALL_EMPTYING)
8316   {
8317     Feld[x][y] = get_next_element(element);
8318     if (!game.magic_wall_active)
8319       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8320     element = Feld[newx][newy] = Store[x][y];
8321
8322     InitField(newx, newy, FALSE);
8323   }
8324   else if (element == EL_BD_MAGIC_WALL_FILLING)
8325   {
8326     element = Feld[newx][newy] = get_next_element(element);
8327     if (!game.magic_wall_active)
8328       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8329     Store[newx][newy] = Store[x][y];
8330   }
8331   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8332   {
8333     Feld[x][y] = get_next_element(element);
8334     if (!game.magic_wall_active)
8335       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8336     element = Feld[newx][newy] = Store[x][y];
8337
8338     InitField(newx, newy, FALSE);
8339   }
8340   else if (element == EL_DC_MAGIC_WALL_FILLING)
8341   {
8342     element = Feld[newx][newy] = get_next_element(element);
8343     if (!game.magic_wall_active)
8344       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8345     Store[newx][newy] = Store[x][y];
8346   }
8347   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8348   {
8349     Feld[x][y] = get_next_element(element);
8350     if (!game.magic_wall_active)
8351       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8352     element = Feld[newx][newy] = Store[x][y];
8353
8354     InitField(newx, newy, FALSE);
8355   }
8356   else if (element == EL_AMOEBA_DROPPING)
8357   {
8358     Feld[x][y] = get_next_element(element);
8359     element = Feld[newx][newy] = Store[x][y];
8360   }
8361   else if (element == EL_SOKOBAN_OBJECT)
8362   {
8363     if (Back[x][y])
8364       Feld[x][y] = Back[x][y];
8365
8366     if (Back[newx][newy])
8367       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8368
8369     Back[x][y] = Back[newx][newy] = 0;
8370   }
8371
8372   Store[x][y] = EL_EMPTY;
8373   MovPos[x][y] = 0;
8374   MovDir[x][y] = 0;
8375   MovDelay[x][y] = 0;
8376
8377   MovDelay[newx][newy] = 0;
8378
8379   if (CAN_CHANGE_OR_HAS_ACTION(element))
8380   {
8381     // copy element change control values to new field
8382     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8383     ChangePage[newx][newy]  = ChangePage[x][y];
8384     ChangeCount[newx][newy] = ChangeCount[x][y];
8385     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8386   }
8387
8388   CustomValue[newx][newy] = CustomValue[x][y];
8389
8390   ChangeDelay[x][y] = 0;
8391   ChangePage[x][y] = -1;
8392   ChangeCount[x][y] = 0;
8393   ChangeEvent[x][y] = -1;
8394
8395   CustomValue[x][y] = 0;
8396
8397   // copy animation control values to new field
8398   GfxFrame[newx][newy]  = GfxFrame[x][y];
8399   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8400   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8401   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8402
8403   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8404
8405   // some elements can leave other elements behind after moving
8406   if (ei->move_leave_element != EL_EMPTY &&
8407       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8408       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8409   {
8410     int move_leave_element = ei->move_leave_element;
8411
8412     // this makes it possible to leave the removed element again
8413     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8414       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8415
8416     Feld[x][y] = move_leave_element;
8417
8418     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8419       MovDir[x][y] = direction;
8420
8421     InitField(x, y, FALSE);
8422
8423     if (GFX_CRUMBLED(Feld[x][y]))
8424       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8425
8426     if (ELEM_IS_PLAYER(move_leave_element))
8427       RelocatePlayer(x, y, move_leave_element);
8428   }
8429
8430   // do this after checking for left-behind element
8431   ResetGfxAnimation(x, y);      // reset animation values for old field
8432
8433   if (!CAN_MOVE(element) ||
8434       (CAN_FALL(element) && direction == MV_DOWN &&
8435        (element == EL_SPRING ||
8436         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8437         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8438     GfxDir[x][y] = MovDir[newx][newy] = 0;
8439
8440   TEST_DrawLevelField(x, y);
8441   TEST_DrawLevelField(newx, newy);
8442
8443   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8444
8445   // prevent pushed element from moving on in pushed direction
8446   if (pushed_by_player && CAN_MOVE(element) &&
8447       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8448       !(element_info[element].move_pattern & direction))
8449     TurnRound(newx, newy);
8450
8451   // prevent elements on conveyor belt from moving on in last direction
8452   if (pushed_by_conveyor && CAN_FALL(element) &&
8453       direction & MV_HORIZONTAL)
8454     MovDir[newx][newy] = 0;
8455
8456   if (!pushed_by_player)
8457   {
8458     int nextx = newx + dx, nexty = newy + dy;
8459     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8460
8461     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8462
8463     if (CAN_FALL(element) && direction == MV_DOWN)
8464       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8465
8466     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8467       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8468
8469     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8470       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8471   }
8472
8473   if (DONT_TOUCH(element))      // object may be nasty to player or others
8474   {
8475     TestIfBadThingTouchesPlayer(newx, newy);
8476     TestIfBadThingTouchesFriend(newx, newy);
8477
8478     if (!IS_CUSTOM_ELEMENT(element))
8479       TestIfBadThingTouchesOtherBadThing(newx, newy);
8480   }
8481   else if (element == EL_PENGUIN)
8482     TestIfFriendTouchesBadThing(newx, newy);
8483
8484   if (DONT_GET_HIT_BY(element))
8485   {
8486     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8487   }
8488
8489   // give the player one last chance (one more frame) to move away
8490   if (CAN_FALL(element) && direction == MV_DOWN &&
8491       (last_line || (!IS_FREE(x, newy + 1) &&
8492                      (!IS_PLAYER(x, newy + 1) ||
8493                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8494     Impact(x, newy);
8495
8496   if (pushed_by_player && !game.use_change_when_pushing_bug)
8497   {
8498     int push_side = MV_DIR_OPPOSITE(direction);
8499     struct PlayerInfo *player = PLAYERINFO(x, y);
8500
8501     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8502                                player->index_bit, push_side);
8503     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8504                                         player->index_bit, push_side);
8505   }
8506
8507   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8508     MovDelay[newx][newy] = 1;
8509
8510   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8511
8512   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8513   TestIfElementHitsCustomElement(newx, newy, direction);
8514   TestIfPlayerTouchesCustomElement(newx, newy);
8515   TestIfElementTouchesCustomElement(newx, newy);
8516
8517   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8518       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8519     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8520                              MV_DIR_OPPOSITE(direction));
8521 }
8522
8523 int AmoebeNachbarNr(int ax, int ay)
8524 {
8525   int i;
8526   int element = Feld[ax][ay];
8527   int group_nr = 0;
8528   static int xy[4][2] =
8529   {
8530     { 0, -1 },
8531     { -1, 0 },
8532     { +1, 0 },
8533     { 0, +1 }
8534   };
8535
8536   for (i = 0; i < NUM_DIRECTIONS; i++)
8537   {
8538     int x = ax + xy[i][0];
8539     int y = ay + xy[i][1];
8540
8541     if (!IN_LEV_FIELD(x, y))
8542       continue;
8543
8544     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8545       group_nr = AmoebaNr[x][y];
8546   }
8547
8548   return group_nr;
8549 }
8550
8551 static void AmoebenVereinigen(int ax, int ay)
8552 {
8553   int i, x, y, xx, yy;
8554   int new_group_nr = AmoebaNr[ax][ay];
8555   static int xy[4][2] =
8556   {
8557     { 0, -1 },
8558     { -1, 0 },
8559     { +1, 0 },
8560     { 0, +1 }
8561   };
8562
8563   if (new_group_nr == 0)
8564     return;
8565
8566   for (i = 0; i < NUM_DIRECTIONS; i++)
8567   {
8568     x = ax + xy[i][0];
8569     y = ay + xy[i][1];
8570
8571     if (!IN_LEV_FIELD(x, y))
8572       continue;
8573
8574     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8575          Feld[x][y] == EL_BD_AMOEBA ||
8576          Feld[x][y] == EL_AMOEBA_DEAD) &&
8577         AmoebaNr[x][y] != new_group_nr)
8578     {
8579       int old_group_nr = AmoebaNr[x][y];
8580
8581       if (old_group_nr == 0)
8582         return;
8583
8584       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8585       AmoebaCnt[old_group_nr] = 0;
8586       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8587       AmoebaCnt2[old_group_nr] = 0;
8588
8589       SCAN_PLAYFIELD(xx, yy)
8590       {
8591         if (AmoebaNr[xx][yy] == old_group_nr)
8592           AmoebaNr[xx][yy] = new_group_nr;
8593       }
8594     }
8595   }
8596 }
8597
8598 void AmoebeUmwandeln(int ax, int ay)
8599 {
8600   int i, x, y;
8601
8602   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8603   {
8604     int group_nr = AmoebaNr[ax][ay];
8605
8606 #ifdef DEBUG
8607     if (group_nr == 0)
8608     {
8609       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8610       printf("AmoebeUmwandeln(): This should never happen!\n");
8611       return;
8612     }
8613 #endif
8614
8615     SCAN_PLAYFIELD(x, y)
8616     {
8617       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8618       {
8619         AmoebaNr[x][y] = 0;
8620         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8621       }
8622     }
8623
8624     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8625                             SND_AMOEBA_TURNING_TO_GEM :
8626                             SND_AMOEBA_TURNING_TO_ROCK));
8627     Bang(ax, ay);
8628   }
8629   else
8630   {
8631     static int xy[4][2] =
8632     {
8633       { 0, -1 },
8634       { -1, 0 },
8635       { +1, 0 },
8636       { 0, +1 }
8637     };
8638
8639     for (i = 0; i < NUM_DIRECTIONS; i++)
8640     {
8641       x = ax + xy[i][0];
8642       y = ay + xy[i][1];
8643
8644       if (!IN_LEV_FIELD(x, y))
8645         continue;
8646
8647       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8648       {
8649         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8650                               SND_AMOEBA_TURNING_TO_GEM :
8651                               SND_AMOEBA_TURNING_TO_ROCK));
8652         Bang(x, y);
8653       }
8654     }
8655   }
8656 }
8657
8658 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8659 {
8660   int x, y;
8661   int group_nr = AmoebaNr[ax][ay];
8662   boolean done = FALSE;
8663
8664 #ifdef DEBUG
8665   if (group_nr == 0)
8666   {
8667     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8668     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8669     return;
8670   }
8671 #endif
8672
8673   SCAN_PLAYFIELD(x, y)
8674   {
8675     if (AmoebaNr[x][y] == group_nr &&
8676         (Feld[x][y] == EL_AMOEBA_DEAD ||
8677          Feld[x][y] == EL_BD_AMOEBA ||
8678          Feld[x][y] == EL_AMOEBA_GROWING))
8679     {
8680       AmoebaNr[x][y] = 0;
8681       Feld[x][y] = new_element;
8682       InitField(x, y, FALSE);
8683       TEST_DrawLevelField(x, y);
8684       done = TRUE;
8685     }
8686   }
8687
8688   if (done)
8689     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8690                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8691                             SND_BD_AMOEBA_TURNING_TO_GEM));
8692 }
8693
8694 static void AmoebeWaechst(int x, int y)
8695 {
8696   static unsigned int sound_delay = 0;
8697   static unsigned int sound_delay_value = 0;
8698
8699   if (!MovDelay[x][y])          // start new growing cycle
8700   {
8701     MovDelay[x][y] = 7;
8702
8703     if (DelayReached(&sound_delay, sound_delay_value))
8704     {
8705       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8706       sound_delay_value = 30;
8707     }
8708   }
8709
8710   if (MovDelay[x][y])           // wait some time before growing bigger
8711   {
8712     MovDelay[x][y]--;
8713     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8714     {
8715       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8716                                            6 - MovDelay[x][y]);
8717
8718       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8719     }
8720
8721     if (!MovDelay[x][y])
8722     {
8723       Feld[x][y] = Store[x][y];
8724       Store[x][y] = 0;
8725       TEST_DrawLevelField(x, y);
8726     }
8727   }
8728 }
8729
8730 static void AmoebaDisappearing(int x, int y)
8731 {
8732   static unsigned int sound_delay = 0;
8733   static unsigned int sound_delay_value = 0;
8734
8735   if (!MovDelay[x][y])          // start new shrinking cycle
8736   {
8737     MovDelay[x][y] = 7;
8738
8739     if (DelayReached(&sound_delay, sound_delay_value))
8740       sound_delay_value = 30;
8741   }
8742
8743   if (MovDelay[x][y])           // wait some time before shrinking
8744   {
8745     MovDelay[x][y]--;
8746     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8747     {
8748       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8749                                            6 - MovDelay[x][y]);
8750
8751       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8752     }
8753
8754     if (!MovDelay[x][y])
8755     {
8756       Feld[x][y] = EL_EMPTY;
8757       TEST_DrawLevelField(x, y);
8758
8759       // don't let mole enter this field in this cycle;
8760       // (give priority to objects falling to this field from above)
8761       Stop[x][y] = TRUE;
8762     }
8763   }
8764 }
8765
8766 static void AmoebeAbleger(int ax, int ay)
8767 {
8768   int i;
8769   int element = Feld[ax][ay];
8770   int graphic = el2img(element);
8771   int newax = ax, neway = ay;
8772   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8773   static int xy[4][2] =
8774   {
8775     { 0, -1 },
8776     { -1, 0 },
8777     { +1, 0 },
8778     { 0, +1 }
8779   };
8780
8781   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8782   {
8783     Feld[ax][ay] = EL_AMOEBA_DEAD;
8784     TEST_DrawLevelField(ax, ay);
8785     return;
8786   }
8787
8788   if (IS_ANIMATED(graphic))
8789     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8790
8791   if (!MovDelay[ax][ay])        // start making new amoeba field
8792     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8793
8794   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8795   {
8796     MovDelay[ax][ay]--;
8797     if (MovDelay[ax][ay])
8798       return;
8799   }
8800
8801   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8802   {
8803     int start = RND(4);
8804     int x = ax + xy[start][0];
8805     int y = ay + xy[start][1];
8806
8807     if (!IN_LEV_FIELD(x, y))
8808       return;
8809
8810     if (IS_FREE(x, y) ||
8811         CAN_GROW_INTO(Feld[x][y]) ||
8812         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8813         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8814     {
8815       newax = x;
8816       neway = y;
8817     }
8818
8819     if (newax == ax && neway == ay)
8820       return;
8821   }
8822   else                          // normal or "filled" (BD style) amoeba
8823   {
8824     int start = RND(4);
8825     boolean waiting_for_player = FALSE;
8826
8827     for (i = 0; i < NUM_DIRECTIONS; i++)
8828     {
8829       int j = (start + i) % 4;
8830       int x = ax + xy[j][0];
8831       int y = ay + xy[j][1];
8832
8833       if (!IN_LEV_FIELD(x, y))
8834         continue;
8835
8836       if (IS_FREE(x, y) ||
8837           CAN_GROW_INTO(Feld[x][y]) ||
8838           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8839           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8840       {
8841         newax = x;
8842         neway = y;
8843         break;
8844       }
8845       else if (IS_PLAYER(x, y))
8846         waiting_for_player = TRUE;
8847     }
8848
8849     if (newax == ax && neway == ay)             // amoeba cannot grow
8850     {
8851       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8852       {
8853         Feld[ax][ay] = EL_AMOEBA_DEAD;
8854         TEST_DrawLevelField(ax, ay);
8855         AmoebaCnt[AmoebaNr[ax][ay]]--;
8856
8857         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8858         {
8859           if (element == EL_AMOEBA_FULL)
8860             AmoebeUmwandeln(ax, ay);
8861           else if (element == EL_BD_AMOEBA)
8862             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8863         }
8864       }
8865       return;
8866     }
8867     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8868     {
8869       // amoeba gets larger by growing in some direction
8870
8871       int new_group_nr = AmoebaNr[ax][ay];
8872
8873 #ifdef DEBUG
8874   if (new_group_nr == 0)
8875   {
8876     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8877     printf("AmoebeAbleger(): This should never happen!\n");
8878     return;
8879   }
8880 #endif
8881
8882       AmoebaNr[newax][neway] = new_group_nr;
8883       AmoebaCnt[new_group_nr]++;
8884       AmoebaCnt2[new_group_nr]++;
8885
8886       // if amoeba touches other amoeba(s) after growing, unify them
8887       AmoebenVereinigen(newax, neway);
8888
8889       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8890       {
8891         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8892         return;
8893       }
8894     }
8895   }
8896
8897   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8898       (neway == lev_fieldy - 1 && newax != ax))
8899   {
8900     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8901     Store[newax][neway] = element;
8902   }
8903   else if (neway == ay || element == EL_EMC_DRIPPER)
8904   {
8905     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8906
8907     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8908   }
8909   else
8910   {
8911     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8912     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8913     Store[ax][ay] = EL_AMOEBA_DROP;
8914     ContinueMoving(ax, ay);
8915     return;
8916   }
8917
8918   TEST_DrawLevelField(newax, neway);
8919 }
8920
8921 static void Life(int ax, int ay)
8922 {
8923   int x1, y1, x2, y2;
8924   int life_time = 40;
8925   int element = Feld[ax][ay];
8926   int graphic = el2img(element);
8927   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8928                          level.biomaze);
8929   boolean changed = FALSE;
8930
8931   if (IS_ANIMATED(graphic))
8932     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8933
8934   if (Stop[ax][ay])
8935     return;
8936
8937   if (!MovDelay[ax][ay])        // start new "game of life" cycle
8938     MovDelay[ax][ay] = life_time;
8939
8940   if (MovDelay[ax][ay])         // wait some time before next cycle
8941   {
8942     MovDelay[ax][ay]--;
8943     if (MovDelay[ax][ay])
8944       return;
8945   }
8946
8947   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8948   {
8949     int xx = ax+x1, yy = ay+y1;
8950     int old_element = Feld[xx][yy];
8951     int num_neighbours = 0;
8952
8953     if (!IN_LEV_FIELD(xx, yy))
8954       continue;
8955
8956     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8957     {
8958       int x = xx+x2, y = yy+y2;
8959
8960       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8961         continue;
8962
8963       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
8964       boolean is_neighbour = FALSE;
8965
8966       if (level.use_life_bugs)
8967         is_neighbour =
8968           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
8969            (IS_FREE(x, y)                             &&  Stop[x][y]));
8970       else
8971         is_neighbour =
8972           (Last[x][y] == element || is_player_cell);
8973
8974       if (is_neighbour)
8975         num_neighbours++;
8976     }
8977
8978     boolean is_free = FALSE;
8979
8980     if (level.use_life_bugs)
8981       is_free = (IS_FREE(xx, yy));
8982     else
8983       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
8984
8985     if (xx == ax && yy == ay)           // field in the middle
8986     {
8987       if (num_neighbours < life_parameter[0] ||
8988           num_neighbours > life_parameter[1])
8989       {
8990         Feld[xx][yy] = EL_EMPTY;
8991         if (Feld[xx][yy] != old_element)
8992           TEST_DrawLevelField(xx, yy);
8993         Stop[xx][yy] = TRUE;
8994         changed = TRUE;
8995       }
8996     }
8997     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
8998     {                                   // free border field
8999       if (num_neighbours >= life_parameter[2] &&
9000           num_neighbours <= life_parameter[3])
9001       {
9002         Feld[xx][yy] = element;
9003         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9004         if (Feld[xx][yy] != old_element)
9005           TEST_DrawLevelField(xx, yy);
9006         Stop[xx][yy] = TRUE;
9007         changed = TRUE;
9008       }
9009     }
9010   }
9011
9012   if (changed)
9013     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9014                    SND_GAME_OF_LIFE_GROWING);
9015 }
9016
9017 static void InitRobotWheel(int x, int y)
9018 {
9019   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9020 }
9021
9022 static void RunRobotWheel(int x, int y)
9023 {
9024   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9025 }
9026
9027 static void StopRobotWheel(int x, int y)
9028 {
9029   if (ZX == x && ZY == y)
9030   {
9031     ZX = ZY = -1;
9032
9033     game.robot_wheel_active = FALSE;
9034   }
9035 }
9036
9037 static void InitTimegateWheel(int x, int y)
9038 {
9039   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9040 }
9041
9042 static void RunTimegateWheel(int x, int y)
9043 {
9044   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9045 }
9046
9047 static void InitMagicBallDelay(int x, int y)
9048 {
9049   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9050 }
9051
9052 static void ActivateMagicBall(int bx, int by)
9053 {
9054   int x, y;
9055
9056   if (level.ball_random)
9057   {
9058     int pos_border = RND(8);    // select one of the eight border elements
9059     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9060     int xx = pos_content % 3;
9061     int yy = pos_content / 3;
9062
9063     x = bx - 1 + xx;
9064     y = by - 1 + yy;
9065
9066     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9067       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9068   }
9069   else
9070   {
9071     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9072     {
9073       int xx = x - bx + 1;
9074       int yy = y - by + 1;
9075
9076       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9077         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9078     }
9079   }
9080
9081   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9082 }
9083
9084 static void CheckExit(int x, int y)
9085 {
9086   if (game.gems_still_needed > 0 ||
9087       game.sokoban_fields_still_needed > 0 ||
9088       game.sokoban_objects_still_needed > 0 ||
9089       game.lights_still_needed > 0)
9090   {
9091     int element = Feld[x][y];
9092     int graphic = el2img(element);
9093
9094     if (IS_ANIMATED(graphic))
9095       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9096
9097     return;
9098   }
9099
9100   // do not re-open exit door closed after last player
9101   if (game.all_players_gone)
9102     return;
9103
9104   Feld[x][y] = EL_EXIT_OPENING;
9105
9106   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9107 }
9108
9109 static void CheckExitEM(int x, int y)
9110 {
9111   if (game.gems_still_needed > 0 ||
9112       game.sokoban_fields_still_needed > 0 ||
9113       game.sokoban_objects_still_needed > 0 ||
9114       game.lights_still_needed > 0)
9115   {
9116     int element = Feld[x][y];
9117     int graphic = el2img(element);
9118
9119     if (IS_ANIMATED(graphic))
9120       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9121
9122     return;
9123   }
9124
9125   // do not re-open exit door closed after last player
9126   if (game.all_players_gone)
9127     return;
9128
9129   Feld[x][y] = EL_EM_EXIT_OPENING;
9130
9131   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9132 }
9133
9134 static void CheckExitSteel(int x, int y)
9135 {
9136   if (game.gems_still_needed > 0 ||
9137       game.sokoban_fields_still_needed > 0 ||
9138       game.sokoban_objects_still_needed > 0 ||
9139       game.lights_still_needed > 0)
9140   {
9141     int element = Feld[x][y];
9142     int graphic = el2img(element);
9143
9144     if (IS_ANIMATED(graphic))
9145       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9146
9147     return;
9148   }
9149
9150   // do not re-open exit door closed after last player
9151   if (game.all_players_gone)
9152     return;
9153
9154   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9155
9156   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9157 }
9158
9159 static void CheckExitSteelEM(int x, int y)
9160 {
9161   if (game.gems_still_needed > 0 ||
9162       game.sokoban_fields_still_needed > 0 ||
9163       game.sokoban_objects_still_needed > 0 ||
9164       game.lights_still_needed > 0)
9165   {
9166     int element = Feld[x][y];
9167     int graphic = el2img(element);
9168
9169     if (IS_ANIMATED(graphic))
9170       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9171
9172     return;
9173   }
9174
9175   // do not re-open exit door closed after last player
9176   if (game.all_players_gone)
9177     return;
9178
9179   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9180
9181   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9182 }
9183
9184 static void CheckExitSP(int x, int y)
9185 {
9186   if (game.gems_still_needed > 0)
9187   {
9188     int element = Feld[x][y];
9189     int graphic = el2img(element);
9190
9191     if (IS_ANIMATED(graphic))
9192       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9193
9194     return;
9195   }
9196
9197   // do not re-open exit door closed after last player
9198   if (game.all_players_gone)
9199     return;
9200
9201   Feld[x][y] = EL_SP_EXIT_OPENING;
9202
9203   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9204 }
9205
9206 static void CloseAllOpenTimegates(void)
9207 {
9208   int x, y;
9209
9210   SCAN_PLAYFIELD(x, y)
9211   {
9212     int element = Feld[x][y];
9213
9214     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9215     {
9216       Feld[x][y] = EL_TIMEGATE_CLOSING;
9217
9218       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9219     }
9220   }
9221 }
9222
9223 static void DrawTwinkleOnField(int x, int y)
9224 {
9225   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9226     return;
9227
9228   if (Feld[x][y] == EL_BD_DIAMOND)
9229     return;
9230
9231   if (MovDelay[x][y] == 0)      // next animation frame
9232     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9233
9234   if (MovDelay[x][y] != 0)      // wait some time before next frame
9235   {
9236     MovDelay[x][y]--;
9237
9238     DrawLevelElementAnimation(x, y, Feld[x][y]);
9239
9240     if (MovDelay[x][y] != 0)
9241     {
9242       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9243                                            10 - MovDelay[x][y]);
9244
9245       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9246     }
9247   }
9248 }
9249
9250 static void MauerWaechst(int x, int y)
9251 {
9252   int delay = 6;
9253
9254   if (!MovDelay[x][y])          // next animation frame
9255     MovDelay[x][y] = 3 * delay;
9256
9257   if (MovDelay[x][y])           // wait some time before next frame
9258   {
9259     MovDelay[x][y]--;
9260
9261     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9262     {
9263       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9264       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9265
9266       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9267     }
9268
9269     if (!MovDelay[x][y])
9270     {
9271       if (MovDir[x][y] == MV_LEFT)
9272       {
9273         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9274           TEST_DrawLevelField(x - 1, y);
9275       }
9276       else if (MovDir[x][y] == MV_RIGHT)
9277       {
9278         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9279           TEST_DrawLevelField(x + 1, y);
9280       }
9281       else if (MovDir[x][y] == MV_UP)
9282       {
9283         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9284           TEST_DrawLevelField(x, y - 1);
9285       }
9286       else
9287       {
9288         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9289           TEST_DrawLevelField(x, y + 1);
9290       }
9291
9292       Feld[x][y] = Store[x][y];
9293       Store[x][y] = 0;
9294       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9295       TEST_DrawLevelField(x, y);
9296     }
9297   }
9298 }
9299
9300 static void MauerAbleger(int ax, int ay)
9301 {
9302   int element = Feld[ax][ay];
9303   int graphic = el2img(element);
9304   boolean oben_frei = FALSE, unten_frei = FALSE;
9305   boolean links_frei = FALSE, rechts_frei = FALSE;
9306   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9307   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9308   boolean new_wall = FALSE;
9309
9310   if (IS_ANIMATED(graphic))
9311     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9312
9313   if (!MovDelay[ax][ay])        // start building new wall
9314     MovDelay[ax][ay] = 6;
9315
9316   if (MovDelay[ax][ay])         // wait some time before building new wall
9317   {
9318     MovDelay[ax][ay]--;
9319     if (MovDelay[ax][ay])
9320       return;
9321   }
9322
9323   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9324     oben_frei = TRUE;
9325   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9326     unten_frei = TRUE;
9327   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9328     links_frei = TRUE;
9329   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9330     rechts_frei = TRUE;
9331
9332   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9333       element == EL_EXPANDABLE_WALL_ANY)
9334   {
9335     if (oben_frei)
9336     {
9337       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9338       Store[ax][ay-1] = element;
9339       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9340       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9341         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9342                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9343       new_wall = TRUE;
9344     }
9345     if (unten_frei)
9346     {
9347       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9348       Store[ax][ay+1] = element;
9349       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9350       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9351         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9352                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9353       new_wall = TRUE;
9354     }
9355   }
9356
9357   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9358       element == EL_EXPANDABLE_WALL_ANY ||
9359       element == EL_EXPANDABLE_WALL ||
9360       element == EL_BD_EXPANDABLE_WALL)
9361   {
9362     if (links_frei)
9363     {
9364       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9365       Store[ax-1][ay] = element;
9366       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9367       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9368         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9369                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9370       new_wall = TRUE;
9371     }
9372
9373     if (rechts_frei)
9374     {
9375       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9376       Store[ax+1][ay] = element;
9377       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9378       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9379         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9380                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9381       new_wall = TRUE;
9382     }
9383   }
9384
9385   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9386     TEST_DrawLevelField(ax, ay);
9387
9388   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9389     oben_massiv = TRUE;
9390   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9391     unten_massiv = TRUE;
9392   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9393     links_massiv = TRUE;
9394   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9395     rechts_massiv = TRUE;
9396
9397   if (((oben_massiv && unten_massiv) ||
9398        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9399        element == EL_EXPANDABLE_WALL) &&
9400       ((links_massiv && rechts_massiv) ||
9401        element == EL_EXPANDABLE_WALL_VERTICAL))
9402     Feld[ax][ay] = EL_WALL;
9403
9404   if (new_wall)
9405     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9406 }
9407
9408 static void MauerAblegerStahl(int ax, int ay)
9409 {
9410   int element = Feld[ax][ay];
9411   int graphic = el2img(element);
9412   boolean oben_frei = FALSE, unten_frei = FALSE;
9413   boolean links_frei = FALSE, rechts_frei = FALSE;
9414   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9415   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9416   boolean new_wall = FALSE;
9417
9418   if (IS_ANIMATED(graphic))
9419     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9420
9421   if (!MovDelay[ax][ay])        // start building new wall
9422     MovDelay[ax][ay] = 6;
9423
9424   if (MovDelay[ax][ay])         // wait some time before building new wall
9425   {
9426     MovDelay[ax][ay]--;
9427     if (MovDelay[ax][ay])
9428       return;
9429   }
9430
9431   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9432     oben_frei = TRUE;
9433   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9434     unten_frei = TRUE;
9435   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9436     links_frei = TRUE;
9437   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9438     rechts_frei = TRUE;
9439
9440   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9441       element == EL_EXPANDABLE_STEELWALL_ANY)
9442   {
9443     if (oben_frei)
9444     {
9445       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9446       Store[ax][ay-1] = element;
9447       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9448       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9449         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9450                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9451       new_wall = TRUE;
9452     }
9453     if (unten_frei)
9454     {
9455       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9456       Store[ax][ay+1] = element;
9457       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9458       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9459         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9460                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9461       new_wall = TRUE;
9462     }
9463   }
9464
9465   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9466       element == EL_EXPANDABLE_STEELWALL_ANY)
9467   {
9468     if (links_frei)
9469     {
9470       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9471       Store[ax-1][ay] = element;
9472       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9473       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9474         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9475                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9476       new_wall = TRUE;
9477     }
9478
9479     if (rechts_frei)
9480     {
9481       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9482       Store[ax+1][ay] = element;
9483       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9484       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9485         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9486                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9487       new_wall = TRUE;
9488     }
9489   }
9490
9491   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9492     oben_massiv = TRUE;
9493   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9494     unten_massiv = TRUE;
9495   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9496     links_massiv = TRUE;
9497   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9498     rechts_massiv = TRUE;
9499
9500   if (((oben_massiv && unten_massiv) ||
9501        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9502       ((links_massiv && rechts_massiv) ||
9503        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9504     Feld[ax][ay] = EL_STEELWALL;
9505
9506   if (new_wall)
9507     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9508 }
9509
9510 static void CheckForDragon(int x, int y)
9511 {
9512   int i, j;
9513   boolean dragon_found = FALSE;
9514   static int xy[4][2] =
9515   {
9516     { 0, -1 },
9517     { -1, 0 },
9518     { +1, 0 },
9519     { 0, +1 }
9520   };
9521
9522   for (i = 0; i < NUM_DIRECTIONS; i++)
9523   {
9524     for (j = 0; j < 4; j++)
9525     {
9526       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9527
9528       if (IN_LEV_FIELD(xx, yy) &&
9529           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9530       {
9531         if (Feld[xx][yy] == EL_DRAGON)
9532           dragon_found = TRUE;
9533       }
9534       else
9535         break;
9536     }
9537   }
9538
9539   if (!dragon_found)
9540   {
9541     for (i = 0; i < NUM_DIRECTIONS; i++)
9542     {
9543       for (j = 0; j < 3; j++)
9544       {
9545         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9546   
9547         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9548         {
9549           Feld[xx][yy] = EL_EMPTY;
9550           TEST_DrawLevelField(xx, yy);
9551         }
9552         else
9553           break;
9554       }
9555     }
9556   }
9557 }
9558
9559 static void InitBuggyBase(int x, int y)
9560 {
9561   int element = Feld[x][y];
9562   int activating_delay = FRAMES_PER_SECOND / 4;
9563
9564   ChangeDelay[x][y] =
9565     (element == EL_SP_BUGGY_BASE ?
9566      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9567      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9568      activating_delay :
9569      element == EL_SP_BUGGY_BASE_ACTIVE ?
9570      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9571 }
9572
9573 static void WarnBuggyBase(int x, int y)
9574 {
9575   int i;
9576   static int xy[4][2] =
9577   {
9578     { 0, -1 },
9579     { -1, 0 },
9580     { +1, 0 },
9581     { 0, +1 }
9582   };
9583
9584   for (i = 0; i < NUM_DIRECTIONS; i++)
9585   {
9586     int xx = x + xy[i][0];
9587     int yy = y + xy[i][1];
9588
9589     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9590     {
9591       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9592
9593       break;
9594     }
9595   }
9596 }
9597
9598 static void InitTrap(int x, int y)
9599 {
9600   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9601 }
9602
9603 static void ActivateTrap(int x, int y)
9604 {
9605   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9606 }
9607
9608 static void ChangeActiveTrap(int x, int y)
9609 {
9610   int graphic = IMG_TRAP_ACTIVE;
9611
9612   // if new animation frame was drawn, correct crumbled sand border
9613   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9614     TEST_DrawLevelFieldCrumbled(x, y);
9615 }
9616
9617 static int getSpecialActionElement(int element, int number, int base_element)
9618 {
9619   return (element != EL_EMPTY ? element :
9620           number != -1 ? base_element + number - 1 :
9621           EL_EMPTY);
9622 }
9623
9624 static int getModifiedActionNumber(int value_old, int operator, int operand,
9625                                    int value_min, int value_max)
9626 {
9627   int value_new = (operator == CA_MODE_SET      ? operand :
9628                    operator == CA_MODE_ADD      ? value_old + operand :
9629                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9630                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9631                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9632                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9633                    value_old);
9634
9635   return (value_new < value_min ? value_min :
9636           value_new > value_max ? value_max :
9637           value_new);
9638 }
9639
9640 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9641 {
9642   struct ElementInfo *ei = &element_info[element];
9643   struct ElementChangeInfo *change = &ei->change_page[page];
9644   int target_element = change->target_element;
9645   int action_type = change->action_type;
9646   int action_mode = change->action_mode;
9647   int action_arg = change->action_arg;
9648   int action_element = change->action_element;
9649   int i;
9650
9651   if (!change->has_action)
9652     return;
9653
9654   // ---------- determine action paramater values -----------------------------
9655
9656   int level_time_value =
9657     (level.time > 0 ? TimeLeft :
9658      TimePlayed);
9659
9660   int action_arg_element_raw =
9661     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9662      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9663      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9664      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9665      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9666      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9667      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9668      EL_EMPTY);
9669   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9670
9671   int action_arg_direction =
9672     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9673      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9674      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9675      change->actual_trigger_side :
9676      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9677      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9678      MV_NONE);
9679
9680   int action_arg_number_min =
9681     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9682      CA_ARG_MIN);
9683
9684   int action_arg_number_max =
9685     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9686      action_type == CA_SET_LEVEL_GEMS ? 999 :
9687      action_type == CA_SET_LEVEL_TIME ? 9999 :
9688      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9689      action_type == CA_SET_CE_VALUE ? 9999 :
9690      action_type == CA_SET_CE_SCORE ? 9999 :
9691      CA_ARG_MAX);
9692
9693   int action_arg_number_reset =
9694     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9695      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9696      action_type == CA_SET_LEVEL_TIME ? level.time :
9697      action_type == CA_SET_LEVEL_SCORE ? 0 :
9698      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9699      action_type == CA_SET_CE_SCORE ? 0 :
9700      0);
9701
9702   int action_arg_number =
9703     (action_arg <= CA_ARG_MAX ? action_arg :
9704      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9705      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9706      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9707      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9708      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9709      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9710      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9711      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9712      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9713      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9714      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9715      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9716      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9717      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9718      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9719      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9720      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9721      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9722      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9723      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9724      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9725      -1);
9726
9727   int action_arg_number_old =
9728     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9729      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9730      action_type == CA_SET_LEVEL_SCORE ? game.score :
9731      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9732      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9733      0);
9734
9735   int action_arg_number_new =
9736     getModifiedActionNumber(action_arg_number_old,
9737                             action_mode, action_arg_number,
9738                             action_arg_number_min, action_arg_number_max);
9739
9740   int trigger_player_bits =
9741     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9742      change->actual_trigger_player_bits : change->trigger_player);
9743
9744   int action_arg_player_bits =
9745     (action_arg >= CA_ARG_PLAYER_1 &&
9746      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9747      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9748      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9749      PLAYER_BITS_ANY);
9750
9751   // ---------- execute action  -----------------------------------------------
9752
9753   switch (action_type)
9754   {
9755     case CA_NO_ACTION:
9756     {
9757       return;
9758     }
9759
9760     // ---------- level actions  ----------------------------------------------
9761
9762     case CA_RESTART_LEVEL:
9763     {
9764       game.restart_level = TRUE;
9765
9766       break;
9767     }
9768
9769     case CA_SHOW_ENVELOPE:
9770     {
9771       int element = getSpecialActionElement(action_arg_element,
9772                                             action_arg_number, EL_ENVELOPE_1);
9773
9774       if (IS_ENVELOPE(element))
9775         local_player->show_envelope = element;
9776
9777       break;
9778     }
9779
9780     case CA_SET_LEVEL_TIME:
9781     {
9782       if (level.time > 0)       // only modify limited time value
9783       {
9784         TimeLeft = action_arg_number_new;
9785
9786         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9787
9788         DisplayGameControlValues();
9789
9790         if (!TimeLeft && setup.time_limit)
9791           for (i = 0; i < MAX_PLAYERS; i++)
9792             KillPlayer(&stored_player[i]);
9793       }
9794
9795       break;
9796     }
9797
9798     case CA_SET_LEVEL_SCORE:
9799     {
9800       game.score = action_arg_number_new;
9801
9802       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9803
9804       DisplayGameControlValues();
9805
9806       break;
9807     }
9808
9809     case CA_SET_LEVEL_GEMS:
9810     {
9811       game.gems_still_needed = action_arg_number_new;
9812
9813       game.snapshot.collected_item = TRUE;
9814
9815       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9816
9817       DisplayGameControlValues();
9818
9819       break;
9820     }
9821
9822     case CA_SET_LEVEL_WIND:
9823     {
9824       game.wind_direction = action_arg_direction;
9825
9826       break;
9827     }
9828
9829     case CA_SET_LEVEL_RANDOM_SEED:
9830     {
9831       // ensure that setting a new random seed while playing is predictable
9832       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9833
9834       break;
9835     }
9836
9837     // ---------- player actions  ---------------------------------------------
9838
9839     case CA_MOVE_PLAYER:
9840     {
9841       // automatically move to the next field in specified direction
9842       for (i = 0; i < MAX_PLAYERS; i++)
9843         if (trigger_player_bits & (1 << i))
9844           stored_player[i].programmed_action = action_arg_direction;
9845
9846       break;
9847     }
9848
9849     case CA_EXIT_PLAYER:
9850     {
9851       for (i = 0; i < MAX_PLAYERS; i++)
9852         if (action_arg_player_bits & (1 << i))
9853           ExitPlayer(&stored_player[i]);
9854
9855       if (game.players_still_needed == 0)
9856         LevelSolved();
9857
9858       break;
9859     }
9860
9861     case CA_KILL_PLAYER:
9862     {
9863       for (i = 0; i < MAX_PLAYERS; i++)
9864         if (action_arg_player_bits & (1 << i))
9865           KillPlayer(&stored_player[i]);
9866
9867       break;
9868     }
9869
9870     case CA_SET_PLAYER_KEYS:
9871     {
9872       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9873       int element = getSpecialActionElement(action_arg_element,
9874                                             action_arg_number, EL_KEY_1);
9875
9876       if (IS_KEY(element))
9877       {
9878         for (i = 0; i < MAX_PLAYERS; i++)
9879         {
9880           if (trigger_player_bits & (1 << i))
9881           {
9882             stored_player[i].key[KEY_NR(element)] = key_state;
9883
9884             DrawGameDoorValues();
9885           }
9886         }
9887       }
9888
9889       break;
9890     }
9891
9892     case CA_SET_PLAYER_SPEED:
9893     {
9894       for (i = 0; i < MAX_PLAYERS; i++)
9895       {
9896         if (trigger_player_bits & (1 << i))
9897         {
9898           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9899
9900           if (action_arg == CA_ARG_SPEED_FASTER &&
9901               stored_player[i].cannot_move)
9902           {
9903             action_arg_number = STEPSIZE_VERY_SLOW;
9904           }
9905           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9906                    action_arg == CA_ARG_SPEED_FASTER)
9907           {
9908             action_arg_number = 2;
9909             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9910                            CA_MODE_MULTIPLY);
9911           }
9912           else if (action_arg == CA_ARG_NUMBER_RESET)
9913           {
9914             action_arg_number = level.initial_player_stepsize[i];
9915           }
9916
9917           move_stepsize =
9918             getModifiedActionNumber(move_stepsize,
9919                                     action_mode,
9920                                     action_arg_number,
9921                                     action_arg_number_min,
9922                                     action_arg_number_max);
9923
9924           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9925         }
9926       }
9927
9928       break;
9929     }
9930
9931     case CA_SET_PLAYER_SHIELD:
9932     {
9933       for (i = 0; i < MAX_PLAYERS; i++)
9934       {
9935         if (trigger_player_bits & (1 << i))
9936         {
9937           if (action_arg == CA_ARG_SHIELD_OFF)
9938           {
9939             stored_player[i].shield_normal_time_left = 0;
9940             stored_player[i].shield_deadly_time_left = 0;
9941           }
9942           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9943           {
9944             stored_player[i].shield_normal_time_left = 999999;
9945           }
9946           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9947           {
9948             stored_player[i].shield_normal_time_left = 999999;
9949             stored_player[i].shield_deadly_time_left = 999999;
9950           }
9951         }
9952       }
9953
9954       break;
9955     }
9956
9957     case CA_SET_PLAYER_GRAVITY:
9958     {
9959       for (i = 0; i < MAX_PLAYERS; i++)
9960       {
9961         if (trigger_player_bits & (1 << i))
9962         {
9963           stored_player[i].gravity =
9964             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9965              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9966              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9967              stored_player[i].gravity);
9968         }
9969       }
9970
9971       break;
9972     }
9973
9974     case CA_SET_PLAYER_ARTWORK:
9975     {
9976       for (i = 0; i < MAX_PLAYERS; i++)
9977       {
9978         if (trigger_player_bits & (1 << i))
9979         {
9980           int artwork_element = action_arg_element;
9981
9982           if (action_arg == CA_ARG_ELEMENT_RESET)
9983             artwork_element =
9984               (level.use_artwork_element[i] ? level.artwork_element[i] :
9985                stored_player[i].element_nr);
9986
9987           if (stored_player[i].artwork_element != artwork_element)
9988             stored_player[i].Frame = 0;
9989
9990           stored_player[i].artwork_element = artwork_element;
9991
9992           SetPlayerWaiting(&stored_player[i], FALSE);
9993
9994           // set number of special actions for bored and sleeping animation
9995           stored_player[i].num_special_action_bored =
9996             get_num_special_action(artwork_element,
9997                                    ACTION_BORING_1, ACTION_BORING_LAST);
9998           stored_player[i].num_special_action_sleeping =
9999             get_num_special_action(artwork_element,
10000                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10001         }
10002       }
10003
10004       break;
10005     }
10006
10007     case CA_SET_PLAYER_INVENTORY:
10008     {
10009       for (i = 0; i < MAX_PLAYERS; i++)
10010       {
10011         struct PlayerInfo *player = &stored_player[i];
10012         int j, k;
10013
10014         if (trigger_player_bits & (1 << i))
10015         {
10016           int inventory_element = action_arg_element;
10017
10018           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10019               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10020               action_arg == CA_ARG_ELEMENT_ACTION)
10021           {
10022             int element = inventory_element;
10023             int collect_count = element_info[element].collect_count_initial;
10024
10025             if (!IS_CUSTOM_ELEMENT(element))
10026               collect_count = 1;
10027
10028             if (collect_count == 0)
10029               player->inventory_infinite_element = element;
10030             else
10031               for (k = 0; k < collect_count; k++)
10032                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10033                   player->inventory_element[player->inventory_size++] =
10034                     element;
10035           }
10036           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10037                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10038                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10039           {
10040             if (player->inventory_infinite_element != EL_UNDEFINED &&
10041                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10042                                      action_arg_element_raw))
10043               player->inventory_infinite_element = EL_UNDEFINED;
10044
10045             for (k = 0, j = 0; j < player->inventory_size; j++)
10046             {
10047               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10048                                         action_arg_element_raw))
10049                 player->inventory_element[k++] = player->inventory_element[j];
10050             }
10051
10052             player->inventory_size = k;
10053           }
10054           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10055           {
10056             if (player->inventory_size > 0)
10057             {
10058               for (j = 0; j < player->inventory_size - 1; j++)
10059                 player->inventory_element[j] = player->inventory_element[j + 1];
10060
10061               player->inventory_size--;
10062             }
10063           }
10064           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10065           {
10066             if (player->inventory_size > 0)
10067               player->inventory_size--;
10068           }
10069           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10070           {
10071             player->inventory_infinite_element = EL_UNDEFINED;
10072             player->inventory_size = 0;
10073           }
10074           else if (action_arg == CA_ARG_INVENTORY_RESET)
10075           {
10076             player->inventory_infinite_element = EL_UNDEFINED;
10077             player->inventory_size = 0;
10078
10079             if (level.use_initial_inventory[i])
10080             {
10081               for (j = 0; j < level.initial_inventory_size[i]; j++)
10082               {
10083                 int element = level.initial_inventory_content[i][j];
10084                 int collect_count = element_info[element].collect_count_initial;
10085
10086                 if (!IS_CUSTOM_ELEMENT(element))
10087                   collect_count = 1;
10088
10089                 if (collect_count == 0)
10090                   player->inventory_infinite_element = element;
10091                 else
10092                   for (k = 0; k < collect_count; k++)
10093                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10094                       player->inventory_element[player->inventory_size++] =
10095                         element;
10096               }
10097             }
10098           }
10099         }
10100       }
10101
10102       break;
10103     }
10104
10105     // ---------- CE actions  -------------------------------------------------
10106
10107     case CA_SET_CE_VALUE:
10108     {
10109       int last_ce_value = CustomValue[x][y];
10110
10111       CustomValue[x][y] = action_arg_number_new;
10112
10113       if (CustomValue[x][y] != last_ce_value)
10114       {
10115         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10116         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10117
10118         if (CustomValue[x][y] == 0)
10119         {
10120           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10121           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10122         }
10123       }
10124
10125       break;
10126     }
10127
10128     case CA_SET_CE_SCORE:
10129     {
10130       int last_ce_score = ei->collect_score;
10131
10132       ei->collect_score = action_arg_number_new;
10133
10134       if (ei->collect_score != last_ce_score)
10135       {
10136         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10137         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10138
10139         if (ei->collect_score == 0)
10140         {
10141           int xx, yy;
10142
10143           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10144           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10145
10146           /*
10147             This is a very special case that seems to be a mixture between
10148             CheckElementChange() and CheckTriggeredElementChange(): while
10149             the first one only affects single elements that are triggered
10150             directly, the second one affects multiple elements in the playfield
10151             that are triggered indirectly by another element. This is a third
10152             case: Changing the CE score always affects multiple identical CEs,
10153             so every affected CE must be checked, not only the single CE for
10154             which the CE score was changed in the first place (as every instance
10155             of that CE shares the same CE score, and therefore also can change)!
10156           */
10157           SCAN_PLAYFIELD(xx, yy)
10158           {
10159             if (Feld[xx][yy] == element)
10160               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10161                                  CE_SCORE_GETS_ZERO);
10162           }
10163         }
10164       }
10165
10166       break;
10167     }
10168
10169     case CA_SET_CE_ARTWORK:
10170     {
10171       int artwork_element = action_arg_element;
10172       boolean reset_frame = FALSE;
10173       int xx, yy;
10174
10175       if (action_arg == CA_ARG_ELEMENT_RESET)
10176         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10177                            element);
10178
10179       if (ei->gfx_element != artwork_element)
10180         reset_frame = TRUE;
10181
10182       ei->gfx_element = artwork_element;
10183
10184       SCAN_PLAYFIELD(xx, yy)
10185       {
10186         if (Feld[xx][yy] == element)
10187         {
10188           if (reset_frame)
10189           {
10190             ResetGfxAnimation(xx, yy);
10191             ResetRandomAnimationValue(xx, yy);
10192           }
10193
10194           TEST_DrawLevelField(xx, yy);
10195         }
10196       }
10197
10198       break;
10199     }
10200
10201     // ---------- engine actions  ---------------------------------------------
10202
10203     case CA_SET_ENGINE_SCAN_MODE:
10204     {
10205       InitPlayfieldScanMode(action_arg);
10206
10207       break;
10208     }
10209
10210     default:
10211       break;
10212   }
10213 }
10214
10215 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10216 {
10217   int old_element = Feld[x][y];
10218   int new_element = GetElementFromGroupElement(element);
10219   int previous_move_direction = MovDir[x][y];
10220   int last_ce_value = CustomValue[x][y];
10221   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10222   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10223   boolean add_player_onto_element = (new_element_is_player &&
10224                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10225                                      IS_WALKABLE(old_element));
10226
10227   if (!add_player_onto_element)
10228   {
10229     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10230       RemoveMovingField(x, y);
10231     else
10232       RemoveField(x, y);
10233
10234     Feld[x][y] = new_element;
10235
10236     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10237       MovDir[x][y] = previous_move_direction;
10238
10239     if (element_info[new_element].use_last_ce_value)
10240       CustomValue[x][y] = last_ce_value;
10241
10242     InitField_WithBug1(x, y, FALSE);
10243
10244     new_element = Feld[x][y];   // element may have changed
10245
10246     ResetGfxAnimation(x, y);
10247     ResetRandomAnimationValue(x, y);
10248
10249     TEST_DrawLevelField(x, y);
10250
10251     if (GFX_CRUMBLED(new_element))
10252       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10253   }
10254
10255   // check if element under the player changes from accessible to unaccessible
10256   // (needed for special case of dropping element which then changes)
10257   // (must be checked after creating new element for walkable group elements)
10258   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10259       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10260   {
10261     Bang(x, y);
10262
10263     return;
10264   }
10265
10266   // "ChangeCount" not set yet to allow "entered by player" change one time
10267   if (new_element_is_player)
10268     RelocatePlayer(x, y, new_element);
10269
10270   if (is_change)
10271     ChangeCount[x][y]++;        // count number of changes in the same frame
10272
10273   TestIfBadThingTouchesPlayer(x, y);
10274   TestIfPlayerTouchesCustomElement(x, y);
10275   TestIfElementTouchesCustomElement(x, y);
10276 }
10277
10278 static void CreateField(int x, int y, int element)
10279 {
10280   CreateFieldExt(x, y, element, FALSE);
10281 }
10282
10283 static void CreateElementFromChange(int x, int y, int element)
10284 {
10285   element = GET_VALID_RUNTIME_ELEMENT(element);
10286
10287   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10288   {
10289     int old_element = Feld[x][y];
10290
10291     // prevent changed element from moving in same engine frame
10292     // unless both old and new element can either fall or move
10293     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10294         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10295       Stop[x][y] = TRUE;
10296   }
10297
10298   CreateFieldExt(x, y, element, TRUE);
10299 }
10300
10301 static boolean ChangeElement(int x, int y, int element, int page)
10302 {
10303   struct ElementInfo *ei = &element_info[element];
10304   struct ElementChangeInfo *change = &ei->change_page[page];
10305   int ce_value = CustomValue[x][y];
10306   int ce_score = ei->collect_score;
10307   int target_element;
10308   int old_element = Feld[x][y];
10309
10310   // always use default change event to prevent running into a loop
10311   if (ChangeEvent[x][y] == -1)
10312     ChangeEvent[x][y] = CE_DELAY;
10313
10314   if (ChangeEvent[x][y] == CE_DELAY)
10315   {
10316     // reset actual trigger element, trigger player and action element
10317     change->actual_trigger_element = EL_EMPTY;
10318     change->actual_trigger_player = EL_EMPTY;
10319     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10320     change->actual_trigger_side = CH_SIDE_NONE;
10321     change->actual_trigger_ce_value = 0;
10322     change->actual_trigger_ce_score = 0;
10323   }
10324
10325   // do not change elements more than a specified maximum number of changes
10326   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10327     return FALSE;
10328
10329   ChangeCount[x][y]++;          // count number of changes in the same frame
10330
10331   if (change->explode)
10332   {
10333     Bang(x, y);
10334
10335     return TRUE;
10336   }
10337
10338   if (change->use_target_content)
10339   {
10340     boolean complete_replace = TRUE;
10341     boolean can_replace[3][3];
10342     int xx, yy;
10343
10344     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10345     {
10346       boolean is_empty;
10347       boolean is_walkable;
10348       boolean is_diggable;
10349       boolean is_collectible;
10350       boolean is_removable;
10351       boolean is_destructible;
10352       int ex = x + xx - 1;
10353       int ey = y + yy - 1;
10354       int content_element = change->target_content.e[xx][yy];
10355       int e;
10356
10357       can_replace[xx][yy] = TRUE;
10358
10359       if (ex == x && ey == y)   // do not check changing element itself
10360         continue;
10361
10362       if (content_element == EL_EMPTY_SPACE)
10363       {
10364         can_replace[xx][yy] = FALSE;    // do not replace border with space
10365
10366         continue;
10367       }
10368
10369       if (!IN_LEV_FIELD(ex, ey))
10370       {
10371         can_replace[xx][yy] = FALSE;
10372         complete_replace = FALSE;
10373
10374         continue;
10375       }
10376
10377       e = Feld[ex][ey];
10378
10379       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10380         e = MovingOrBlocked2Element(ex, ey);
10381
10382       is_empty = (IS_FREE(ex, ey) ||
10383                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10384
10385       is_walkable     = (is_empty || IS_WALKABLE(e));
10386       is_diggable     = (is_empty || IS_DIGGABLE(e));
10387       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10388       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10389       is_removable    = (is_diggable || is_collectible);
10390
10391       can_replace[xx][yy] =
10392         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10393           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10394           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10395           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10396           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10397           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10398          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10399
10400       if (!can_replace[xx][yy])
10401         complete_replace = FALSE;
10402     }
10403
10404     if (!change->only_if_complete || complete_replace)
10405     {
10406       boolean something_has_changed = FALSE;
10407
10408       if (change->only_if_complete && change->use_random_replace &&
10409           RND(100) < change->random_percentage)
10410         return FALSE;
10411
10412       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10413       {
10414         int ex = x + xx - 1;
10415         int ey = y + yy - 1;
10416         int content_element;
10417
10418         if (can_replace[xx][yy] && (!change->use_random_replace ||
10419                                     RND(100) < change->random_percentage))
10420         {
10421           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10422             RemoveMovingField(ex, ey);
10423
10424           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10425
10426           content_element = change->target_content.e[xx][yy];
10427           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10428                                               ce_value, ce_score);
10429
10430           CreateElementFromChange(ex, ey, target_element);
10431
10432           something_has_changed = TRUE;
10433
10434           // for symmetry reasons, freeze newly created border elements
10435           if (ex != x || ey != y)
10436             Stop[ex][ey] = TRUE;        // no more moving in this frame
10437         }
10438       }
10439
10440       if (something_has_changed)
10441       {
10442         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10443         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10444       }
10445     }
10446   }
10447   else
10448   {
10449     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10450                                         ce_value, ce_score);
10451
10452     if (element == EL_DIAGONAL_GROWING ||
10453         element == EL_DIAGONAL_SHRINKING)
10454     {
10455       target_element = Store[x][y];
10456
10457       Store[x][y] = EL_EMPTY;
10458     }
10459
10460     CreateElementFromChange(x, y, target_element);
10461
10462     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10463     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10464   }
10465
10466   // this uses direct change before indirect change
10467   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10468
10469   return TRUE;
10470 }
10471
10472 static void HandleElementChange(int x, int y, int page)
10473 {
10474   int element = MovingOrBlocked2Element(x, y);
10475   struct ElementInfo *ei = &element_info[element];
10476   struct ElementChangeInfo *change = &ei->change_page[page];
10477   boolean handle_action_before_change = FALSE;
10478
10479 #ifdef DEBUG
10480   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10481       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10482   {
10483     printf("\n\n");
10484     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10485            x, y, element, element_info[element].token_name);
10486     printf("HandleElementChange(): This should never happen!\n");
10487     printf("\n\n");
10488   }
10489 #endif
10490
10491   // this can happen with classic bombs on walkable, changing elements
10492   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10493   {
10494     return;
10495   }
10496
10497   if (ChangeDelay[x][y] == 0)           // initialize element change
10498   {
10499     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10500
10501     if (change->can_change)
10502     {
10503       // !!! not clear why graphic animation should be reset at all here !!!
10504       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10505       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10506
10507       /*
10508         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10509
10510         When using an animation frame delay of 1 (this only happens with
10511         "sp_zonk.moving.left/right" in the classic graphics), the default
10512         (non-moving) animation shows wrong animation frames (while the
10513         moving animation, like "sp_zonk.moving.left/right", is correct,
10514         so this graphical bug never shows up with the classic graphics).
10515         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10516         be drawn instead of the correct frames 0,1,2,3. This is caused by
10517         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10518         an element change: First when the change delay ("ChangeDelay[][]")
10519         counter has reached zero after decrementing, then a second time in
10520         the next frame (after "GfxFrame[][]" was already incremented) when
10521         "ChangeDelay[][]" is reset to the initial delay value again.
10522
10523         This causes frame 0 to be drawn twice, while the last frame won't
10524         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10525
10526         As some animations may already be cleverly designed around this bug
10527         (at least the "Snake Bite" snake tail animation does this), it cannot
10528         simply be fixed here without breaking such existing animations.
10529         Unfortunately, it cannot easily be detected if a graphics set was
10530         designed "before" or "after" the bug was fixed. As a workaround,
10531         a new graphics set option "game.graphics_engine_version" was added
10532         to be able to specify the game's major release version for which the
10533         graphics set was designed, which can then be used to decide if the
10534         bugfix should be used (version 4 and above) or not (version 3 or
10535         below, or if no version was specified at all, as with old sets).
10536
10537         (The wrong/fixed animation frames can be tested with the test level set
10538         "test_gfxframe" and level "000", which contains a specially prepared
10539         custom element at level position (x/y) == (11/9) which uses the zonk
10540         animation mentioned above. Using "game.graphics_engine_version: 4"
10541         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10542         This can also be seen from the debug output for this test element.)
10543       */
10544
10545       // when a custom element is about to change (for example by change delay),
10546       // do not reset graphic animation when the custom element is moving
10547       if (game.graphics_engine_version < 4 &&
10548           !IS_MOVING(x, y))
10549       {
10550         ResetGfxAnimation(x, y);
10551         ResetRandomAnimationValue(x, y);
10552       }
10553
10554       if (change->pre_change_function)
10555         change->pre_change_function(x, y);
10556     }
10557   }
10558
10559   ChangeDelay[x][y]--;
10560
10561   if (ChangeDelay[x][y] != 0)           // continue element change
10562   {
10563     if (change->can_change)
10564     {
10565       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10566
10567       if (IS_ANIMATED(graphic))
10568         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10569
10570       if (change->change_function)
10571         change->change_function(x, y);
10572     }
10573   }
10574   else                                  // finish element change
10575   {
10576     if (ChangePage[x][y] != -1)         // remember page from delayed change
10577     {
10578       page = ChangePage[x][y];
10579       ChangePage[x][y] = -1;
10580
10581       change = &ei->change_page[page];
10582     }
10583
10584     if (IS_MOVING(x, y))                // never change a running system ;-)
10585     {
10586       ChangeDelay[x][y] = 1;            // try change after next move step
10587       ChangePage[x][y] = page;          // remember page to use for change
10588
10589       return;
10590     }
10591
10592     // special case: set new level random seed before changing element
10593     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10594       handle_action_before_change = TRUE;
10595
10596     if (change->has_action && handle_action_before_change)
10597       ExecuteCustomElementAction(x, y, element, page);
10598
10599     if (change->can_change)
10600     {
10601       if (ChangeElement(x, y, element, page))
10602       {
10603         if (change->post_change_function)
10604           change->post_change_function(x, y);
10605       }
10606     }
10607
10608     if (change->has_action && !handle_action_before_change)
10609       ExecuteCustomElementAction(x, y, element, page);
10610   }
10611 }
10612
10613 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10614                                               int trigger_element,
10615                                               int trigger_event,
10616                                               int trigger_player,
10617                                               int trigger_side,
10618                                               int trigger_page)
10619 {
10620   boolean change_done_any = FALSE;
10621   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10622   int i;
10623
10624   if (!(trigger_events[trigger_element][trigger_event]))
10625     return FALSE;
10626
10627   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10628
10629   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10630   {
10631     int element = EL_CUSTOM_START + i;
10632     boolean change_done = FALSE;
10633     int p;
10634
10635     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10636         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10637       continue;
10638
10639     for (p = 0; p < element_info[element].num_change_pages; p++)
10640     {
10641       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10642
10643       if (change->can_change_or_has_action &&
10644           change->has_event[trigger_event] &&
10645           change->trigger_side & trigger_side &&
10646           change->trigger_player & trigger_player &&
10647           change->trigger_page & trigger_page_bits &&
10648           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10649       {
10650         change->actual_trigger_element = trigger_element;
10651         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10652         change->actual_trigger_player_bits = trigger_player;
10653         change->actual_trigger_side = trigger_side;
10654         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10655         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10656
10657         if ((change->can_change && !change_done) || change->has_action)
10658         {
10659           int x, y;
10660
10661           SCAN_PLAYFIELD(x, y)
10662           {
10663             if (Feld[x][y] == element)
10664             {
10665               if (change->can_change && !change_done)
10666               {
10667                 // if element already changed in this frame, not only prevent
10668                 // another element change (checked in ChangeElement()), but
10669                 // also prevent additional element actions for this element
10670
10671                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10672                     !level.use_action_after_change_bug)
10673                   continue;
10674
10675                 ChangeDelay[x][y] = 1;
10676                 ChangeEvent[x][y] = trigger_event;
10677
10678                 HandleElementChange(x, y, p);
10679               }
10680               else if (change->has_action)
10681               {
10682                 // if element already changed in this frame, not only prevent
10683                 // another element change (checked in ChangeElement()), but
10684                 // also prevent additional element actions for this element
10685
10686                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10687                     !level.use_action_after_change_bug)
10688                   continue;
10689
10690                 ExecuteCustomElementAction(x, y, element, p);
10691                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10692               }
10693             }
10694           }
10695
10696           if (change->can_change)
10697           {
10698             change_done = TRUE;
10699             change_done_any = TRUE;
10700           }
10701         }
10702       }
10703     }
10704   }
10705
10706   RECURSION_LOOP_DETECTION_END();
10707
10708   return change_done_any;
10709 }
10710
10711 static boolean CheckElementChangeExt(int x, int y,
10712                                      int element,
10713                                      int trigger_element,
10714                                      int trigger_event,
10715                                      int trigger_player,
10716                                      int trigger_side)
10717 {
10718   boolean change_done = FALSE;
10719   int p;
10720
10721   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10722       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10723     return FALSE;
10724
10725   if (Feld[x][y] == EL_BLOCKED)
10726   {
10727     Blocked2Moving(x, y, &x, &y);
10728     element = Feld[x][y];
10729   }
10730
10731   // check if element has already changed or is about to change after moving
10732   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10733        Feld[x][y] != element) ||
10734
10735       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10736        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10737         ChangePage[x][y] != -1)))
10738     return FALSE;
10739
10740   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10741
10742   for (p = 0; p < element_info[element].num_change_pages; p++)
10743   {
10744     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10745
10746     /* check trigger element for all events where the element that is checked
10747        for changing interacts with a directly adjacent element -- this is
10748        different to element changes that affect other elements to change on the
10749        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10750     boolean check_trigger_element =
10751       (trigger_event == CE_TOUCHING_X ||
10752        trigger_event == CE_HITTING_X ||
10753        trigger_event == CE_HIT_BY_X ||
10754        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10755
10756     if (change->can_change_or_has_action &&
10757         change->has_event[trigger_event] &&
10758         change->trigger_side & trigger_side &&
10759         change->trigger_player & trigger_player &&
10760         (!check_trigger_element ||
10761          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10762     {
10763       change->actual_trigger_element = trigger_element;
10764       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10765       change->actual_trigger_player_bits = trigger_player;
10766       change->actual_trigger_side = trigger_side;
10767       change->actual_trigger_ce_value = CustomValue[x][y];
10768       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10769
10770       // special case: trigger element not at (x,y) position for some events
10771       if (check_trigger_element)
10772       {
10773         static struct
10774         {
10775           int dx, dy;
10776         } move_xy[] =
10777           {
10778             {  0,  0 },
10779             { -1,  0 },
10780             { +1,  0 },
10781             {  0,  0 },
10782             {  0, -1 },
10783             {  0,  0 }, { 0, 0 }, { 0, 0 },
10784             {  0, +1 }
10785           };
10786
10787         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10788         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10789
10790         change->actual_trigger_ce_value = CustomValue[xx][yy];
10791         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10792       }
10793
10794       if (change->can_change && !change_done)
10795       {
10796         ChangeDelay[x][y] = 1;
10797         ChangeEvent[x][y] = trigger_event;
10798
10799         HandleElementChange(x, y, p);
10800
10801         change_done = TRUE;
10802       }
10803       else if (change->has_action)
10804       {
10805         ExecuteCustomElementAction(x, y, element, p);
10806         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10807       }
10808     }
10809   }
10810
10811   RECURSION_LOOP_DETECTION_END();
10812
10813   return change_done;
10814 }
10815
10816 static void PlayPlayerSound(struct PlayerInfo *player)
10817 {
10818   int jx = player->jx, jy = player->jy;
10819   int sound_element = player->artwork_element;
10820   int last_action = player->last_action_waiting;
10821   int action = player->action_waiting;
10822
10823   if (player->is_waiting)
10824   {
10825     if (action != last_action)
10826       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10827     else
10828       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10829   }
10830   else
10831   {
10832     if (action != last_action)
10833       StopSound(element_info[sound_element].sound[last_action]);
10834
10835     if (last_action == ACTION_SLEEPING)
10836       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10837   }
10838 }
10839
10840 static void PlayAllPlayersSound(void)
10841 {
10842   int i;
10843
10844   for (i = 0; i < MAX_PLAYERS; i++)
10845     if (stored_player[i].active)
10846       PlayPlayerSound(&stored_player[i]);
10847 }
10848
10849 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10850 {
10851   boolean last_waiting = player->is_waiting;
10852   int move_dir = player->MovDir;
10853
10854   player->dir_waiting = move_dir;
10855   player->last_action_waiting = player->action_waiting;
10856
10857   if (is_waiting)
10858   {
10859     if (!last_waiting)          // not waiting -> waiting
10860     {
10861       player->is_waiting = TRUE;
10862
10863       player->frame_counter_bored =
10864         FrameCounter +
10865         game.player_boring_delay_fixed +
10866         GetSimpleRandom(game.player_boring_delay_random);
10867       player->frame_counter_sleeping =
10868         FrameCounter +
10869         game.player_sleeping_delay_fixed +
10870         GetSimpleRandom(game.player_sleeping_delay_random);
10871
10872       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10873     }
10874
10875     if (game.player_sleeping_delay_fixed +
10876         game.player_sleeping_delay_random > 0 &&
10877         player->anim_delay_counter == 0 &&
10878         player->post_delay_counter == 0 &&
10879         FrameCounter >= player->frame_counter_sleeping)
10880       player->is_sleeping = TRUE;
10881     else if (game.player_boring_delay_fixed +
10882              game.player_boring_delay_random > 0 &&
10883              FrameCounter >= player->frame_counter_bored)
10884       player->is_bored = TRUE;
10885
10886     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10887                               player->is_bored ? ACTION_BORING :
10888                               ACTION_WAITING);
10889
10890     if (player->is_sleeping && player->use_murphy)
10891     {
10892       // special case for sleeping Murphy when leaning against non-free tile
10893
10894       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10895           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10896            !IS_MOVING(player->jx - 1, player->jy)))
10897         move_dir = MV_LEFT;
10898       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10899                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10900                 !IS_MOVING(player->jx + 1, player->jy)))
10901         move_dir = MV_RIGHT;
10902       else
10903         player->is_sleeping = FALSE;
10904
10905       player->dir_waiting = move_dir;
10906     }
10907
10908     if (player->is_sleeping)
10909     {
10910       if (player->num_special_action_sleeping > 0)
10911       {
10912         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10913         {
10914           int last_special_action = player->special_action_sleeping;
10915           int num_special_action = player->num_special_action_sleeping;
10916           int special_action =
10917             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10918              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10919              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10920              last_special_action + 1 : ACTION_SLEEPING);
10921           int special_graphic =
10922             el_act_dir2img(player->artwork_element, special_action, move_dir);
10923
10924           player->anim_delay_counter =
10925             graphic_info[special_graphic].anim_delay_fixed +
10926             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10927           player->post_delay_counter =
10928             graphic_info[special_graphic].post_delay_fixed +
10929             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10930
10931           player->special_action_sleeping = special_action;
10932         }
10933
10934         if (player->anim_delay_counter > 0)
10935         {
10936           player->action_waiting = player->special_action_sleeping;
10937           player->anim_delay_counter--;
10938         }
10939         else if (player->post_delay_counter > 0)
10940         {
10941           player->post_delay_counter--;
10942         }
10943       }
10944     }
10945     else if (player->is_bored)
10946     {
10947       if (player->num_special_action_bored > 0)
10948       {
10949         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10950         {
10951           int special_action =
10952             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10953           int special_graphic =
10954             el_act_dir2img(player->artwork_element, special_action, move_dir);
10955
10956           player->anim_delay_counter =
10957             graphic_info[special_graphic].anim_delay_fixed +
10958             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10959           player->post_delay_counter =
10960             graphic_info[special_graphic].post_delay_fixed +
10961             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10962
10963           player->special_action_bored = special_action;
10964         }
10965
10966         if (player->anim_delay_counter > 0)
10967         {
10968           player->action_waiting = player->special_action_bored;
10969           player->anim_delay_counter--;
10970         }
10971         else if (player->post_delay_counter > 0)
10972         {
10973           player->post_delay_counter--;
10974         }
10975       }
10976     }
10977   }
10978   else if (last_waiting)        // waiting -> not waiting
10979   {
10980     player->is_waiting = FALSE;
10981     player->is_bored = FALSE;
10982     player->is_sleeping = FALSE;
10983
10984     player->frame_counter_bored = -1;
10985     player->frame_counter_sleeping = -1;
10986
10987     player->anim_delay_counter = 0;
10988     player->post_delay_counter = 0;
10989
10990     player->dir_waiting = player->MovDir;
10991     player->action_waiting = ACTION_DEFAULT;
10992
10993     player->special_action_bored = ACTION_DEFAULT;
10994     player->special_action_sleeping = ACTION_DEFAULT;
10995   }
10996 }
10997
10998 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10999 {
11000   if ((!player->is_moving  && player->was_moving) ||
11001       (player->MovPos == 0 && player->was_moving) ||
11002       (player->is_snapping && !player->was_snapping) ||
11003       (player->is_dropping && !player->was_dropping))
11004   {
11005     if (!CheckSaveEngineSnapshotToList())
11006       return;
11007
11008     player->was_moving = FALSE;
11009     player->was_snapping = TRUE;
11010     player->was_dropping = TRUE;
11011   }
11012   else
11013   {
11014     if (player->is_moving)
11015       player->was_moving = TRUE;
11016
11017     if (!player->is_snapping)
11018       player->was_snapping = FALSE;
11019
11020     if (!player->is_dropping)
11021       player->was_dropping = FALSE;
11022   }
11023 }
11024
11025 static void CheckSingleStepMode(struct PlayerInfo *player)
11026 {
11027   if (tape.single_step && tape.recording && !tape.pausing)
11028   {
11029     /* as it is called "single step mode", just return to pause mode when the
11030        player stopped moving after one tile (or never starts moving at all) */
11031     if (!player->is_moving &&
11032         !player->is_pushing &&
11033         !player->is_dropping_pressed)
11034     {
11035       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11036       SnapField(player, 0, 0);                  // stop snapping
11037     }
11038   }
11039
11040   CheckSaveEngineSnapshot(player);
11041 }
11042
11043 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11044 {
11045   int left      = player_action & JOY_LEFT;
11046   int right     = player_action & JOY_RIGHT;
11047   int up        = player_action & JOY_UP;
11048   int down      = player_action & JOY_DOWN;
11049   int button1   = player_action & JOY_BUTTON_1;
11050   int button2   = player_action & JOY_BUTTON_2;
11051   int dx        = (left ? -1 : right ? 1 : 0);
11052   int dy        = (up   ? -1 : down  ? 1 : 0);
11053
11054   if (!player->active || tape.pausing)
11055     return 0;
11056
11057   if (player_action)
11058   {
11059     if (button1)
11060       SnapField(player, dx, dy);
11061     else
11062     {
11063       if (button2)
11064         DropElement(player);
11065
11066       MovePlayer(player, dx, dy);
11067     }
11068
11069     CheckSingleStepMode(player);
11070
11071     SetPlayerWaiting(player, FALSE);
11072
11073     return player_action;
11074   }
11075   else
11076   {
11077     // no actions for this player (no input at player's configured device)
11078
11079     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11080     SnapField(player, 0, 0);
11081     CheckGravityMovementWhenNotMoving(player);
11082
11083     if (player->MovPos == 0)
11084       SetPlayerWaiting(player, TRUE);
11085
11086     if (player->MovPos == 0)    // needed for tape.playing
11087       player->is_moving = FALSE;
11088
11089     player->is_dropping = FALSE;
11090     player->is_dropping_pressed = FALSE;
11091     player->drop_pressed_delay = 0;
11092
11093     CheckSingleStepMode(player);
11094
11095     return 0;
11096   }
11097 }
11098
11099 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11100                                          byte *tape_action)
11101 {
11102   if (!tape.use_mouse)
11103     return;
11104
11105   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11106   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11107   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11108 }
11109
11110 static void SetTapeActionFromMouseAction(byte *tape_action,
11111                                          struct MouseActionInfo *mouse_action)
11112 {
11113   if (!tape.use_mouse)
11114     return;
11115
11116   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11117   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11118   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11119 }
11120
11121 static void CheckLevelSolved(void)
11122 {
11123   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11124   {
11125     if (game_em.level_solved &&
11126         !game_em.game_over)                             // game won
11127     {
11128       LevelSolved();
11129
11130       game_em.game_over = TRUE;
11131
11132       game.all_players_gone = TRUE;
11133     }
11134
11135     if (game_em.game_over)                              // game lost
11136       game.all_players_gone = TRUE;
11137   }
11138   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11139   {
11140     if (game_sp.level_solved &&
11141         !game_sp.game_over)                             // game won
11142     {
11143       LevelSolved();
11144
11145       game_sp.game_over = TRUE;
11146
11147       game.all_players_gone = TRUE;
11148     }
11149
11150     if (game_sp.game_over)                              // game lost
11151       game.all_players_gone = TRUE;
11152   }
11153   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11154   {
11155     if (game_mm.level_solved &&
11156         !game_mm.game_over)                             // game won
11157     {
11158       LevelSolved();
11159
11160       game_mm.game_over = TRUE;
11161
11162       game.all_players_gone = TRUE;
11163     }
11164
11165     if (game_mm.game_over)                              // game lost
11166       game.all_players_gone = TRUE;
11167   }
11168 }
11169
11170 static void CheckLevelTime(void)
11171 {
11172   int i;
11173
11174   if (TimeFrames >= FRAMES_PER_SECOND)
11175   {
11176     TimeFrames = 0;
11177     TapeTime++;
11178
11179     for (i = 0; i < MAX_PLAYERS; i++)
11180     {
11181       struct PlayerInfo *player = &stored_player[i];
11182
11183       if (SHIELD_ON(player))
11184       {
11185         player->shield_normal_time_left--;
11186
11187         if (player->shield_deadly_time_left > 0)
11188           player->shield_deadly_time_left--;
11189       }
11190     }
11191
11192     if (!game.LevelSolved && !level.use_step_counter)
11193     {
11194       TimePlayed++;
11195
11196       if (TimeLeft > 0)
11197       {
11198         TimeLeft--;
11199
11200         if (TimeLeft <= 10 && setup.time_limit)
11201           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11202
11203         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11204            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11205
11206         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11207
11208         if (!TimeLeft && setup.time_limit)
11209         {
11210           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11211             level.native_em_level->lev->killed_out_of_time = TRUE;
11212           else
11213             for (i = 0; i < MAX_PLAYERS; i++)
11214               KillPlayer(&stored_player[i]);
11215         }
11216       }
11217       else if (game.no_time_limit && !game.all_players_gone)
11218       {
11219         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11220       }
11221
11222       level.native_em_level->lev->time =
11223         (game.no_time_limit ? TimePlayed : TimeLeft);
11224     }
11225
11226     if (tape.recording || tape.playing)
11227       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11228   }
11229
11230   if (tape.recording || tape.playing)
11231     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11232
11233   UpdateAndDisplayGameControlValues();
11234 }
11235
11236 void AdvanceFrameAndPlayerCounters(int player_nr)
11237 {
11238   int i;
11239
11240   // advance frame counters (global frame counter and time frame counter)
11241   FrameCounter++;
11242   TimeFrames++;
11243
11244   // advance player counters (counters for move delay, move animation etc.)
11245   for (i = 0; i < MAX_PLAYERS; i++)
11246   {
11247     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11248     int move_delay_value = stored_player[i].move_delay_value;
11249     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11250
11251     if (!advance_player_counters)       // not all players may be affected
11252       continue;
11253
11254     if (move_frames == 0)       // less than one move per game frame
11255     {
11256       int stepsize = TILEX / move_delay_value;
11257       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11258       int count = (stored_player[i].is_moving ?
11259                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11260
11261       if (count % delay == 0)
11262         move_frames = 1;
11263     }
11264
11265     stored_player[i].Frame += move_frames;
11266
11267     if (stored_player[i].MovPos != 0)
11268       stored_player[i].StepFrame += move_frames;
11269
11270     if (stored_player[i].move_delay > 0)
11271       stored_player[i].move_delay--;
11272
11273     // due to bugs in previous versions, counter must count up, not down
11274     if (stored_player[i].push_delay != -1)
11275       stored_player[i].push_delay++;
11276
11277     if (stored_player[i].drop_delay > 0)
11278       stored_player[i].drop_delay--;
11279
11280     if (stored_player[i].is_dropping_pressed)
11281       stored_player[i].drop_pressed_delay++;
11282   }
11283 }
11284
11285 void StartGameActions(boolean init_network_game, boolean record_tape,
11286                       int random_seed)
11287 {
11288   unsigned int new_random_seed = InitRND(random_seed);
11289
11290   if (record_tape)
11291     TapeStartRecording(new_random_seed);
11292
11293   if (init_network_game)
11294   {
11295     SendToServer_LevelFile();
11296     SendToServer_StartPlaying();
11297
11298     return;
11299   }
11300
11301   InitGame();
11302 }
11303
11304 static void GameActionsExt(void)
11305 {
11306 #if 0
11307   static unsigned int game_frame_delay = 0;
11308 #endif
11309   unsigned int game_frame_delay_value;
11310   byte *recorded_player_action;
11311   byte summarized_player_action = 0;
11312   byte tape_action[MAX_PLAYERS];
11313   int i;
11314
11315   // detect endless loops, caused by custom element programming
11316   if (recursion_loop_detected && recursion_loop_depth == 0)
11317   {
11318     char *message = getStringCat3("Internal Error! Element ",
11319                                   EL_NAME(recursion_loop_element),
11320                                   " caused endless loop! Quit the game?");
11321
11322     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11323           EL_NAME(recursion_loop_element));
11324
11325     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11326
11327     recursion_loop_detected = FALSE;    // if game should be continued
11328
11329     free(message);
11330
11331     return;
11332   }
11333
11334   if (game.restart_level)
11335     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11336
11337   CheckLevelSolved();
11338
11339   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11340     GameWon();
11341
11342   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11343     TapeStop();
11344
11345   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11346     return;
11347
11348   game_frame_delay_value =
11349     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11350
11351   if (tape.playing && tape.warp_forward && !tape.pausing)
11352     game_frame_delay_value = 0;
11353
11354   SetVideoFrameDelay(game_frame_delay_value);
11355
11356 #if 0
11357 #if 0
11358   // ---------- main game synchronization point ----------
11359
11360   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11361
11362   printf("::: skip == %d\n", skip);
11363
11364 #else
11365   // ---------- main game synchronization point ----------
11366
11367   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11368 #endif
11369 #endif
11370
11371   if (network_playing && !network_player_action_received)
11372   {
11373     // try to get network player actions in time
11374
11375     // last chance to get network player actions without main loop delay
11376     HandleNetworking();
11377
11378     // game was quit by network peer
11379     if (game_status != GAME_MODE_PLAYING)
11380       return;
11381
11382     // check if network player actions still missing and game still running
11383     if (!network_player_action_received && !checkGameEnded())
11384       return;           // failed to get network player actions in time
11385
11386     // do not yet reset "network_player_action_received" (for tape.pausing)
11387   }
11388
11389   if (tape.pausing)
11390     return;
11391
11392   // at this point we know that we really continue executing the game
11393
11394   network_player_action_received = FALSE;
11395
11396   // when playing tape, read previously recorded player input from tape data
11397   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11398
11399   local_player->effective_mouse_action = local_player->mouse_action;
11400
11401   if (recorded_player_action != NULL)
11402     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11403                                  recorded_player_action);
11404
11405   // TapePlayAction() may return NULL when toggling to "pause before death"
11406   if (tape.pausing)
11407     return;
11408
11409   if (tape.set_centered_player)
11410   {
11411     game.centered_player_nr_next = tape.centered_player_nr_next;
11412     game.set_centered_player = TRUE;
11413   }
11414
11415   for (i = 0; i < MAX_PLAYERS; i++)
11416   {
11417     summarized_player_action |= stored_player[i].action;
11418
11419     if (!network_playing && (game.team_mode || tape.playing))
11420       stored_player[i].effective_action = stored_player[i].action;
11421   }
11422
11423   if (network_playing && !checkGameEnded())
11424     SendToServer_MovePlayer(summarized_player_action);
11425
11426   // summarize all actions at local players mapped input device position
11427   // (this allows using different input devices in single player mode)
11428   if (!network.enabled && !game.team_mode)
11429     stored_player[map_player_action[local_player->index_nr]].effective_action =
11430       summarized_player_action;
11431
11432   if (tape.recording &&
11433       setup.team_mode &&
11434       setup.input_on_focus &&
11435       game.centered_player_nr != -1)
11436   {
11437     for (i = 0; i < MAX_PLAYERS; i++)
11438       stored_player[i].effective_action =
11439         (i == game.centered_player_nr ? summarized_player_action : 0);
11440   }
11441
11442   if (recorded_player_action != NULL)
11443     for (i = 0; i < MAX_PLAYERS; i++)
11444       stored_player[i].effective_action = recorded_player_action[i];
11445
11446   for (i = 0; i < MAX_PLAYERS; i++)
11447   {
11448     tape_action[i] = stored_player[i].effective_action;
11449
11450     /* (this may happen in the RND game engine if a player was not present on
11451        the playfield on level start, but appeared later from a custom element */
11452     if (setup.team_mode &&
11453         tape.recording &&
11454         tape_action[i] &&
11455         !tape.player_participates[i])
11456       tape.player_participates[i] = TRUE;
11457   }
11458
11459   SetTapeActionFromMouseAction(tape_action,
11460                                &local_player->effective_mouse_action);
11461
11462   // only record actions from input devices, but not programmed actions
11463   if (tape.recording)
11464     TapeRecordAction(tape_action);
11465
11466 #if USE_NEW_PLAYER_ASSIGNMENTS
11467   // !!! also map player actions in single player mode !!!
11468   // if (game.team_mode)
11469   if (1)
11470   {
11471     byte mapped_action[MAX_PLAYERS];
11472
11473 #if DEBUG_PLAYER_ACTIONS
11474     printf(":::");
11475     for (i = 0; i < MAX_PLAYERS; i++)
11476       printf(" %d, ", stored_player[i].effective_action);
11477 #endif
11478
11479     for (i = 0; i < MAX_PLAYERS; i++)
11480       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11481
11482     for (i = 0; i < MAX_PLAYERS; i++)
11483       stored_player[i].effective_action = mapped_action[i];
11484
11485 #if DEBUG_PLAYER_ACTIONS
11486     printf(" =>");
11487     for (i = 0; i < MAX_PLAYERS; i++)
11488       printf(" %d, ", stored_player[i].effective_action);
11489     printf("\n");
11490 #endif
11491   }
11492 #if DEBUG_PLAYER_ACTIONS
11493   else
11494   {
11495     printf(":::");
11496     for (i = 0; i < MAX_PLAYERS; i++)
11497       printf(" %d, ", stored_player[i].effective_action);
11498     printf("\n");
11499   }
11500 #endif
11501 #endif
11502
11503   for (i = 0; i < MAX_PLAYERS; i++)
11504   {
11505     // allow engine snapshot in case of changed movement attempt
11506     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11507         (stored_player[i].effective_action & KEY_MOTION))
11508       game.snapshot.changed_action = TRUE;
11509
11510     // allow engine snapshot in case of snapping/dropping attempt
11511     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11512         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11513       game.snapshot.changed_action = TRUE;
11514
11515     game.snapshot.last_action[i] = stored_player[i].effective_action;
11516   }
11517
11518   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11519   {
11520     GameActions_EM_Main();
11521   }
11522   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11523   {
11524     GameActions_SP_Main();
11525   }
11526   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11527   {
11528     GameActions_MM_Main();
11529   }
11530   else
11531   {
11532     GameActions_RND_Main();
11533   }
11534
11535   BlitScreenToBitmap(backbuffer);
11536
11537   CheckLevelSolved();
11538   CheckLevelTime();
11539
11540   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11541
11542   if (global.show_frames_per_second)
11543   {
11544     static unsigned int fps_counter = 0;
11545     static int fps_frames = 0;
11546     unsigned int fps_delay_ms = Counter() - fps_counter;
11547
11548     fps_frames++;
11549
11550     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11551     {
11552       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11553
11554       fps_frames = 0;
11555       fps_counter = Counter();
11556
11557       // always draw FPS to screen after FPS value was updated
11558       redraw_mask |= REDRAW_FPS;
11559     }
11560
11561     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11562     if (GetDrawDeactivationMask() == REDRAW_NONE)
11563       redraw_mask |= REDRAW_FPS;
11564   }
11565 }
11566
11567 static void GameActions_CheckSaveEngineSnapshot(void)
11568 {
11569   if (!game.snapshot.save_snapshot)
11570     return;
11571
11572   // clear flag for saving snapshot _before_ saving snapshot
11573   game.snapshot.save_snapshot = FALSE;
11574
11575   SaveEngineSnapshotToList();
11576 }
11577
11578 void GameActions(void)
11579 {
11580   GameActionsExt();
11581
11582   GameActions_CheckSaveEngineSnapshot();
11583 }
11584
11585 void GameActions_EM_Main(void)
11586 {
11587   byte effective_action[MAX_PLAYERS];
11588   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11589   int i;
11590
11591   for (i = 0; i < MAX_PLAYERS; i++)
11592     effective_action[i] = stored_player[i].effective_action;
11593
11594   GameActions_EM(effective_action, warp_mode);
11595 }
11596
11597 void GameActions_SP_Main(void)
11598 {
11599   byte effective_action[MAX_PLAYERS];
11600   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11601   int i;
11602
11603   for (i = 0; i < MAX_PLAYERS; i++)
11604     effective_action[i] = stored_player[i].effective_action;
11605
11606   GameActions_SP(effective_action, warp_mode);
11607
11608   for (i = 0; i < MAX_PLAYERS; i++)
11609   {
11610     if (stored_player[i].force_dropping)
11611       stored_player[i].action |= KEY_BUTTON_DROP;
11612
11613     stored_player[i].force_dropping = FALSE;
11614   }
11615 }
11616
11617 void GameActions_MM_Main(void)
11618 {
11619   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11620
11621   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11622 }
11623
11624 void GameActions_RND_Main(void)
11625 {
11626   GameActions_RND();
11627 }
11628
11629 void GameActions_RND(void)
11630 {
11631   int magic_wall_x = 0, magic_wall_y = 0;
11632   int i, x, y, element, graphic, last_gfx_frame;
11633
11634   InitPlayfieldScanModeVars();
11635
11636   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11637   {
11638     SCAN_PLAYFIELD(x, y)
11639     {
11640       ChangeCount[x][y] = 0;
11641       ChangeEvent[x][y] = -1;
11642     }
11643   }
11644
11645   if (game.set_centered_player)
11646   {
11647     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11648
11649     // switching to "all players" only possible if all players fit to screen
11650     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11651     {
11652       game.centered_player_nr_next = game.centered_player_nr;
11653       game.set_centered_player = FALSE;
11654     }
11655
11656     // do not switch focus to non-existing (or non-active) player
11657     if (game.centered_player_nr_next >= 0 &&
11658         !stored_player[game.centered_player_nr_next].active)
11659     {
11660       game.centered_player_nr_next = game.centered_player_nr;
11661       game.set_centered_player = FALSE;
11662     }
11663   }
11664
11665   if (game.set_centered_player &&
11666       ScreenMovPos == 0)        // screen currently aligned at tile position
11667   {
11668     int sx, sy;
11669
11670     if (game.centered_player_nr_next == -1)
11671     {
11672       setScreenCenteredToAllPlayers(&sx, &sy);
11673     }
11674     else
11675     {
11676       sx = stored_player[game.centered_player_nr_next].jx;
11677       sy = stored_player[game.centered_player_nr_next].jy;
11678     }
11679
11680     game.centered_player_nr = game.centered_player_nr_next;
11681     game.set_centered_player = FALSE;
11682
11683     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11684     DrawGameDoorValues();
11685   }
11686
11687   for (i = 0; i < MAX_PLAYERS; i++)
11688   {
11689     int actual_player_action = stored_player[i].effective_action;
11690
11691 #if 1
11692     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11693        - rnd_equinox_tetrachloride 048
11694        - rnd_equinox_tetrachloride_ii 096
11695        - rnd_emanuel_schmieg 002
11696        - doctor_sloan_ww 001, 020
11697     */
11698     if (stored_player[i].MovPos == 0)
11699       CheckGravityMovement(&stored_player[i]);
11700 #endif
11701
11702     // overwrite programmed action with tape action
11703     if (stored_player[i].programmed_action)
11704       actual_player_action = stored_player[i].programmed_action;
11705
11706     PlayerActions(&stored_player[i], actual_player_action);
11707
11708     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11709   }
11710
11711   ScrollScreen(NULL, SCROLL_GO_ON);
11712
11713   /* for backwards compatibility, the following code emulates a fixed bug that
11714      occured when pushing elements (causing elements that just made their last
11715      pushing step to already (if possible) make their first falling step in the
11716      same game frame, which is bad); this code is also needed to use the famous
11717      "spring push bug" which is used in older levels and might be wanted to be
11718      used also in newer levels, but in this case the buggy pushing code is only
11719      affecting the "spring" element and no other elements */
11720
11721   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11722   {
11723     for (i = 0; i < MAX_PLAYERS; i++)
11724     {
11725       struct PlayerInfo *player = &stored_player[i];
11726       int x = player->jx;
11727       int y = player->jy;
11728
11729       if (player->active && player->is_pushing && player->is_moving &&
11730           IS_MOVING(x, y) &&
11731           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11732            Feld[x][y] == EL_SPRING))
11733       {
11734         ContinueMoving(x, y);
11735
11736         // continue moving after pushing (this is actually a bug)
11737         if (!IS_MOVING(x, y))
11738           Stop[x][y] = FALSE;
11739       }
11740     }
11741   }
11742
11743   SCAN_PLAYFIELD(x, y)
11744   {
11745     Last[x][y] = Feld[x][y];
11746
11747     ChangeCount[x][y] = 0;
11748     ChangeEvent[x][y] = -1;
11749
11750     // this must be handled before main playfield loop
11751     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11752     {
11753       MovDelay[x][y]--;
11754       if (MovDelay[x][y] <= 0)
11755         RemoveField(x, y);
11756     }
11757
11758     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11759     {
11760       MovDelay[x][y]--;
11761       if (MovDelay[x][y] <= 0)
11762       {
11763         RemoveField(x, y);
11764         TEST_DrawLevelField(x, y);
11765
11766         TestIfElementTouchesCustomElement(x, y);        // for empty space
11767       }
11768     }
11769
11770 #if DEBUG
11771     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11772     {
11773       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11774       printf("GameActions(): This should never happen!\n");
11775
11776       ChangePage[x][y] = -1;
11777     }
11778 #endif
11779
11780     Stop[x][y] = FALSE;
11781     if (WasJustMoving[x][y] > 0)
11782       WasJustMoving[x][y]--;
11783     if (WasJustFalling[x][y] > 0)
11784       WasJustFalling[x][y]--;
11785     if (CheckCollision[x][y] > 0)
11786       CheckCollision[x][y]--;
11787     if (CheckImpact[x][y] > 0)
11788       CheckImpact[x][y]--;
11789
11790     GfxFrame[x][y]++;
11791
11792     /* reset finished pushing action (not done in ContinueMoving() to allow
11793        continuous pushing animation for elements with zero push delay) */
11794     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11795     {
11796       ResetGfxAnimation(x, y);
11797       TEST_DrawLevelField(x, y);
11798     }
11799
11800 #if DEBUG
11801     if (IS_BLOCKED(x, y))
11802     {
11803       int oldx, oldy;
11804
11805       Blocked2Moving(x, y, &oldx, &oldy);
11806       if (!IS_MOVING(oldx, oldy))
11807       {
11808         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11809         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11810         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11811         printf("GameActions(): This should never happen!\n");
11812       }
11813     }
11814 #endif
11815   }
11816
11817   SCAN_PLAYFIELD(x, y)
11818   {
11819     element = Feld[x][y];
11820     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11821     last_gfx_frame = GfxFrame[x][y];
11822
11823     ResetGfxFrame(x, y);
11824
11825     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11826       DrawLevelGraphicAnimation(x, y, graphic);
11827
11828     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11829         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11830       ResetRandomAnimationValue(x, y);
11831
11832     SetRandomAnimationValue(x, y);
11833
11834     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11835
11836     if (IS_INACTIVE(element))
11837     {
11838       if (IS_ANIMATED(graphic))
11839         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11840
11841       continue;
11842     }
11843
11844     // this may take place after moving, so 'element' may have changed
11845     if (IS_CHANGING(x, y) &&
11846         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11847     {
11848       int page = element_info[element].event_page_nr[CE_DELAY];
11849
11850       HandleElementChange(x, y, page);
11851
11852       element = Feld[x][y];
11853       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11854     }
11855
11856     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11857     {
11858       StartMoving(x, y);
11859
11860       element = Feld[x][y];
11861       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11862
11863       if (IS_ANIMATED(graphic) &&
11864           !IS_MOVING(x, y) &&
11865           !Stop[x][y])
11866         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11867
11868       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11869         TEST_DrawTwinkleOnField(x, y);
11870     }
11871     else if (element == EL_ACID)
11872     {
11873       if (!Stop[x][y])
11874         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11875     }
11876     else if ((element == EL_EXIT_OPEN ||
11877               element == EL_EM_EXIT_OPEN ||
11878               element == EL_SP_EXIT_OPEN ||
11879               element == EL_STEEL_EXIT_OPEN ||
11880               element == EL_EM_STEEL_EXIT_OPEN ||
11881               element == EL_SP_TERMINAL ||
11882               element == EL_SP_TERMINAL_ACTIVE ||
11883               element == EL_EXTRA_TIME ||
11884               element == EL_SHIELD_NORMAL ||
11885               element == EL_SHIELD_DEADLY) &&
11886              IS_ANIMATED(graphic))
11887       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11888     else if (IS_MOVING(x, y))
11889       ContinueMoving(x, y);
11890     else if (IS_ACTIVE_BOMB(element))
11891       CheckDynamite(x, y);
11892     else if (element == EL_AMOEBA_GROWING)
11893       AmoebeWaechst(x, y);
11894     else if (element == EL_AMOEBA_SHRINKING)
11895       AmoebaDisappearing(x, y);
11896
11897 #if !USE_NEW_AMOEBA_CODE
11898     else if (IS_AMOEBALIVE(element))
11899       AmoebeAbleger(x, y);
11900 #endif
11901
11902     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11903       Life(x, y);
11904     else if (element == EL_EXIT_CLOSED)
11905       CheckExit(x, y);
11906     else if (element == EL_EM_EXIT_CLOSED)
11907       CheckExitEM(x, y);
11908     else if (element == EL_STEEL_EXIT_CLOSED)
11909       CheckExitSteel(x, y);
11910     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11911       CheckExitSteelEM(x, y);
11912     else if (element == EL_SP_EXIT_CLOSED)
11913       CheckExitSP(x, y);
11914     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11915              element == EL_EXPANDABLE_STEELWALL_GROWING)
11916       MauerWaechst(x, y);
11917     else if (element == EL_EXPANDABLE_WALL ||
11918              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11919              element == EL_EXPANDABLE_WALL_VERTICAL ||
11920              element == EL_EXPANDABLE_WALL_ANY ||
11921              element == EL_BD_EXPANDABLE_WALL)
11922       MauerAbleger(x, y);
11923     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11924              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11925              element == EL_EXPANDABLE_STEELWALL_ANY)
11926       MauerAblegerStahl(x, y);
11927     else if (element == EL_FLAMES)
11928       CheckForDragon(x, y);
11929     else if (element == EL_EXPLOSION)
11930       ; // drawing of correct explosion animation is handled separately
11931     else if (element == EL_ELEMENT_SNAPPING ||
11932              element == EL_DIAGONAL_SHRINKING ||
11933              element == EL_DIAGONAL_GROWING)
11934     {
11935       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11936
11937       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11938     }
11939     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11940       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11941
11942     if (IS_BELT_ACTIVE(element))
11943       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11944
11945     if (game.magic_wall_active)
11946     {
11947       int jx = local_player->jx, jy = local_player->jy;
11948
11949       // play the element sound at the position nearest to the player
11950       if ((element == EL_MAGIC_WALL_FULL ||
11951            element == EL_MAGIC_WALL_ACTIVE ||
11952            element == EL_MAGIC_WALL_EMPTYING ||
11953            element == EL_BD_MAGIC_WALL_FULL ||
11954            element == EL_BD_MAGIC_WALL_ACTIVE ||
11955            element == EL_BD_MAGIC_WALL_EMPTYING ||
11956            element == EL_DC_MAGIC_WALL_FULL ||
11957            element == EL_DC_MAGIC_WALL_ACTIVE ||
11958            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11959           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11960       {
11961         magic_wall_x = x;
11962         magic_wall_y = y;
11963       }
11964     }
11965   }
11966
11967 #if USE_NEW_AMOEBA_CODE
11968   // new experimental amoeba growth stuff
11969   if (!(FrameCounter % 8))
11970   {
11971     static unsigned int random = 1684108901;
11972
11973     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11974     {
11975       x = RND(lev_fieldx);
11976       y = RND(lev_fieldy);
11977       element = Feld[x][y];
11978
11979       if (!IS_PLAYER(x,y) &&
11980           (element == EL_EMPTY ||
11981            CAN_GROW_INTO(element) ||
11982            element == EL_QUICKSAND_EMPTY ||
11983            element == EL_QUICKSAND_FAST_EMPTY ||
11984            element == EL_ACID_SPLASH_LEFT ||
11985            element == EL_ACID_SPLASH_RIGHT))
11986       {
11987         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11988             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11989             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11990             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11991           Feld[x][y] = EL_AMOEBA_DROP;
11992       }
11993
11994       random = random * 129 + 1;
11995     }
11996   }
11997 #endif
11998
11999   game.explosions_delayed = FALSE;
12000
12001   SCAN_PLAYFIELD(x, y)
12002   {
12003     element = Feld[x][y];
12004
12005     if (ExplodeField[x][y])
12006       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12007     else if (element == EL_EXPLOSION)
12008       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12009
12010     ExplodeField[x][y] = EX_TYPE_NONE;
12011   }
12012
12013   game.explosions_delayed = TRUE;
12014
12015   if (game.magic_wall_active)
12016   {
12017     if (!(game.magic_wall_time_left % 4))
12018     {
12019       int element = Feld[magic_wall_x][magic_wall_y];
12020
12021       if (element == EL_BD_MAGIC_WALL_FULL ||
12022           element == EL_BD_MAGIC_WALL_ACTIVE ||
12023           element == EL_BD_MAGIC_WALL_EMPTYING)
12024         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12025       else if (element == EL_DC_MAGIC_WALL_FULL ||
12026                element == EL_DC_MAGIC_WALL_ACTIVE ||
12027                element == EL_DC_MAGIC_WALL_EMPTYING)
12028         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12029       else
12030         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12031     }
12032
12033     if (game.magic_wall_time_left > 0)
12034     {
12035       game.magic_wall_time_left--;
12036
12037       if (!game.magic_wall_time_left)
12038       {
12039         SCAN_PLAYFIELD(x, y)
12040         {
12041           element = Feld[x][y];
12042
12043           if (element == EL_MAGIC_WALL_ACTIVE ||
12044               element == EL_MAGIC_WALL_FULL)
12045           {
12046             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12047             TEST_DrawLevelField(x, y);
12048           }
12049           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12050                    element == EL_BD_MAGIC_WALL_FULL)
12051           {
12052             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12053             TEST_DrawLevelField(x, y);
12054           }
12055           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12056                    element == EL_DC_MAGIC_WALL_FULL)
12057           {
12058             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12059             TEST_DrawLevelField(x, y);
12060           }
12061         }
12062
12063         game.magic_wall_active = FALSE;
12064       }
12065     }
12066   }
12067
12068   if (game.light_time_left > 0)
12069   {
12070     game.light_time_left--;
12071
12072     if (game.light_time_left == 0)
12073       RedrawAllLightSwitchesAndInvisibleElements();
12074   }
12075
12076   if (game.timegate_time_left > 0)
12077   {
12078     game.timegate_time_left--;
12079
12080     if (game.timegate_time_left == 0)
12081       CloseAllOpenTimegates();
12082   }
12083
12084   if (game.lenses_time_left > 0)
12085   {
12086     game.lenses_time_left--;
12087
12088     if (game.lenses_time_left == 0)
12089       RedrawAllInvisibleElementsForLenses();
12090   }
12091
12092   if (game.magnify_time_left > 0)
12093   {
12094     game.magnify_time_left--;
12095
12096     if (game.magnify_time_left == 0)
12097       RedrawAllInvisibleElementsForMagnifier();
12098   }
12099
12100   for (i = 0; i < MAX_PLAYERS; i++)
12101   {
12102     struct PlayerInfo *player = &stored_player[i];
12103
12104     if (SHIELD_ON(player))
12105     {
12106       if (player->shield_deadly_time_left)
12107         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12108       else if (player->shield_normal_time_left)
12109         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12110     }
12111   }
12112
12113 #if USE_DELAYED_GFX_REDRAW
12114   SCAN_PLAYFIELD(x, y)
12115   {
12116     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12117     {
12118       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12119          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12120
12121       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12122         DrawLevelField(x, y);
12123
12124       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12125         DrawLevelFieldCrumbled(x, y);
12126
12127       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12128         DrawLevelFieldCrumbledNeighbours(x, y);
12129
12130       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12131         DrawTwinkleOnField(x, y);
12132     }
12133
12134     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12135   }
12136 #endif
12137
12138   DrawAllPlayers();
12139   PlayAllPlayersSound();
12140
12141   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12142   {
12143     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12144
12145     local_player->show_envelope = 0;
12146   }
12147
12148   // use random number generator in every frame to make it less predictable
12149   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12150     RND(1);
12151 }
12152
12153 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12154 {
12155   int min_x = x, min_y = y, max_x = x, max_y = y;
12156   int i;
12157
12158   for (i = 0; i < MAX_PLAYERS; i++)
12159   {
12160     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12161
12162     if (!stored_player[i].active || &stored_player[i] == player)
12163       continue;
12164
12165     min_x = MIN(min_x, jx);
12166     min_y = MIN(min_y, jy);
12167     max_x = MAX(max_x, jx);
12168     max_y = MAX(max_y, jy);
12169   }
12170
12171   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12172 }
12173
12174 static boolean AllPlayersInVisibleScreen(void)
12175 {
12176   int i;
12177
12178   for (i = 0; i < MAX_PLAYERS; i++)
12179   {
12180     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12181
12182     if (!stored_player[i].active)
12183       continue;
12184
12185     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12186       return FALSE;
12187   }
12188
12189   return TRUE;
12190 }
12191
12192 void ScrollLevel(int dx, int dy)
12193 {
12194   int scroll_offset = 2 * TILEX_VAR;
12195   int x, y;
12196
12197   BlitBitmap(drawto_field, drawto_field,
12198              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12199              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12200              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12201              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12202              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12203              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12204
12205   if (dx != 0)
12206   {
12207     x = (dx == 1 ? BX1 : BX2);
12208     for (y = BY1; y <= BY2; y++)
12209       DrawScreenField(x, y);
12210   }
12211
12212   if (dy != 0)
12213   {
12214     y = (dy == 1 ? BY1 : BY2);
12215     for (x = BX1; x <= BX2; x++)
12216       DrawScreenField(x, y);
12217   }
12218
12219   redraw_mask |= REDRAW_FIELD;
12220 }
12221
12222 static boolean canFallDown(struct PlayerInfo *player)
12223 {
12224   int jx = player->jx, jy = player->jy;
12225
12226   return (IN_LEV_FIELD(jx, jy + 1) &&
12227           (IS_FREE(jx, jy + 1) ||
12228            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12229           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12230           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12231 }
12232
12233 static boolean canPassField(int x, int y, int move_dir)
12234 {
12235   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12236   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12237   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12238   int nextx = x + dx;
12239   int nexty = y + dy;
12240   int element = Feld[x][y];
12241
12242   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12243           !CAN_MOVE(element) &&
12244           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12245           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12246           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12247 }
12248
12249 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12250 {
12251   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12252   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12253   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12254   int newx = x + dx;
12255   int newy = y + dy;
12256
12257   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12258           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12259           (IS_DIGGABLE(Feld[newx][newy]) ||
12260            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12261            canPassField(newx, newy, move_dir)));
12262 }
12263
12264 static void CheckGravityMovement(struct PlayerInfo *player)
12265 {
12266   if (player->gravity && !player->programmed_action)
12267   {
12268     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12269     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12270     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12271     int jx = player->jx, jy = player->jy;
12272     boolean player_is_moving_to_valid_field =
12273       (!player_is_snapping &&
12274        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12275         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12276     boolean player_can_fall_down = canFallDown(player);
12277
12278     if (player_can_fall_down &&
12279         !player_is_moving_to_valid_field)
12280       player->programmed_action = MV_DOWN;
12281   }
12282 }
12283
12284 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12285 {
12286   return CheckGravityMovement(player);
12287
12288   if (player->gravity && !player->programmed_action)
12289   {
12290     int jx = player->jx, jy = player->jy;
12291     boolean field_under_player_is_free =
12292       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12293     boolean player_is_standing_on_valid_field =
12294       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12295        (IS_WALKABLE(Feld[jx][jy]) &&
12296         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12297
12298     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12299       player->programmed_action = MV_DOWN;
12300   }
12301 }
12302
12303 /*
12304   MovePlayerOneStep()
12305   -----------------------------------------------------------------------------
12306   dx, dy:               direction (non-diagonal) to try to move the player to
12307   real_dx, real_dy:     direction as read from input device (can be diagonal)
12308 */
12309
12310 boolean MovePlayerOneStep(struct PlayerInfo *player,
12311                           int dx, int dy, int real_dx, int real_dy)
12312 {
12313   int jx = player->jx, jy = player->jy;
12314   int new_jx = jx + dx, new_jy = jy + dy;
12315   int can_move;
12316   boolean player_can_move = !player->cannot_move;
12317
12318   if (!player->active || (!dx && !dy))
12319     return MP_NO_ACTION;
12320
12321   player->MovDir = (dx < 0 ? MV_LEFT :
12322                     dx > 0 ? MV_RIGHT :
12323                     dy < 0 ? MV_UP :
12324                     dy > 0 ? MV_DOWN :  MV_NONE);
12325
12326   if (!IN_LEV_FIELD(new_jx, new_jy))
12327     return MP_NO_ACTION;
12328
12329   if (!player_can_move)
12330   {
12331     if (player->MovPos == 0)
12332     {
12333       player->is_moving = FALSE;
12334       player->is_digging = FALSE;
12335       player->is_collecting = FALSE;
12336       player->is_snapping = FALSE;
12337       player->is_pushing = FALSE;
12338     }
12339   }
12340
12341   if (!network.enabled && game.centered_player_nr == -1 &&
12342       !AllPlayersInSight(player, new_jx, new_jy))
12343     return MP_NO_ACTION;
12344
12345   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12346   if (can_move != MP_MOVING)
12347     return can_move;
12348
12349   // check if DigField() has caused relocation of the player
12350   if (player->jx != jx || player->jy != jy)
12351     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12352
12353   StorePlayer[jx][jy] = 0;
12354   player->last_jx = jx;
12355   player->last_jy = jy;
12356   player->jx = new_jx;
12357   player->jy = new_jy;
12358   StorePlayer[new_jx][new_jy] = player->element_nr;
12359
12360   if (player->move_delay_value_next != -1)
12361   {
12362     player->move_delay_value = player->move_delay_value_next;
12363     player->move_delay_value_next = -1;
12364   }
12365
12366   player->MovPos =
12367     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12368
12369   player->step_counter++;
12370
12371   PlayerVisit[jx][jy] = FrameCounter;
12372
12373   player->is_moving = TRUE;
12374
12375 #if 1
12376   // should better be called in MovePlayer(), but this breaks some tapes
12377   ScrollPlayer(player, SCROLL_INIT);
12378 #endif
12379
12380   return MP_MOVING;
12381 }
12382
12383 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12384 {
12385   int jx = player->jx, jy = player->jy;
12386   int old_jx = jx, old_jy = jy;
12387   int moved = MP_NO_ACTION;
12388
12389   if (!player->active)
12390     return FALSE;
12391
12392   if (!dx && !dy)
12393   {
12394     if (player->MovPos == 0)
12395     {
12396       player->is_moving = FALSE;
12397       player->is_digging = FALSE;
12398       player->is_collecting = FALSE;
12399       player->is_snapping = FALSE;
12400       player->is_pushing = FALSE;
12401     }
12402
12403     return FALSE;
12404   }
12405
12406   if (player->move_delay > 0)
12407     return FALSE;
12408
12409   player->move_delay = -1;              // set to "uninitialized" value
12410
12411   // store if player is automatically moved to next field
12412   player->is_auto_moving = (player->programmed_action != MV_NONE);
12413
12414   // remove the last programmed player action
12415   player->programmed_action = 0;
12416
12417   if (player->MovPos)
12418   {
12419     // should only happen if pre-1.2 tape recordings are played
12420     // this is only for backward compatibility
12421
12422     int original_move_delay_value = player->move_delay_value;
12423
12424 #if DEBUG
12425     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12426            tape.counter);
12427 #endif
12428
12429     // scroll remaining steps with finest movement resolution
12430     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12431
12432     while (player->MovPos)
12433     {
12434       ScrollPlayer(player, SCROLL_GO_ON);
12435       ScrollScreen(NULL, SCROLL_GO_ON);
12436
12437       AdvanceFrameAndPlayerCounters(player->index_nr);
12438
12439       DrawAllPlayers();
12440       BackToFront_WithFrameDelay(0);
12441     }
12442
12443     player->move_delay_value = original_move_delay_value;
12444   }
12445
12446   player->is_active = FALSE;
12447
12448   if (player->last_move_dir & MV_HORIZONTAL)
12449   {
12450     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12451       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12452   }
12453   else
12454   {
12455     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12456       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12457   }
12458
12459   if (!moved && !player->is_active)
12460   {
12461     player->is_moving = FALSE;
12462     player->is_digging = FALSE;
12463     player->is_collecting = FALSE;
12464     player->is_snapping = FALSE;
12465     player->is_pushing = FALSE;
12466   }
12467
12468   jx = player->jx;
12469   jy = player->jy;
12470
12471   if (moved & MP_MOVING && !ScreenMovPos &&
12472       (player->index_nr == game.centered_player_nr ||
12473        game.centered_player_nr == -1))
12474   {
12475     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12476     int offset = game.scroll_delay_value;
12477
12478     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12479     {
12480       // actual player has left the screen -- scroll in that direction
12481       if (jx != old_jx)         // player has moved horizontally
12482         scroll_x += (jx - old_jx);
12483       else                      // player has moved vertically
12484         scroll_y += (jy - old_jy);
12485     }
12486     else
12487     {
12488       if (jx != old_jx)         // player has moved horizontally
12489       {
12490         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12491             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12492           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12493
12494         // don't scroll over playfield boundaries
12495         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12496           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12497
12498         // don't scroll more than one field at a time
12499         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12500
12501         // don't scroll against the player's moving direction
12502         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12503             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12504           scroll_x = old_scroll_x;
12505       }
12506       else                      // player has moved vertically
12507       {
12508         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12509             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12510           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12511
12512         // don't scroll over playfield boundaries
12513         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12514           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12515
12516         // don't scroll more than one field at a time
12517         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12518
12519         // don't scroll against the player's moving direction
12520         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12521             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12522           scroll_y = old_scroll_y;
12523       }
12524     }
12525
12526     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12527     {
12528       if (!network.enabled && game.centered_player_nr == -1 &&
12529           !AllPlayersInVisibleScreen())
12530       {
12531         scroll_x = old_scroll_x;
12532         scroll_y = old_scroll_y;
12533       }
12534       else
12535       {
12536         ScrollScreen(player, SCROLL_INIT);
12537         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12538       }
12539     }
12540   }
12541
12542   player->StepFrame = 0;
12543
12544   if (moved & MP_MOVING)
12545   {
12546     if (old_jx != jx && old_jy == jy)
12547       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12548     else if (old_jx == jx && old_jy != jy)
12549       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12550
12551     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12552
12553     player->last_move_dir = player->MovDir;
12554     player->is_moving = TRUE;
12555     player->is_snapping = FALSE;
12556     player->is_switching = FALSE;
12557     player->is_dropping = FALSE;
12558     player->is_dropping_pressed = FALSE;
12559     player->drop_pressed_delay = 0;
12560
12561 #if 0
12562     // should better be called here than above, but this breaks some tapes
12563     ScrollPlayer(player, SCROLL_INIT);
12564 #endif
12565   }
12566   else
12567   {
12568     CheckGravityMovementWhenNotMoving(player);
12569
12570     player->is_moving = FALSE;
12571
12572     /* at this point, the player is allowed to move, but cannot move right now
12573        (e.g. because of something blocking the way) -- ensure that the player
12574        is also allowed to move in the next frame (in old versions before 3.1.1,
12575        the player was forced to wait again for eight frames before next try) */
12576
12577     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12578       player->move_delay = 0;   // allow direct movement in the next frame
12579   }
12580
12581   if (player->move_delay == -1)         // not yet initialized by DigField()
12582     player->move_delay = player->move_delay_value;
12583
12584   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12585   {
12586     TestIfPlayerTouchesBadThing(jx, jy);
12587     TestIfPlayerTouchesCustomElement(jx, jy);
12588   }
12589
12590   if (!player->active)
12591     RemovePlayer(player);
12592
12593   return moved;
12594 }
12595
12596 void ScrollPlayer(struct PlayerInfo *player, int mode)
12597 {
12598   int jx = player->jx, jy = player->jy;
12599   int last_jx = player->last_jx, last_jy = player->last_jy;
12600   int move_stepsize = TILEX / player->move_delay_value;
12601
12602   if (!player->active)
12603     return;
12604
12605   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12606     return;
12607
12608   if (mode == SCROLL_INIT)
12609   {
12610     player->actual_frame_counter = FrameCounter;
12611     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12612
12613     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12614         Feld[last_jx][last_jy] == EL_EMPTY)
12615     {
12616       int last_field_block_delay = 0;   // start with no blocking at all
12617       int block_delay_adjustment = player->block_delay_adjustment;
12618
12619       // if player blocks last field, add delay for exactly one move
12620       if (player->block_last_field)
12621       {
12622         last_field_block_delay += player->move_delay_value;
12623
12624         // when blocking enabled, prevent moving up despite gravity
12625         if (player->gravity && player->MovDir == MV_UP)
12626           block_delay_adjustment = -1;
12627       }
12628
12629       // add block delay adjustment (also possible when not blocking)
12630       last_field_block_delay += block_delay_adjustment;
12631
12632       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12633       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12634     }
12635
12636     if (player->MovPos != 0)    // player has not yet reached destination
12637       return;
12638   }
12639   else if (!FrameReached(&player->actual_frame_counter, 1))
12640     return;
12641
12642   if (player->MovPos != 0)
12643   {
12644     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12645     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12646
12647     // before DrawPlayer() to draw correct player graphic for this case
12648     if (player->MovPos == 0)
12649       CheckGravityMovement(player);
12650   }
12651
12652   if (player->MovPos == 0)      // player reached destination field
12653   {
12654     if (player->move_delay_reset_counter > 0)
12655     {
12656       player->move_delay_reset_counter--;
12657
12658       if (player->move_delay_reset_counter == 0)
12659       {
12660         // continue with normal speed after quickly moving through gate
12661         HALVE_PLAYER_SPEED(player);
12662
12663         // be able to make the next move without delay
12664         player->move_delay = 0;
12665       }
12666     }
12667
12668     player->last_jx = jx;
12669     player->last_jy = jy;
12670
12671     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12672         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12673         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12674         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12675         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12676         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12677         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12678         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12679     {
12680       ExitPlayer(player);
12681
12682       if (game.players_still_needed == 0 &&
12683           (game.friends_still_needed == 0 ||
12684            IS_SP_ELEMENT(Feld[jx][jy])))
12685         LevelSolved();
12686     }
12687
12688     // this breaks one level: "machine", level 000
12689     {
12690       int move_direction = player->MovDir;
12691       int enter_side = MV_DIR_OPPOSITE(move_direction);
12692       int leave_side = move_direction;
12693       int old_jx = last_jx;
12694       int old_jy = last_jy;
12695       int old_element = Feld[old_jx][old_jy];
12696       int new_element = Feld[jx][jy];
12697
12698       if (IS_CUSTOM_ELEMENT(old_element))
12699         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12700                                    CE_LEFT_BY_PLAYER,
12701                                    player->index_bit, leave_side);
12702
12703       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12704                                           CE_PLAYER_LEAVES_X,
12705                                           player->index_bit, leave_side);
12706
12707       if (IS_CUSTOM_ELEMENT(new_element))
12708         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12709                                    player->index_bit, enter_side);
12710
12711       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12712                                           CE_PLAYER_ENTERS_X,
12713                                           player->index_bit, enter_side);
12714
12715       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12716                                         CE_MOVE_OF_X, move_direction);
12717     }
12718
12719     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12720     {
12721       TestIfPlayerTouchesBadThing(jx, jy);
12722       TestIfPlayerTouchesCustomElement(jx, jy);
12723
12724       /* needed because pushed element has not yet reached its destination,
12725          so it would trigger a change event at its previous field location */
12726       if (!player->is_pushing)
12727         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12728
12729       if (!player->active)
12730         RemovePlayer(player);
12731     }
12732
12733     if (!game.LevelSolved && level.use_step_counter)
12734     {
12735       int i;
12736
12737       TimePlayed++;
12738
12739       if (TimeLeft > 0)
12740       {
12741         TimeLeft--;
12742
12743         if (TimeLeft <= 10 && setup.time_limit)
12744           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12745
12746         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12747
12748         DisplayGameControlValues();
12749
12750         if (!TimeLeft && setup.time_limit)
12751           for (i = 0; i < MAX_PLAYERS; i++)
12752             KillPlayer(&stored_player[i]);
12753       }
12754       else if (game.no_time_limit && !game.all_players_gone)
12755       {
12756         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12757
12758         DisplayGameControlValues();
12759       }
12760     }
12761
12762     if (tape.single_step && tape.recording && !tape.pausing &&
12763         !player->programmed_action)
12764       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12765
12766     if (!player->programmed_action)
12767       CheckSaveEngineSnapshot(player);
12768   }
12769 }
12770
12771 void ScrollScreen(struct PlayerInfo *player, int mode)
12772 {
12773   static unsigned int screen_frame_counter = 0;
12774
12775   if (mode == SCROLL_INIT)
12776   {
12777     // set scrolling step size according to actual player's moving speed
12778     ScrollStepSize = TILEX / player->move_delay_value;
12779
12780     screen_frame_counter = FrameCounter;
12781     ScreenMovDir = player->MovDir;
12782     ScreenMovPos = player->MovPos;
12783     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12784     return;
12785   }
12786   else if (!FrameReached(&screen_frame_counter, 1))
12787     return;
12788
12789   if (ScreenMovPos)
12790   {
12791     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12792     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12793     redraw_mask |= REDRAW_FIELD;
12794   }
12795   else
12796     ScreenMovDir = MV_NONE;
12797 }
12798
12799 void TestIfPlayerTouchesCustomElement(int x, int y)
12800 {
12801   static int xy[4][2] =
12802   {
12803     { 0, -1 },
12804     { -1, 0 },
12805     { +1, 0 },
12806     { 0, +1 }
12807   };
12808   static int trigger_sides[4][2] =
12809   {
12810     // center side       border side
12811     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12812     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12813     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12814     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12815   };
12816   static int touch_dir[4] =
12817   {
12818     MV_LEFT | MV_RIGHT,
12819     MV_UP   | MV_DOWN,
12820     MV_UP   | MV_DOWN,
12821     MV_LEFT | MV_RIGHT
12822   };
12823   int center_element = Feld[x][y];      // should always be non-moving!
12824   int i;
12825
12826   for (i = 0; i < NUM_DIRECTIONS; i++)
12827   {
12828     int xx = x + xy[i][0];
12829     int yy = y + xy[i][1];
12830     int center_side = trigger_sides[i][0];
12831     int border_side = trigger_sides[i][1];
12832     int border_element;
12833
12834     if (!IN_LEV_FIELD(xx, yy))
12835       continue;
12836
12837     if (IS_PLAYER(x, y))                // player found at center element
12838     {
12839       struct PlayerInfo *player = PLAYERINFO(x, y);
12840
12841       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12842         border_element = Feld[xx][yy];          // may be moving!
12843       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12844         border_element = Feld[xx][yy];
12845       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12846         border_element = MovingOrBlocked2Element(xx, yy);
12847       else
12848         continue;               // center and border element do not touch
12849
12850       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12851                                  player->index_bit, border_side);
12852       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12853                                           CE_PLAYER_TOUCHES_X,
12854                                           player->index_bit, border_side);
12855
12856       {
12857         /* use player element that is initially defined in the level playfield,
12858            not the player element that corresponds to the runtime player number
12859            (example: a level that contains EL_PLAYER_3 as the only player would
12860            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12861         int player_element = PLAYERINFO(x, y)->initial_element;
12862
12863         CheckElementChangeBySide(xx, yy, border_element, player_element,
12864                                  CE_TOUCHING_X, border_side);
12865       }
12866     }
12867     else if (IS_PLAYER(xx, yy))         // player found at border element
12868     {
12869       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12870
12871       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12872       {
12873         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12874           continue;             // center and border element do not touch
12875       }
12876
12877       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12878                                  player->index_bit, center_side);
12879       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12880                                           CE_PLAYER_TOUCHES_X,
12881                                           player->index_bit, center_side);
12882
12883       {
12884         /* use player element that is initially defined in the level playfield,
12885            not the player element that corresponds to the runtime player number
12886            (example: a level that contains EL_PLAYER_3 as the only player would
12887            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12888         int player_element = PLAYERINFO(xx, yy)->initial_element;
12889
12890         CheckElementChangeBySide(x, y, center_element, player_element,
12891                                  CE_TOUCHING_X, center_side);
12892       }
12893
12894       break;
12895     }
12896   }
12897 }
12898
12899 void TestIfElementTouchesCustomElement(int x, int y)
12900 {
12901   static int xy[4][2] =
12902   {
12903     { 0, -1 },
12904     { -1, 0 },
12905     { +1, 0 },
12906     { 0, +1 }
12907   };
12908   static int trigger_sides[4][2] =
12909   {
12910     // center side      border side
12911     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12912     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12913     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12914     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12915   };
12916   static int touch_dir[4] =
12917   {
12918     MV_LEFT | MV_RIGHT,
12919     MV_UP   | MV_DOWN,
12920     MV_UP   | MV_DOWN,
12921     MV_LEFT | MV_RIGHT
12922   };
12923   boolean change_center_element = FALSE;
12924   int center_element = Feld[x][y];      // should always be non-moving!
12925   int border_element_old[NUM_DIRECTIONS];
12926   int i;
12927
12928   for (i = 0; i < NUM_DIRECTIONS; i++)
12929   {
12930     int xx = x + xy[i][0];
12931     int yy = y + xy[i][1];
12932     int border_element;
12933
12934     border_element_old[i] = -1;
12935
12936     if (!IN_LEV_FIELD(xx, yy))
12937       continue;
12938
12939     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12940       border_element = Feld[xx][yy];    // may be moving!
12941     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12942       border_element = Feld[xx][yy];
12943     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
12944       border_element = MovingOrBlocked2Element(xx, yy);
12945     else
12946       continue;                 // center and border element do not touch
12947
12948     border_element_old[i] = border_element;
12949   }
12950
12951   for (i = 0; i < NUM_DIRECTIONS; i++)
12952   {
12953     int xx = x + xy[i][0];
12954     int yy = y + xy[i][1];
12955     int center_side = trigger_sides[i][0];
12956     int border_element = border_element_old[i];
12957
12958     if (border_element == -1)
12959       continue;
12960
12961     // check for change of border element
12962     CheckElementChangeBySide(xx, yy, border_element, center_element,
12963                              CE_TOUCHING_X, center_side);
12964
12965     // (center element cannot be player, so we dont have to check this here)
12966   }
12967
12968   for (i = 0; i < NUM_DIRECTIONS; i++)
12969   {
12970     int xx = x + xy[i][0];
12971     int yy = y + xy[i][1];
12972     int border_side = trigger_sides[i][1];
12973     int border_element = border_element_old[i];
12974
12975     if (border_element == -1)
12976       continue;
12977
12978     // check for change of center element (but change it only once)
12979     if (!change_center_element)
12980       change_center_element =
12981         CheckElementChangeBySide(x, y, center_element, border_element,
12982                                  CE_TOUCHING_X, border_side);
12983
12984     if (IS_PLAYER(xx, yy))
12985     {
12986       /* use player element that is initially defined in the level playfield,
12987          not the player element that corresponds to the runtime player number
12988          (example: a level that contains EL_PLAYER_3 as the only player would
12989          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12990       int player_element = PLAYERINFO(xx, yy)->initial_element;
12991
12992       CheckElementChangeBySide(x, y, center_element, player_element,
12993                                CE_TOUCHING_X, border_side);
12994     }
12995   }
12996 }
12997
12998 void TestIfElementHitsCustomElement(int x, int y, int direction)
12999 {
13000   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13001   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13002   int hitx = x + dx, hity = y + dy;
13003   int hitting_element = Feld[x][y];
13004   int touched_element;
13005
13006   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13007     return;
13008
13009   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13010                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13011
13012   if (IN_LEV_FIELD(hitx, hity))
13013   {
13014     int opposite_direction = MV_DIR_OPPOSITE(direction);
13015     int hitting_side = direction;
13016     int touched_side = opposite_direction;
13017     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13018                           MovDir[hitx][hity] != direction ||
13019                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13020
13021     object_hit = TRUE;
13022
13023     if (object_hit)
13024     {
13025       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13026                                CE_HITTING_X, touched_side);
13027
13028       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13029                                CE_HIT_BY_X, hitting_side);
13030
13031       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13032                                CE_HIT_BY_SOMETHING, opposite_direction);
13033
13034       if (IS_PLAYER(hitx, hity))
13035       {
13036         /* use player element that is initially defined in the level playfield,
13037            not the player element that corresponds to the runtime player number
13038            (example: a level that contains EL_PLAYER_3 as the only player would
13039            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13040         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13041
13042         CheckElementChangeBySide(x, y, hitting_element, player_element,
13043                                  CE_HITTING_X, touched_side);
13044       }
13045     }
13046   }
13047
13048   // "hitting something" is also true when hitting the playfield border
13049   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13050                            CE_HITTING_SOMETHING, direction);
13051 }
13052
13053 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13054 {
13055   int i, kill_x = -1, kill_y = -1;
13056
13057   int bad_element = -1;
13058   static int test_xy[4][2] =
13059   {
13060     { 0, -1 },
13061     { -1, 0 },
13062     { +1, 0 },
13063     { 0, +1 }
13064   };
13065   static int test_dir[4] =
13066   {
13067     MV_UP,
13068     MV_LEFT,
13069     MV_RIGHT,
13070     MV_DOWN
13071   };
13072
13073   for (i = 0; i < NUM_DIRECTIONS; i++)
13074   {
13075     int test_x, test_y, test_move_dir, test_element;
13076
13077     test_x = good_x + test_xy[i][0];
13078     test_y = good_y + test_xy[i][1];
13079
13080     if (!IN_LEV_FIELD(test_x, test_y))
13081       continue;
13082
13083     test_move_dir =
13084       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13085
13086     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13087
13088     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13089        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13090     */
13091     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13092         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13093     {
13094       kill_x = test_x;
13095       kill_y = test_y;
13096       bad_element = test_element;
13097
13098       break;
13099     }
13100   }
13101
13102   if (kill_x != -1 || kill_y != -1)
13103   {
13104     if (IS_PLAYER(good_x, good_y))
13105     {
13106       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13107
13108       if (player->shield_deadly_time_left > 0 &&
13109           !IS_INDESTRUCTIBLE(bad_element))
13110         Bang(kill_x, kill_y);
13111       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13112         KillPlayer(player);
13113     }
13114     else
13115       Bang(good_x, good_y);
13116   }
13117 }
13118
13119 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13120 {
13121   int i, kill_x = -1, kill_y = -1;
13122   int bad_element = Feld[bad_x][bad_y];
13123   static int test_xy[4][2] =
13124   {
13125     { 0, -1 },
13126     { -1, 0 },
13127     { +1, 0 },
13128     { 0, +1 }
13129   };
13130   static int touch_dir[4] =
13131   {
13132     MV_LEFT | MV_RIGHT,
13133     MV_UP   | MV_DOWN,
13134     MV_UP   | MV_DOWN,
13135     MV_LEFT | MV_RIGHT
13136   };
13137   static int test_dir[4] =
13138   {
13139     MV_UP,
13140     MV_LEFT,
13141     MV_RIGHT,
13142     MV_DOWN
13143   };
13144
13145   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13146     return;
13147
13148   for (i = 0; i < NUM_DIRECTIONS; i++)
13149   {
13150     int test_x, test_y, test_move_dir, test_element;
13151
13152     test_x = bad_x + test_xy[i][0];
13153     test_y = bad_y + test_xy[i][1];
13154
13155     if (!IN_LEV_FIELD(test_x, test_y))
13156       continue;
13157
13158     test_move_dir =
13159       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13160
13161     test_element = Feld[test_x][test_y];
13162
13163     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13164        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13165     */
13166     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13167         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13168     {
13169       // good thing is player or penguin that does not move away
13170       if (IS_PLAYER(test_x, test_y))
13171       {
13172         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13173
13174         if (bad_element == EL_ROBOT && player->is_moving)
13175           continue;     // robot does not kill player if he is moving
13176
13177         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13178         {
13179           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13180             continue;           // center and border element do not touch
13181         }
13182
13183         kill_x = test_x;
13184         kill_y = test_y;
13185
13186         break;
13187       }
13188       else if (test_element == EL_PENGUIN)
13189       {
13190         kill_x = test_x;
13191         kill_y = test_y;
13192
13193         break;
13194       }
13195     }
13196   }
13197
13198   if (kill_x != -1 || kill_y != -1)
13199   {
13200     if (IS_PLAYER(kill_x, kill_y))
13201     {
13202       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13203
13204       if (player->shield_deadly_time_left > 0 &&
13205           !IS_INDESTRUCTIBLE(bad_element))
13206         Bang(bad_x, bad_y);
13207       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13208         KillPlayer(player);
13209     }
13210     else
13211       Bang(kill_x, kill_y);
13212   }
13213 }
13214
13215 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13216 {
13217   int bad_element = Feld[bad_x][bad_y];
13218   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13219   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13220   int test_x = bad_x + dx, test_y = bad_y + dy;
13221   int test_move_dir, test_element;
13222   int kill_x = -1, kill_y = -1;
13223
13224   if (!IN_LEV_FIELD(test_x, test_y))
13225     return;
13226
13227   test_move_dir =
13228     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13229
13230   test_element = Feld[test_x][test_y];
13231
13232   if (test_move_dir != bad_move_dir)
13233   {
13234     // good thing can be player or penguin that does not move away
13235     if (IS_PLAYER(test_x, test_y))
13236     {
13237       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13238
13239       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13240          player as being hit when he is moving towards the bad thing, because
13241          the "get hit by" condition would be lost after the player stops) */
13242       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13243         return;         // player moves away from bad thing
13244
13245       kill_x = test_x;
13246       kill_y = test_y;
13247     }
13248     else if (test_element == EL_PENGUIN)
13249     {
13250       kill_x = test_x;
13251       kill_y = test_y;
13252     }
13253   }
13254
13255   if (kill_x != -1 || kill_y != -1)
13256   {
13257     if (IS_PLAYER(kill_x, kill_y))
13258     {
13259       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13260
13261       if (player->shield_deadly_time_left > 0 &&
13262           !IS_INDESTRUCTIBLE(bad_element))
13263         Bang(bad_x, bad_y);
13264       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13265         KillPlayer(player);
13266     }
13267     else
13268       Bang(kill_x, kill_y);
13269   }
13270 }
13271
13272 void TestIfPlayerTouchesBadThing(int x, int y)
13273 {
13274   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13275 }
13276
13277 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13278 {
13279   TestIfGoodThingHitsBadThing(x, y, move_dir);
13280 }
13281
13282 void TestIfBadThingTouchesPlayer(int x, int y)
13283 {
13284   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13285 }
13286
13287 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13288 {
13289   TestIfBadThingHitsGoodThing(x, y, move_dir);
13290 }
13291
13292 void TestIfFriendTouchesBadThing(int x, int y)
13293 {
13294   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13295 }
13296
13297 void TestIfBadThingTouchesFriend(int x, int y)
13298 {
13299   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13300 }
13301
13302 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13303 {
13304   int i, kill_x = bad_x, kill_y = bad_y;
13305   static int xy[4][2] =
13306   {
13307     { 0, -1 },
13308     { -1, 0 },
13309     { +1, 0 },
13310     { 0, +1 }
13311   };
13312
13313   for (i = 0; i < NUM_DIRECTIONS; i++)
13314   {
13315     int x, y, element;
13316
13317     x = bad_x + xy[i][0];
13318     y = bad_y + xy[i][1];
13319     if (!IN_LEV_FIELD(x, y))
13320       continue;
13321
13322     element = Feld[x][y];
13323     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13324         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13325     {
13326       kill_x = x;
13327       kill_y = y;
13328       break;
13329     }
13330   }
13331
13332   if (kill_x != bad_x || kill_y != bad_y)
13333     Bang(bad_x, bad_y);
13334 }
13335
13336 void KillPlayer(struct PlayerInfo *player)
13337 {
13338   int jx = player->jx, jy = player->jy;
13339
13340   if (!player->active)
13341     return;
13342
13343 #if 0
13344   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13345          player->killed, player->active, player->reanimated);
13346 #endif
13347
13348   /* the following code was introduced to prevent an infinite loop when calling
13349      -> Bang()
13350      -> CheckTriggeredElementChangeExt()
13351      -> ExecuteCustomElementAction()
13352      -> KillPlayer()
13353      -> (infinitely repeating the above sequence of function calls)
13354      which occurs when killing the player while having a CE with the setting
13355      "kill player X when explosion of <player X>"; the solution using a new
13356      field "player->killed" was chosen for backwards compatibility, although
13357      clever use of the fields "player->active" etc. would probably also work */
13358 #if 1
13359   if (player->killed)
13360     return;
13361 #endif
13362
13363   player->killed = TRUE;
13364
13365   // remove accessible field at the player's position
13366   Feld[jx][jy] = EL_EMPTY;
13367
13368   // deactivate shield (else Bang()/Explode() would not work right)
13369   player->shield_normal_time_left = 0;
13370   player->shield_deadly_time_left = 0;
13371
13372 #if 0
13373   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13374          player->killed, player->active, player->reanimated);
13375 #endif
13376
13377   Bang(jx, jy);
13378
13379 #if 0
13380   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13381          player->killed, player->active, player->reanimated);
13382 #endif
13383
13384   if (player->reanimated)       // killed player may have been reanimated
13385     player->killed = player->reanimated = FALSE;
13386   else
13387     BuryPlayer(player);
13388 }
13389
13390 static void KillPlayerUnlessEnemyProtected(int x, int y)
13391 {
13392   if (!PLAYER_ENEMY_PROTECTED(x, y))
13393     KillPlayer(PLAYERINFO(x, y));
13394 }
13395
13396 static void KillPlayerUnlessExplosionProtected(int x, int y)
13397 {
13398   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13399     KillPlayer(PLAYERINFO(x, y));
13400 }
13401
13402 void BuryPlayer(struct PlayerInfo *player)
13403 {
13404   int jx = player->jx, jy = player->jy;
13405
13406   if (!player->active)
13407     return;
13408
13409   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13410   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13411
13412   RemovePlayer(player);
13413
13414   player->buried = TRUE;
13415
13416   if (game.all_players_gone)
13417     game.GameOver = TRUE;
13418 }
13419
13420 void RemovePlayer(struct PlayerInfo *player)
13421 {
13422   int jx = player->jx, jy = player->jy;
13423   int i, found = FALSE;
13424
13425   player->present = FALSE;
13426   player->active = FALSE;
13427
13428   if (!ExplodeField[jx][jy])
13429     StorePlayer[jx][jy] = 0;
13430
13431   if (player->is_moving)
13432     TEST_DrawLevelField(player->last_jx, player->last_jy);
13433
13434   for (i = 0; i < MAX_PLAYERS; i++)
13435     if (stored_player[i].active)
13436       found = TRUE;
13437
13438   if (!found)
13439   {
13440     game.all_players_gone = TRUE;
13441     game.GameOver = TRUE;
13442   }
13443
13444   ExitX = ZX = jx;
13445   ExitY = ZY = jy;
13446 }
13447
13448 void ExitPlayer(struct PlayerInfo *player)
13449 {
13450   DrawPlayer(player);   // needed here only to cleanup last field
13451   RemovePlayer(player);
13452
13453   if (game.players_still_needed > 0)
13454     game.players_still_needed--;
13455 }
13456
13457 static void setFieldForSnapping(int x, int y, int element, int direction)
13458 {
13459   struct ElementInfo *ei = &element_info[element];
13460   int direction_bit = MV_DIR_TO_BIT(direction);
13461   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13462   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13463                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13464
13465   Feld[x][y] = EL_ELEMENT_SNAPPING;
13466   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13467
13468   ResetGfxAnimation(x, y);
13469
13470   GfxElement[x][y] = element;
13471   GfxAction[x][y] = action;
13472   GfxDir[x][y] = direction;
13473   GfxFrame[x][y] = -1;
13474 }
13475
13476 /*
13477   =============================================================================
13478   checkDiagonalPushing()
13479   -----------------------------------------------------------------------------
13480   check if diagonal input device direction results in pushing of object
13481   (by checking if the alternative direction is walkable, diggable, ...)
13482   =============================================================================
13483 */
13484
13485 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13486                                     int x, int y, int real_dx, int real_dy)
13487 {
13488   int jx, jy, dx, dy, xx, yy;
13489
13490   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13491     return TRUE;
13492
13493   // diagonal direction: check alternative direction
13494   jx = player->jx;
13495   jy = player->jy;
13496   dx = x - jx;
13497   dy = y - jy;
13498   xx = jx + (dx == 0 ? real_dx : 0);
13499   yy = jy + (dy == 0 ? real_dy : 0);
13500
13501   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13502 }
13503
13504 /*
13505   =============================================================================
13506   DigField()
13507   -----------------------------------------------------------------------------
13508   x, y:                 field next to player (non-diagonal) to try to dig to
13509   real_dx, real_dy:     direction as read from input device (can be diagonal)
13510   =============================================================================
13511 */
13512
13513 static int DigField(struct PlayerInfo *player,
13514                     int oldx, int oldy, int x, int y,
13515                     int real_dx, int real_dy, int mode)
13516 {
13517   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13518   boolean player_was_pushing = player->is_pushing;
13519   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13520   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13521   int jx = oldx, jy = oldy;
13522   int dx = x - jx, dy = y - jy;
13523   int nextx = x + dx, nexty = y + dy;
13524   int move_direction = (dx == -1 ? MV_LEFT  :
13525                         dx == +1 ? MV_RIGHT :
13526                         dy == -1 ? MV_UP    :
13527                         dy == +1 ? MV_DOWN  : MV_NONE);
13528   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13529   int dig_side = MV_DIR_OPPOSITE(move_direction);
13530   int old_element = Feld[jx][jy];
13531   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13532   int collect_count;
13533
13534   if (is_player)                // function can also be called by EL_PENGUIN
13535   {
13536     if (player->MovPos == 0)
13537     {
13538       player->is_digging = FALSE;
13539       player->is_collecting = FALSE;
13540     }
13541
13542     if (player->MovPos == 0)    // last pushing move finished
13543       player->is_pushing = FALSE;
13544
13545     if (mode == DF_NO_PUSH)     // player just stopped pushing
13546     {
13547       player->is_switching = FALSE;
13548       player->push_delay = -1;
13549
13550       return MP_NO_ACTION;
13551     }
13552   }
13553
13554   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13555     old_element = Back[jx][jy];
13556
13557   // in case of element dropped at player position, check background
13558   else if (Back[jx][jy] != EL_EMPTY &&
13559            game.engine_version >= VERSION_IDENT(2,2,0,0))
13560     old_element = Back[jx][jy];
13561
13562   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13563     return MP_NO_ACTION;        // field has no opening in this direction
13564
13565   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13566     return MP_NO_ACTION;        // field has no opening in this direction
13567
13568   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13569   {
13570     SplashAcid(x, y);
13571
13572     Feld[jx][jy] = player->artwork_element;
13573     InitMovingField(jx, jy, MV_DOWN);
13574     Store[jx][jy] = EL_ACID;
13575     ContinueMoving(jx, jy);
13576     BuryPlayer(player);
13577
13578     return MP_DONT_RUN_INTO;
13579   }
13580
13581   if (player_can_move && DONT_RUN_INTO(element))
13582   {
13583     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13584
13585     return MP_DONT_RUN_INTO;
13586   }
13587
13588   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13589     return MP_NO_ACTION;
13590
13591   collect_count = element_info[element].collect_count_initial;
13592
13593   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13594     return MP_NO_ACTION;
13595
13596   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13597     player_can_move = player_can_move_or_snap;
13598
13599   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13600       game.engine_version >= VERSION_IDENT(2,2,0,0))
13601   {
13602     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13603                                player->index_bit, dig_side);
13604     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13605                                         player->index_bit, dig_side);
13606
13607     if (element == EL_DC_LANDMINE)
13608       Bang(x, y);
13609
13610     if (Feld[x][y] != element)          // field changed by snapping
13611       return MP_ACTION;
13612
13613     return MP_NO_ACTION;
13614   }
13615
13616   if (player->gravity && is_player && !player->is_auto_moving &&
13617       canFallDown(player) && move_direction != MV_DOWN &&
13618       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13619     return MP_NO_ACTION;        // player cannot walk here due to gravity
13620
13621   if (player_can_move &&
13622       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13623   {
13624     int sound_element = SND_ELEMENT(element);
13625     int sound_action = ACTION_WALKING;
13626
13627     if (IS_RND_GATE(element))
13628     {
13629       if (!player->key[RND_GATE_NR(element)])
13630         return MP_NO_ACTION;
13631     }
13632     else if (IS_RND_GATE_GRAY(element))
13633     {
13634       if (!player->key[RND_GATE_GRAY_NR(element)])
13635         return MP_NO_ACTION;
13636     }
13637     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13638     {
13639       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13640         return MP_NO_ACTION;
13641     }
13642     else if (element == EL_EXIT_OPEN ||
13643              element == EL_EM_EXIT_OPEN ||
13644              element == EL_EM_EXIT_OPENING ||
13645              element == EL_STEEL_EXIT_OPEN ||
13646              element == EL_EM_STEEL_EXIT_OPEN ||
13647              element == EL_EM_STEEL_EXIT_OPENING ||
13648              element == EL_SP_EXIT_OPEN ||
13649              element == EL_SP_EXIT_OPENING)
13650     {
13651       sound_action = ACTION_PASSING;    // player is passing exit
13652     }
13653     else if (element == EL_EMPTY)
13654     {
13655       sound_action = ACTION_MOVING;             // nothing to walk on
13656     }
13657
13658     // play sound from background or player, whatever is available
13659     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13660       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13661     else
13662       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13663   }
13664   else if (player_can_move &&
13665            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13666   {
13667     if (!ACCESS_FROM(element, opposite_direction))
13668       return MP_NO_ACTION;      // field not accessible from this direction
13669
13670     if (CAN_MOVE(element))      // only fixed elements can be passed!
13671       return MP_NO_ACTION;
13672
13673     if (IS_EM_GATE(element))
13674     {
13675       if (!player->key[EM_GATE_NR(element)])
13676         return MP_NO_ACTION;
13677     }
13678     else if (IS_EM_GATE_GRAY(element))
13679     {
13680       if (!player->key[EM_GATE_GRAY_NR(element)])
13681         return MP_NO_ACTION;
13682     }
13683     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13684     {
13685       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13686         return MP_NO_ACTION;
13687     }
13688     else if (IS_EMC_GATE(element))
13689     {
13690       if (!player->key[EMC_GATE_NR(element)])
13691         return MP_NO_ACTION;
13692     }
13693     else if (IS_EMC_GATE_GRAY(element))
13694     {
13695       if (!player->key[EMC_GATE_GRAY_NR(element)])
13696         return MP_NO_ACTION;
13697     }
13698     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13699     {
13700       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13701         return MP_NO_ACTION;
13702     }
13703     else if (element == EL_DC_GATE_WHITE ||
13704              element == EL_DC_GATE_WHITE_GRAY ||
13705              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13706     {
13707       if (player->num_white_keys == 0)
13708         return MP_NO_ACTION;
13709
13710       player->num_white_keys--;
13711     }
13712     else if (IS_SP_PORT(element))
13713     {
13714       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13715           element == EL_SP_GRAVITY_PORT_RIGHT ||
13716           element == EL_SP_GRAVITY_PORT_UP ||
13717           element == EL_SP_GRAVITY_PORT_DOWN)
13718         player->gravity = !player->gravity;
13719       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13720                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13721                element == EL_SP_GRAVITY_ON_PORT_UP ||
13722                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13723         player->gravity = TRUE;
13724       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13725                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13726                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13727                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13728         player->gravity = FALSE;
13729     }
13730
13731     // automatically move to the next field with double speed
13732     player->programmed_action = move_direction;
13733
13734     if (player->move_delay_reset_counter == 0)
13735     {
13736       player->move_delay_reset_counter = 2;     // two double speed steps
13737
13738       DOUBLE_PLAYER_SPEED(player);
13739     }
13740
13741     PlayLevelSoundAction(x, y, ACTION_PASSING);
13742   }
13743   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13744   {
13745     RemoveField(x, y);
13746
13747     if (mode != DF_SNAP)
13748     {
13749       GfxElement[x][y] = GFX_ELEMENT(element);
13750       player->is_digging = TRUE;
13751     }
13752
13753     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13754
13755     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13756                                         player->index_bit, dig_side);
13757
13758     if (mode == DF_SNAP)
13759     {
13760       if (level.block_snap_field)
13761         setFieldForSnapping(x, y, element, move_direction);
13762       else
13763         TestIfElementTouchesCustomElement(x, y);        // for empty space
13764
13765       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13766                                           player->index_bit, dig_side);
13767     }
13768   }
13769   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13770   {
13771     RemoveField(x, y);
13772
13773     if (is_player && mode != DF_SNAP)
13774     {
13775       GfxElement[x][y] = element;
13776       player->is_collecting = TRUE;
13777     }
13778
13779     if (element == EL_SPEED_PILL)
13780     {
13781       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13782     }
13783     else if (element == EL_EXTRA_TIME && level.time > 0)
13784     {
13785       TimeLeft += level.extra_time;
13786
13787       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13788
13789       DisplayGameControlValues();
13790     }
13791     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13792     {
13793       player->shield_normal_time_left += level.shield_normal_time;
13794       if (element == EL_SHIELD_DEADLY)
13795         player->shield_deadly_time_left += level.shield_deadly_time;
13796     }
13797     else if (element == EL_DYNAMITE ||
13798              element == EL_EM_DYNAMITE ||
13799              element == EL_SP_DISK_RED)
13800     {
13801       if (player->inventory_size < MAX_INVENTORY_SIZE)
13802         player->inventory_element[player->inventory_size++] = element;
13803
13804       DrawGameDoorValues();
13805     }
13806     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13807     {
13808       player->dynabomb_count++;
13809       player->dynabombs_left++;
13810     }
13811     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13812     {
13813       player->dynabomb_size++;
13814     }
13815     else if (element == EL_DYNABOMB_INCREASE_POWER)
13816     {
13817       player->dynabomb_xl = TRUE;
13818     }
13819     else if (IS_KEY(element))
13820     {
13821       player->key[KEY_NR(element)] = TRUE;
13822
13823       DrawGameDoorValues();
13824     }
13825     else if (element == EL_DC_KEY_WHITE)
13826     {
13827       player->num_white_keys++;
13828
13829       // display white keys?
13830       // DrawGameDoorValues();
13831     }
13832     else if (IS_ENVELOPE(element))
13833     {
13834       player->show_envelope = element;
13835     }
13836     else if (element == EL_EMC_LENSES)
13837     {
13838       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13839
13840       RedrawAllInvisibleElementsForLenses();
13841     }
13842     else if (element == EL_EMC_MAGNIFIER)
13843     {
13844       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13845
13846       RedrawAllInvisibleElementsForMagnifier();
13847     }
13848     else if (IS_DROPPABLE(element) ||
13849              IS_THROWABLE(element))     // can be collected and dropped
13850     {
13851       int i;
13852
13853       if (collect_count == 0)
13854         player->inventory_infinite_element = element;
13855       else
13856         for (i = 0; i < collect_count; i++)
13857           if (player->inventory_size < MAX_INVENTORY_SIZE)
13858             player->inventory_element[player->inventory_size++] = element;
13859
13860       DrawGameDoorValues();
13861     }
13862     else if (collect_count > 0)
13863     {
13864       game.gems_still_needed -= collect_count;
13865       if (game.gems_still_needed < 0)
13866         game.gems_still_needed = 0;
13867
13868       game.snapshot.collected_item = TRUE;
13869
13870       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
13871
13872       DisplayGameControlValues();
13873     }
13874
13875     RaiseScoreElement(element);
13876     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13877
13878     if (is_player)
13879       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13880                                           player->index_bit, dig_side);
13881
13882     if (mode == DF_SNAP)
13883     {
13884       if (level.block_snap_field)
13885         setFieldForSnapping(x, y, element, move_direction);
13886       else
13887         TestIfElementTouchesCustomElement(x, y);        // for empty space
13888
13889       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13890                                           player->index_bit, dig_side);
13891     }
13892   }
13893   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13894   {
13895     if (mode == DF_SNAP && element != EL_BD_ROCK)
13896       return MP_NO_ACTION;
13897
13898     if (CAN_FALL(element) && dy)
13899       return MP_NO_ACTION;
13900
13901     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13902         !(element == EL_SPRING && level.use_spring_bug))
13903       return MP_NO_ACTION;
13904
13905     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13906         ((move_direction & MV_VERTICAL &&
13907           ((element_info[element].move_pattern & MV_LEFT &&
13908             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13909            (element_info[element].move_pattern & MV_RIGHT &&
13910             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13911          (move_direction & MV_HORIZONTAL &&
13912           ((element_info[element].move_pattern & MV_UP &&
13913             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13914            (element_info[element].move_pattern & MV_DOWN &&
13915             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13916       return MP_NO_ACTION;
13917
13918     // do not push elements already moving away faster than player
13919     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13920         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13921       return MP_NO_ACTION;
13922
13923     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13924     {
13925       if (player->push_delay_value == -1 || !player_was_pushing)
13926         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13927     }
13928     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13929     {
13930       if (player->push_delay_value == -1)
13931         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13932     }
13933     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13934     {
13935       if (!player->is_pushing)
13936         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13937     }
13938
13939     player->is_pushing = TRUE;
13940     player->is_active = TRUE;
13941
13942     if (!(IN_LEV_FIELD(nextx, nexty) &&
13943           (IS_FREE(nextx, nexty) ||
13944            (IS_SB_ELEMENT(element) &&
13945             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13946            (IS_CUSTOM_ELEMENT(element) &&
13947             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13948       return MP_NO_ACTION;
13949
13950     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13951       return MP_NO_ACTION;
13952
13953     if (player->push_delay == -1)       // new pushing; restart delay
13954       player->push_delay = 0;
13955
13956     if (player->push_delay < player->push_delay_value &&
13957         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13958         element != EL_SPRING && element != EL_BALLOON)
13959     {
13960       // make sure that there is no move delay before next try to push
13961       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13962         player->move_delay = 0;
13963
13964       return MP_NO_ACTION;
13965     }
13966
13967     if (IS_CUSTOM_ELEMENT(element) &&
13968         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13969     {
13970       if (!DigFieldByCE(nextx, nexty, element))
13971         return MP_NO_ACTION;
13972     }
13973
13974     if (IS_SB_ELEMENT(element))
13975     {
13976       boolean sokoban_task_solved = FALSE;
13977
13978       if (element == EL_SOKOBAN_FIELD_FULL)
13979       {
13980         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13981
13982         IncrementSokobanFieldsNeeded();
13983         IncrementSokobanObjectsNeeded();
13984       }
13985
13986       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13987       {
13988         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13989
13990         DecrementSokobanFieldsNeeded();
13991         DecrementSokobanObjectsNeeded();
13992
13993         // sokoban object was pushed from empty field to sokoban field
13994         if (Back[x][y] == EL_EMPTY)
13995           sokoban_task_solved = TRUE;
13996       }
13997
13998       Feld[x][y] = EL_SOKOBAN_OBJECT;
13999
14000       if (Back[x][y] == Back[nextx][nexty])
14001         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14002       else if (Back[x][y] != 0)
14003         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14004                                     ACTION_EMPTYING);
14005       else
14006         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14007                                     ACTION_FILLING);
14008
14009       if (sokoban_task_solved &&
14010           game.sokoban_fields_still_needed == 0 &&
14011           game.sokoban_objects_still_needed == 0 &&
14012           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14013       {
14014         game.players_still_needed = 0;
14015
14016         LevelSolved();
14017
14018         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14019       }
14020     }
14021     else
14022       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14023
14024     InitMovingField(x, y, move_direction);
14025     GfxAction[x][y] = ACTION_PUSHING;
14026
14027     if (mode == DF_SNAP)
14028       ContinueMoving(x, y);
14029     else
14030       MovPos[x][y] = (dx != 0 ? dx : dy);
14031
14032     Pushed[x][y] = TRUE;
14033     Pushed[nextx][nexty] = TRUE;
14034
14035     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14036       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14037     else
14038       player->push_delay_value = -1;    // get new value later
14039
14040     // check for element change _after_ element has been pushed
14041     if (game.use_change_when_pushing_bug)
14042     {
14043       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14044                                  player->index_bit, dig_side);
14045       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14046                                           player->index_bit, dig_side);
14047     }
14048   }
14049   else if (IS_SWITCHABLE(element))
14050   {
14051     if (PLAYER_SWITCHING(player, x, y))
14052     {
14053       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14054                                           player->index_bit, dig_side);
14055
14056       return MP_ACTION;
14057     }
14058
14059     player->is_switching = TRUE;
14060     player->switch_x = x;
14061     player->switch_y = y;
14062
14063     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14064
14065     if (element == EL_ROBOT_WHEEL)
14066     {
14067       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14068       ZX = x;
14069       ZY = y;
14070
14071       game.robot_wheel_active = TRUE;
14072
14073       TEST_DrawLevelField(x, y);
14074     }
14075     else if (element == EL_SP_TERMINAL)
14076     {
14077       int xx, yy;
14078
14079       SCAN_PLAYFIELD(xx, yy)
14080       {
14081         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14082         {
14083           Bang(xx, yy);
14084         }
14085         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14086         {
14087           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14088
14089           ResetGfxAnimation(xx, yy);
14090           TEST_DrawLevelField(xx, yy);
14091         }
14092       }
14093     }
14094     else if (IS_BELT_SWITCH(element))
14095     {
14096       ToggleBeltSwitch(x, y);
14097     }
14098     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14099              element == EL_SWITCHGATE_SWITCH_DOWN ||
14100              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14101              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14102     {
14103       ToggleSwitchgateSwitch(x, y);
14104     }
14105     else if (element == EL_LIGHT_SWITCH ||
14106              element == EL_LIGHT_SWITCH_ACTIVE)
14107     {
14108       ToggleLightSwitch(x, y);
14109     }
14110     else if (element == EL_TIMEGATE_SWITCH ||
14111              element == EL_DC_TIMEGATE_SWITCH)
14112     {
14113       ActivateTimegateSwitch(x, y);
14114     }
14115     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14116              element == EL_BALLOON_SWITCH_RIGHT ||
14117              element == EL_BALLOON_SWITCH_UP    ||
14118              element == EL_BALLOON_SWITCH_DOWN  ||
14119              element == EL_BALLOON_SWITCH_NONE  ||
14120              element == EL_BALLOON_SWITCH_ANY)
14121     {
14122       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14123                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14124                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14125                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14126                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14127                              move_direction);
14128     }
14129     else if (element == EL_LAMP)
14130     {
14131       Feld[x][y] = EL_LAMP_ACTIVE;
14132       game.lights_still_needed--;
14133
14134       ResetGfxAnimation(x, y);
14135       TEST_DrawLevelField(x, y);
14136     }
14137     else if (element == EL_TIME_ORB_FULL)
14138     {
14139       Feld[x][y] = EL_TIME_ORB_EMPTY;
14140
14141       if (level.time > 0 || level.use_time_orb_bug)
14142       {
14143         TimeLeft += level.time_orb_time;
14144         game.no_time_limit = FALSE;
14145
14146         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14147
14148         DisplayGameControlValues();
14149       }
14150
14151       ResetGfxAnimation(x, y);
14152       TEST_DrawLevelField(x, y);
14153     }
14154     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14155              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14156     {
14157       int xx, yy;
14158
14159       game.ball_state = !game.ball_state;
14160
14161       SCAN_PLAYFIELD(xx, yy)
14162       {
14163         int e = Feld[xx][yy];
14164
14165         if (game.ball_state)
14166         {
14167           if (e == EL_EMC_MAGIC_BALL)
14168             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14169           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14170             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14171         }
14172         else
14173         {
14174           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14175             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14176           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14177             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14178         }
14179       }
14180     }
14181
14182     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14183                                         player->index_bit, dig_side);
14184
14185     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14186                                         player->index_bit, dig_side);
14187
14188     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14189                                         player->index_bit, dig_side);
14190
14191     return MP_ACTION;
14192   }
14193   else
14194   {
14195     if (!PLAYER_SWITCHING(player, x, y))
14196     {
14197       player->is_switching = TRUE;
14198       player->switch_x = x;
14199       player->switch_y = y;
14200
14201       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14202                                  player->index_bit, dig_side);
14203       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14204                                           player->index_bit, dig_side);
14205
14206       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14207                                  player->index_bit, dig_side);
14208       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14209                                           player->index_bit, dig_side);
14210     }
14211
14212     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14213                                player->index_bit, dig_side);
14214     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14215                                         player->index_bit, dig_side);
14216
14217     return MP_NO_ACTION;
14218   }
14219
14220   player->push_delay = -1;
14221
14222   if (is_player)                // function can also be called by EL_PENGUIN
14223   {
14224     if (Feld[x][y] != element)          // really digged/collected something
14225     {
14226       player->is_collecting = !player->is_digging;
14227       player->is_active = TRUE;
14228     }
14229   }
14230
14231   return MP_MOVING;
14232 }
14233
14234 static boolean DigFieldByCE(int x, int y, int digging_element)
14235 {
14236   int element = Feld[x][y];
14237
14238   if (!IS_FREE(x, y))
14239   {
14240     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14241                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14242                   ACTION_BREAKING);
14243
14244     // no element can dig solid indestructible elements
14245     if (IS_INDESTRUCTIBLE(element) &&
14246         !IS_DIGGABLE(element) &&
14247         !IS_COLLECTIBLE(element))
14248       return FALSE;
14249
14250     if (AmoebaNr[x][y] &&
14251         (element == EL_AMOEBA_FULL ||
14252          element == EL_BD_AMOEBA ||
14253          element == EL_AMOEBA_GROWING))
14254     {
14255       AmoebaCnt[AmoebaNr[x][y]]--;
14256       AmoebaCnt2[AmoebaNr[x][y]]--;
14257     }
14258
14259     if (IS_MOVING(x, y))
14260       RemoveMovingField(x, y);
14261     else
14262     {
14263       RemoveField(x, y);
14264       TEST_DrawLevelField(x, y);
14265     }
14266
14267     // if digged element was about to explode, prevent the explosion
14268     ExplodeField[x][y] = EX_TYPE_NONE;
14269
14270     PlayLevelSoundAction(x, y, action);
14271   }
14272
14273   Store[x][y] = EL_EMPTY;
14274
14275   // this makes it possible to leave the removed element again
14276   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14277     Store[x][y] = element;
14278
14279   return TRUE;
14280 }
14281
14282 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14283 {
14284   int jx = player->jx, jy = player->jy;
14285   int x = jx + dx, y = jy + dy;
14286   int snap_direction = (dx == -1 ? MV_LEFT  :
14287                         dx == +1 ? MV_RIGHT :
14288                         dy == -1 ? MV_UP    :
14289                         dy == +1 ? MV_DOWN  : MV_NONE);
14290   boolean can_continue_snapping = (level.continuous_snapping &&
14291                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14292
14293   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14294     return FALSE;
14295
14296   if (!player->active || !IN_LEV_FIELD(x, y))
14297     return FALSE;
14298
14299   if (dx && dy)
14300     return FALSE;
14301
14302   if (!dx && !dy)
14303   {
14304     if (player->MovPos == 0)
14305       player->is_pushing = FALSE;
14306
14307     player->is_snapping = FALSE;
14308
14309     if (player->MovPos == 0)
14310     {
14311       player->is_moving = FALSE;
14312       player->is_digging = FALSE;
14313       player->is_collecting = FALSE;
14314     }
14315
14316     return FALSE;
14317   }
14318
14319   // prevent snapping with already pressed snap key when not allowed
14320   if (player->is_snapping && !can_continue_snapping)
14321     return FALSE;
14322
14323   player->MovDir = snap_direction;
14324
14325   if (player->MovPos == 0)
14326   {
14327     player->is_moving = FALSE;
14328     player->is_digging = FALSE;
14329     player->is_collecting = FALSE;
14330   }
14331
14332   player->is_dropping = FALSE;
14333   player->is_dropping_pressed = FALSE;
14334   player->drop_pressed_delay = 0;
14335
14336   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14337     return FALSE;
14338
14339   player->is_snapping = TRUE;
14340   player->is_active = TRUE;
14341
14342   if (player->MovPos == 0)
14343   {
14344     player->is_moving = FALSE;
14345     player->is_digging = FALSE;
14346     player->is_collecting = FALSE;
14347   }
14348
14349   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14350     TEST_DrawLevelField(player->last_jx, player->last_jy);
14351
14352   TEST_DrawLevelField(x, y);
14353
14354   return TRUE;
14355 }
14356
14357 static boolean DropElement(struct PlayerInfo *player)
14358 {
14359   int old_element, new_element;
14360   int dropx = player->jx, dropy = player->jy;
14361   int drop_direction = player->MovDir;
14362   int drop_side = drop_direction;
14363   int drop_element = get_next_dropped_element(player);
14364
14365   /* do not drop an element on top of another element; when holding drop key
14366      pressed without moving, dropped element must move away before the next
14367      element can be dropped (this is especially important if the next element
14368      is dynamite, which can be placed on background for historical reasons) */
14369   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14370     return MP_ACTION;
14371
14372   if (IS_THROWABLE(drop_element))
14373   {
14374     dropx += GET_DX_FROM_DIR(drop_direction);
14375     dropy += GET_DY_FROM_DIR(drop_direction);
14376
14377     if (!IN_LEV_FIELD(dropx, dropy))
14378       return FALSE;
14379   }
14380
14381   old_element = Feld[dropx][dropy];     // old element at dropping position
14382   new_element = drop_element;           // default: no change when dropping
14383
14384   // check if player is active, not moving and ready to drop
14385   if (!player->active || player->MovPos || player->drop_delay > 0)
14386     return FALSE;
14387
14388   // check if player has anything that can be dropped
14389   if (new_element == EL_UNDEFINED)
14390     return FALSE;
14391
14392   // only set if player has anything that can be dropped
14393   player->is_dropping_pressed = TRUE;
14394
14395   // check if drop key was pressed long enough for EM style dynamite
14396   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14397     return FALSE;
14398
14399   // check if anything can be dropped at the current position
14400   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14401     return FALSE;
14402
14403   // collected custom elements can only be dropped on empty fields
14404   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14405     return FALSE;
14406
14407   if (old_element != EL_EMPTY)
14408     Back[dropx][dropy] = old_element;   // store old element on this field
14409
14410   ResetGfxAnimation(dropx, dropy);
14411   ResetRandomAnimationValue(dropx, dropy);
14412
14413   if (player->inventory_size > 0 ||
14414       player->inventory_infinite_element != EL_UNDEFINED)
14415   {
14416     if (player->inventory_size > 0)
14417     {
14418       player->inventory_size--;
14419
14420       DrawGameDoorValues();
14421
14422       if (new_element == EL_DYNAMITE)
14423         new_element = EL_DYNAMITE_ACTIVE;
14424       else if (new_element == EL_EM_DYNAMITE)
14425         new_element = EL_EM_DYNAMITE_ACTIVE;
14426       else if (new_element == EL_SP_DISK_RED)
14427         new_element = EL_SP_DISK_RED_ACTIVE;
14428     }
14429
14430     Feld[dropx][dropy] = new_element;
14431
14432     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14433       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14434                           el2img(Feld[dropx][dropy]), 0);
14435
14436     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14437
14438     // needed if previous element just changed to "empty" in the last frame
14439     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14440
14441     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14442                                player->index_bit, drop_side);
14443     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14444                                         CE_PLAYER_DROPS_X,
14445                                         player->index_bit, drop_side);
14446
14447     TestIfElementTouchesCustomElement(dropx, dropy);
14448   }
14449   else          // player is dropping a dyna bomb
14450   {
14451     player->dynabombs_left--;
14452
14453     Feld[dropx][dropy] = new_element;
14454
14455     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14456       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14457                           el2img(Feld[dropx][dropy]), 0);
14458
14459     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14460   }
14461
14462   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14463     InitField_WithBug1(dropx, dropy, FALSE);
14464
14465   new_element = Feld[dropx][dropy];     // element might have changed
14466
14467   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14468       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14469   {
14470     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14471       MovDir[dropx][dropy] = drop_direction;
14472
14473     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14474
14475     // do not cause impact style collision by dropping elements that can fall
14476     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14477   }
14478
14479   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14480   player->is_dropping = TRUE;
14481
14482   player->drop_pressed_delay = 0;
14483   player->is_dropping_pressed = FALSE;
14484
14485   player->drop_x = dropx;
14486   player->drop_y = dropy;
14487
14488   return TRUE;
14489 }
14490
14491 // ----------------------------------------------------------------------------
14492 // game sound playing functions
14493 // ----------------------------------------------------------------------------
14494
14495 static int *loop_sound_frame = NULL;
14496 static int *loop_sound_volume = NULL;
14497
14498 void InitPlayLevelSound(void)
14499 {
14500   int num_sounds = getSoundListSize();
14501
14502   checked_free(loop_sound_frame);
14503   checked_free(loop_sound_volume);
14504
14505   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14506   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14507 }
14508
14509 static void PlayLevelSound(int x, int y, int nr)
14510 {
14511   int sx = SCREENX(x), sy = SCREENY(y);
14512   int volume, stereo_position;
14513   int max_distance = 8;
14514   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14515
14516   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14517       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14518     return;
14519
14520   if (!IN_LEV_FIELD(x, y) ||
14521       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14522       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14523     return;
14524
14525   volume = SOUND_MAX_VOLUME;
14526
14527   if (!IN_SCR_FIELD(sx, sy))
14528   {
14529     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14530     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14531
14532     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14533   }
14534
14535   stereo_position = (SOUND_MAX_LEFT +
14536                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14537                      (SCR_FIELDX + 2 * max_distance));
14538
14539   if (IS_LOOP_SOUND(nr))
14540   {
14541     /* This assures that quieter loop sounds do not overwrite louder ones,
14542        while restarting sound volume comparison with each new game frame. */
14543
14544     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14545       return;
14546
14547     loop_sound_volume[nr] = volume;
14548     loop_sound_frame[nr] = FrameCounter;
14549   }
14550
14551   PlaySoundExt(nr, volume, stereo_position, type);
14552 }
14553
14554 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14555 {
14556   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14557                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14558                  y < LEVELY(BY1) ? LEVELY(BY1) :
14559                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14560                  sound_action);
14561 }
14562
14563 static void PlayLevelSoundAction(int x, int y, int action)
14564 {
14565   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14566 }
14567
14568 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14569 {
14570   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14571
14572   if (sound_effect != SND_UNDEFINED)
14573     PlayLevelSound(x, y, sound_effect);
14574 }
14575
14576 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14577                                               int action)
14578 {
14579   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14580
14581   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14582     PlayLevelSound(x, y, sound_effect);
14583 }
14584
14585 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14586 {
14587   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14588
14589   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14590     PlayLevelSound(x, y, sound_effect);
14591 }
14592
14593 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14594 {
14595   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14596
14597   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14598     StopSound(sound_effect);
14599 }
14600
14601 static int getLevelMusicNr(void)
14602 {
14603   if (levelset.music[level_nr] != MUS_UNDEFINED)
14604     return levelset.music[level_nr];            // from config file
14605   else
14606     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14607 }
14608
14609 static void FadeLevelSounds(void)
14610 {
14611   FadeSounds();
14612 }
14613
14614 static void FadeLevelMusic(void)
14615 {
14616   int music_nr = getLevelMusicNr();
14617   char *curr_music = getCurrentlyPlayingMusicFilename();
14618   char *next_music = getMusicInfoEntryFilename(music_nr);
14619
14620   if (!strEqual(curr_music, next_music))
14621     FadeMusic();
14622 }
14623
14624 void FadeLevelSoundsAndMusic(void)
14625 {
14626   FadeLevelSounds();
14627   FadeLevelMusic();
14628 }
14629
14630 static void PlayLevelMusic(void)
14631 {
14632   int music_nr = getLevelMusicNr();
14633   char *curr_music = getCurrentlyPlayingMusicFilename();
14634   char *next_music = getMusicInfoEntryFilename(music_nr);
14635
14636   if (!strEqual(curr_music, next_music))
14637     PlayMusicLoop(music_nr);
14638 }
14639
14640 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14641 {
14642   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14643   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14644   int x = xx - 1 - offset;
14645   int y = yy - 1 - offset;
14646
14647   switch (sample)
14648   {
14649     case SAMPLE_blank:
14650       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14651       break;
14652
14653     case SAMPLE_roll:
14654       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14655       break;
14656
14657     case SAMPLE_stone:
14658       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14659       break;
14660
14661     case SAMPLE_nut:
14662       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14663       break;
14664
14665     case SAMPLE_crack:
14666       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14667       break;
14668
14669     case SAMPLE_bug:
14670       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14671       break;
14672
14673     case SAMPLE_tank:
14674       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14675       break;
14676
14677     case SAMPLE_android_clone:
14678       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14679       break;
14680
14681     case SAMPLE_android_move:
14682       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14683       break;
14684
14685     case SAMPLE_spring:
14686       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14687       break;
14688
14689     case SAMPLE_slurp:
14690       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14691       break;
14692
14693     case SAMPLE_eater:
14694       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14695       break;
14696
14697     case SAMPLE_eater_eat:
14698       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14699       break;
14700
14701     case SAMPLE_alien:
14702       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14703       break;
14704
14705     case SAMPLE_collect:
14706       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14707       break;
14708
14709     case SAMPLE_diamond:
14710       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14711       break;
14712
14713     case SAMPLE_squash:
14714       // !!! CHECK THIS !!!
14715 #if 1
14716       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14717 #else
14718       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14719 #endif
14720       break;
14721
14722     case SAMPLE_wonderfall:
14723       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14724       break;
14725
14726     case SAMPLE_drip:
14727       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14728       break;
14729
14730     case SAMPLE_push:
14731       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14732       break;
14733
14734     case SAMPLE_dirt:
14735       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14736       break;
14737
14738     case SAMPLE_acid:
14739       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14740       break;
14741
14742     case SAMPLE_ball:
14743       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14744       break;
14745
14746     case SAMPLE_grow:
14747       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14748       break;
14749
14750     case SAMPLE_wonder:
14751       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14752       break;
14753
14754     case SAMPLE_door:
14755       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14756       break;
14757
14758     case SAMPLE_exit_open:
14759       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14760       break;
14761
14762     case SAMPLE_exit_leave:
14763       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14764       break;
14765
14766     case SAMPLE_dynamite:
14767       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14768       break;
14769
14770     case SAMPLE_tick:
14771       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14772       break;
14773
14774     case SAMPLE_press:
14775       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14776       break;
14777
14778     case SAMPLE_wheel:
14779       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14780       break;
14781
14782     case SAMPLE_boom:
14783       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14784       break;
14785
14786     case SAMPLE_die:
14787       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14788       break;
14789
14790     case SAMPLE_time:
14791       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14792       break;
14793
14794     default:
14795       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14796       break;
14797   }
14798 }
14799
14800 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14801 {
14802   int element = map_element_SP_to_RND(element_sp);
14803   int action = map_action_SP_to_RND(action_sp);
14804   int offset = (setup.sp_show_border_elements ? 0 : 1);
14805   int x = xx - offset;
14806   int y = yy - offset;
14807
14808   PlayLevelSoundElementAction(x, y, element, action);
14809 }
14810
14811 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14812 {
14813   int element = map_element_MM_to_RND(element_mm);
14814   int action = map_action_MM_to_RND(action_mm);
14815   int offset = 0;
14816   int x = xx - offset;
14817   int y = yy - offset;
14818
14819   if (!IS_MM_ELEMENT(element))
14820     element = EL_MM_DEFAULT;
14821
14822   PlayLevelSoundElementAction(x, y, element, action);
14823 }
14824
14825 void PlaySound_MM(int sound_mm)
14826 {
14827   int sound = map_sound_MM_to_RND(sound_mm);
14828
14829   if (sound == SND_UNDEFINED)
14830     return;
14831
14832   PlaySound(sound);
14833 }
14834
14835 void PlaySoundLoop_MM(int sound_mm)
14836 {
14837   int sound = map_sound_MM_to_RND(sound_mm);
14838
14839   if (sound == SND_UNDEFINED)
14840     return;
14841
14842   PlaySoundLoop(sound);
14843 }
14844
14845 void StopSound_MM(int sound_mm)
14846 {
14847   int sound = map_sound_MM_to_RND(sound_mm);
14848
14849   if (sound == SND_UNDEFINED)
14850     return;
14851
14852   StopSound(sound);
14853 }
14854
14855 void RaiseScore(int value)
14856 {
14857   game.score += value;
14858
14859   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
14860
14861   DisplayGameControlValues();
14862 }
14863
14864 void RaiseScoreElement(int element)
14865 {
14866   switch (element)
14867   {
14868     case EL_EMERALD:
14869     case EL_BD_DIAMOND:
14870     case EL_EMERALD_YELLOW:
14871     case EL_EMERALD_RED:
14872     case EL_EMERALD_PURPLE:
14873     case EL_SP_INFOTRON:
14874       RaiseScore(level.score[SC_EMERALD]);
14875       break;
14876     case EL_DIAMOND:
14877       RaiseScore(level.score[SC_DIAMOND]);
14878       break;
14879     case EL_CRYSTAL:
14880       RaiseScore(level.score[SC_CRYSTAL]);
14881       break;
14882     case EL_PEARL:
14883       RaiseScore(level.score[SC_PEARL]);
14884       break;
14885     case EL_BUG:
14886     case EL_BD_BUTTERFLY:
14887     case EL_SP_ELECTRON:
14888       RaiseScore(level.score[SC_BUG]);
14889       break;
14890     case EL_SPACESHIP:
14891     case EL_BD_FIREFLY:
14892     case EL_SP_SNIKSNAK:
14893       RaiseScore(level.score[SC_SPACESHIP]);
14894       break;
14895     case EL_YAMYAM:
14896     case EL_DARK_YAMYAM:
14897       RaiseScore(level.score[SC_YAMYAM]);
14898       break;
14899     case EL_ROBOT:
14900       RaiseScore(level.score[SC_ROBOT]);
14901       break;
14902     case EL_PACMAN:
14903       RaiseScore(level.score[SC_PACMAN]);
14904       break;
14905     case EL_NUT:
14906       RaiseScore(level.score[SC_NUT]);
14907       break;
14908     case EL_DYNAMITE:
14909     case EL_EM_DYNAMITE:
14910     case EL_SP_DISK_RED:
14911     case EL_DYNABOMB_INCREASE_NUMBER:
14912     case EL_DYNABOMB_INCREASE_SIZE:
14913     case EL_DYNABOMB_INCREASE_POWER:
14914       RaiseScore(level.score[SC_DYNAMITE]);
14915       break;
14916     case EL_SHIELD_NORMAL:
14917     case EL_SHIELD_DEADLY:
14918       RaiseScore(level.score[SC_SHIELD]);
14919       break;
14920     case EL_EXTRA_TIME:
14921       RaiseScore(level.extra_time_score);
14922       break;
14923     case EL_KEY_1:
14924     case EL_KEY_2:
14925     case EL_KEY_3:
14926     case EL_KEY_4:
14927     case EL_EM_KEY_1:
14928     case EL_EM_KEY_2:
14929     case EL_EM_KEY_3:
14930     case EL_EM_KEY_4:
14931     case EL_EMC_KEY_5:
14932     case EL_EMC_KEY_6:
14933     case EL_EMC_KEY_7:
14934     case EL_EMC_KEY_8:
14935     case EL_DC_KEY_WHITE:
14936       RaiseScore(level.score[SC_KEY]);
14937       break;
14938     default:
14939       RaiseScore(element_info[element].collect_score);
14940       break;
14941   }
14942 }
14943
14944 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14945 {
14946   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14947   {
14948     // closing door required in case of envelope style request dialogs
14949     if (!skip_request)
14950       CloseDoor(DOOR_CLOSE_1);
14951
14952     if (network.enabled)
14953       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14954     else
14955     {
14956       if (quick_quit)
14957         FadeSkipNextFadeIn();
14958
14959       SetGameStatus(GAME_MODE_MAIN);
14960
14961       DrawMainMenu();
14962     }
14963   }
14964   else          // continue playing the game
14965   {
14966     if (tape.playing && tape.deactivate_display)
14967       TapeDeactivateDisplayOff(TRUE);
14968
14969     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14970
14971     if (tape.playing && tape.deactivate_display)
14972       TapeDeactivateDisplayOn();
14973   }
14974 }
14975
14976 void RequestQuitGame(boolean ask_if_really_quit)
14977 {
14978   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14979   boolean skip_request = game.all_players_gone || quick_quit;
14980
14981   RequestQuitGameExt(skip_request, quick_quit,
14982                      "Do you really want to quit the game?");
14983 }
14984
14985 void RequestRestartGame(char *message)
14986 {
14987   game.restart_game_message = NULL;
14988
14989   boolean has_started_game = hasStartedNetworkGame();
14990   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
14991
14992   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
14993   {
14994     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14995   }
14996   else
14997   {
14998     SetGameStatus(GAME_MODE_MAIN);
14999
15000     DrawMainMenu();
15001   }
15002 }
15003
15004 void CheckGameOver(void)
15005 {
15006   static boolean last_game_over = FALSE;
15007   static int game_over_delay = 0;
15008   int game_over_delay_value = 50;
15009   boolean game_over = checkGameFailed();
15010
15011   // do not handle game over if request dialog is already active
15012   if (game.request_active)
15013     return;
15014
15015   if (!game_over)
15016   {
15017     last_game_over = FALSE;
15018     game_over_delay = game_over_delay_value;
15019
15020     return;
15021   }
15022
15023   if (game_over_delay > 0)
15024   {
15025     game_over_delay--;
15026
15027     return;
15028   }
15029
15030   if (last_game_over != game_over)
15031     game.restart_game_message = (hasStartedNetworkGame() ?
15032                                  "Game over! Play it again?" :
15033                                  "Game over!");
15034
15035   last_game_over = game_over;
15036 }
15037
15038 boolean checkGameSolved(void)
15039 {
15040   // set for all game engines if level was solved
15041   return game.LevelSolved_GameEnd;
15042 }
15043
15044 boolean checkGameFailed(void)
15045 {
15046   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15047     return (game_em.game_over && !game_em.level_solved);
15048   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15049     return (game_sp.game_over && !game_sp.level_solved);
15050   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15051     return (game_mm.game_over && !game_mm.level_solved);
15052   else                          // GAME_ENGINE_TYPE_RND
15053     return (game.GameOver && !game.LevelSolved);
15054 }
15055
15056 boolean checkGameEnded(void)
15057 {
15058   return (checkGameSolved() || checkGameFailed());
15059 }
15060
15061
15062 // ----------------------------------------------------------------------------
15063 // random generator functions
15064 // ----------------------------------------------------------------------------
15065
15066 unsigned int InitEngineRandom_RND(int seed)
15067 {
15068   game.num_random_calls = 0;
15069
15070   return InitEngineRandom(seed);
15071 }
15072
15073 unsigned int RND(int max)
15074 {
15075   if (max > 0)
15076   {
15077     game.num_random_calls++;
15078
15079     return GetEngineRandom(max);
15080   }
15081
15082   return 0;
15083 }
15084
15085
15086 // ----------------------------------------------------------------------------
15087 // game engine snapshot handling functions
15088 // ----------------------------------------------------------------------------
15089
15090 struct EngineSnapshotInfo
15091 {
15092   // runtime values for custom element collect score
15093   int collect_score[NUM_CUSTOM_ELEMENTS];
15094
15095   // runtime values for group element choice position
15096   int choice_pos[NUM_GROUP_ELEMENTS];
15097
15098   // runtime values for belt position animations
15099   int belt_graphic[4][NUM_BELT_PARTS];
15100   int belt_anim_mode[4][NUM_BELT_PARTS];
15101 };
15102
15103 static struct EngineSnapshotInfo engine_snapshot_rnd;
15104 static char *snapshot_level_identifier = NULL;
15105 static int snapshot_level_nr = -1;
15106
15107 static void SaveEngineSnapshotValues_RND(void)
15108 {
15109   static int belt_base_active_element[4] =
15110   {
15111     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15112     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15113     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15114     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15115   };
15116   int i, j;
15117
15118   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15119   {
15120     int element = EL_CUSTOM_START + i;
15121
15122     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15123   }
15124
15125   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15126   {
15127     int element = EL_GROUP_START + i;
15128
15129     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15130   }
15131
15132   for (i = 0; i < 4; i++)
15133   {
15134     for (j = 0; j < NUM_BELT_PARTS; j++)
15135     {
15136       int element = belt_base_active_element[i] + j;
15137       int graphic = el2img(element);
15138       int anim_mode = graphic_info[graphic].anim_mode;
15139
15140       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15141       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15142     }
15143   }
15144 }
15145
15146 static void LoadEngineSnapshotValues_RND(void)
15147 {
15148   unsigned int num_random_calls = game.num_random_calls;
15149   int i, j;
15150
15151   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15152   {
15153     int element = EL_CUSTOM_START + i;
15154
15155     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15156   }
15157
15158   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15159   {
15160     int element = EL_GROUP_START + i;
15161
15162     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15163   }
15164
15165   for (i = 0; i < 4; i++)
15166   {
15167     for (j = 0; j < NUM_BELT_PARTS; j++)
15168     {
15169       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15170       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15171
15172       graphic_info[graphic].anim_mode = anim_mode;
15173     }
15174   }
15175
15176   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15177   {
15178     InitRND(tape.random_seed);
15179     for (i = 0; i < num_random_calls; i++)
15180       RND(1);
15181   }
15182
15183   if (game.num_random_calls != num_random_calls)
15184   {
15185     Error(ERR_INFO, "number of random calls out of sync");
15186     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15187     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15188     Error(ERR_EXIT, "this should not happen -- please debug");
15189   }
15190 }
15191
15192 void FreeEngineSnapshotSingle(void)
15193 {
15194   FreeSnapshotSingle();
15195
15196   setString(&snapshot_level_identifier, NULL);
15197   snapshot_level_nr = -1;
15198 }
15199
15200 void FreeEngineSnapshotList(void)
15201 {
15202   FreeSnapshotList();
15203 }
15204
15205 static ListNode *SaveEngineSnapshotBuffers(void)
15206 {
15207   ListNode *buffers = NULL;
15208
15209   // copy some special values to a structure better suited for the snapshot
15210
15211   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15212     SaveEngineSnapshotValues_RND();
15213   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15214     SaveEngineSnapshotValues_EM();
15215   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15216     SaveEngineSnapshotValues_SP(&buffers);
15217   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15218     SaveEngineSnapshotValues_MM(&buffers);
15219
15220   // save values stored in special snapshot structure
15221
15222   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15223     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15224   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15225     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15226   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15227     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15228   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15229     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15230
15231   // save further RND engine values
15232
15233   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15234   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15235   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15236
15237   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15238   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15239   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15240   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15241
15242   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15243   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15244   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15245   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15246   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15247
15248   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15249   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15250   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15251
15252   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15253
15254   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15255   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15256
15257   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15258   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15259   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15260   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15261   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15262   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15263   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15264   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15265   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15266   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15267   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15268   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15269   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15270   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15271   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15272   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15273   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15274   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15275
15276   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15277   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15278
15279   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15280   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15281   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15282
15283   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15284   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15285
15286   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15287   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15288   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15289   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15290   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15291
15292   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15293   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15294
15295 #if 0
15296   ListNode *node = engine_snapshot_list_rnd;
15297   int num_bytes = 0;
15298
15299   while (node != NULL)
15300   {
15301     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15302
15303     node = node->next;
15304   }
15305
15306   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15307 #endif
15308
15309   return buffers;
15310 }
15311
15312 void SaveEngineSnapshotSingle(void)
15313 {
15314   ListNode *buffers = SaveEngineSnapshotBuffers();
15315
15316   // finally save all snapshot buffers to single snapshot
15317   SaveSnapshotSingle(buffers);
15318
15319   // save level identification information
15320   setString(&snapshot_level_identifier, leveldir_current->identifier);
15321   snapshot_level_nr = level_nr;
15322 }
15323
15324 boolean CheckSaveEngineSnapshotToList(void)
15325 {
15326   boolean save_snapshot =
15327     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15328      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15329       game.snapshot.changed_action) ||
15330      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15331       game.snapshot.collected_item));
15332
15333   game.snapshot.changed_action = FALSE;
15334   game.snapshot.collected_item = FALSE;
15335   game.snapshot.save_snapshot = save_snapshot;
15336
15337   return save_snapshot;
15338 }
15339
15340 void SaveEngineSnapshotToList(void)
15341 {
15342   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15343       tape.quick_resume)
15344     return;
15345
15346   ListNode *buffers = SaveEngineSnapshotBuffers();
15347
15348   // finally save all snapshot buffers to snapshot list
15349   SaveSnapshotToList(buffers);
15350 }
15351
15352 void SaveEngineSnapshotToListInitial(void)
15353 {
15354   FreeEngineSnapshotList();
15355
15356   SaveEngineSnapshotToList();
15357 }
15358
15359 static void LoadEngineSnapshotValues(void)
15360 {
15361   // restore special values from snapshot structure
15362
15363   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15364     LoadEngineSnapshotValues_RND();
15365   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15366     LoadEngineSnapshotValues_EM();
15367   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15368     LoadEngineSnapshotValues_SP();
15369   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15370     LoadEngineSnapshotValues_MM();
15371 }
15372
15373 void LoadEngineSnapshotSingle(void)
15374 {
15375   LoadSnapshotSingle();
15376
15377   LoadEngineSnapshotValues();
15378 }
15379
15380 static void LoadEngineSnapshot_Undo(int steps)
15381 {
15382   LoadSnapshotFromList_Older(steps);
15383
15384   LoadEngineSnapshotValues();
15385 }
15386
15387 static void LoadEngineSnapshot_Redo(int steps)
15388 {
15389   LoadSnapshotFromList_Newer(steps);
15390
15391   LoadEngineSnapshotValues();
15392 }
15393
15394 boolean CheckEngineSnapshotSingle(void)
15395 {
15396   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15397           snapshot_level_nr == level_nr);
15398 }
15399
15400 boolean CheckEngineSnapshotList(void)
15401 {
15402   return CheckSnapshotList();
15403 }
15404
15405
15406 // ---------- new game button stuff -------------------------------------------
15407
15408 static struct
15409 {
15410   int graphic;
15411   struct XY *pos;
15412   int gadget_id;
15413   boolean *setup_value;
15414   boolean allowed_on_tape;
15415   char *infotext;
15416 } gamebutton_info[NUM_GAME_BUTTONS] =
15417 {
15418   {
15419     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15420     GAME_CTRL_ID_STOP,                          NULL,
15421     TRUE,                                       "stop game"
15422   },
15423   {
15424     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15425     GAME_CTRL_ID_PAUSE,                         NULL,
15426     TRUE,                                       "pause game"
15427   },
15428   {
15429     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15430     GAME_CTRL_ID_PLAY,                          NULL,
15431     TRUE,                                       "play game"
15432   },
15433   {
15434     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15435     GAME_CTRL_ID_UNDO,                          NULL,
15436     TRUE,                                       "undo step"
15437   },
15438   {
15439     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15440     GAME_CTRL_ID_REDO,                          NULL,
15441     TRUE,                                       "redo step"
15442   },
15443   {
15444     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15445     GAME_CTRL_ID_SAVE,                          NULL,
15446     TRUE,                                       "save game"
15447   },
15448   {
15449     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15450     GAME_CTRL_ID_PAUSE2,                        NULL,
15451     TRUE,                                       "pause game"
15452   },
15453   {
15454     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15455     GAME_CTRL_ID_LOAD,                          NULL,
15456     TRUE,                                       "load game"
15457   },
15458   {
15459     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15460     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15461     FALSE,                                      "stop game"
15462   },
15463   {
15464     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15465     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15466     FALSE,                                      "pause game"
15467   },
15468   {
15469     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15470     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15471     FALSE,                                      "play game"
15472   },
15473   {
15474     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15475     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15476     TRUE,                                       "background music on/off"
15477   },
15478   {
15479     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15480     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15481     TRUE,                                       "sound loops on/off"
15482   },
15483   {
15484     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15485     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15486     TRUE,                                       "normal sounds on/off"
15487   },
15488   {
15489     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15490     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15491     FALSE,                                      "background music on/off"
15492   },
15493   {
15494     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15495     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15496     FALSE,                                      "sound loops on/off"
15497   },
15498   {
15499     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15500     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15501     FALSE,                                      "normal sounds on/off"
15502   }
15503 };
15504
15505 void CreateGameButtons(void)
15506 {
15507   int i;
15508
15509   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15510   {
15511     int graphic = gamebutton_info[i].graphic;
15512     struct GraphicInfo *gfx = &graphic_info[graphic];
15513     struct XY *pos = gamebutton_info[i].pos;
15514     struct GadgetInfo *gi;
15515     int button_type;
15516     boolean checked;
15517     unsigned int event_mask;
15518     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15519     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15520     int base_x = (on_tape ? VX : DX);
15521     int base_y = (on_tape ? VY : DY);
15522     int gd_x   = gfx->src_x;
15523     int gd_y   = gfx->src_y;
15524     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15525     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15526     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15527     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15528     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15529     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15530     int id = i;
15531
15532     if (gfx->bitmap == NULL)
15533     {
15534       game_gadget[id] = NULL;
15535
15536       continue;
15537     }
15538
15539     if (id == GAME_CTRL_ID_STOP ||
15540         id == GAME_CTRL_ID_PANEL_STOP ||
15541         id == GAME_CTRL_ID_PLAY ||
15542         id == GAME_CTRL_ID_PANEL_PLAY ||
15543         id == GAME_CTRL_ID_SAVE ||
15544         id == GAME_CTRL_ID_LOAD)
15545     {
15546       button_type = GD_TYPE_NORMAL_BUTTON;
15547       checked = FALSE;
15548       event_mask = GD_EVENT_RELEASED;
15549     }
15550     else if (id == GAME_CTRL_ID_UNDO ||
15551              id == GAME_CTRL_ID_REDO)
15552     {
15553       button_type = GD_TYPE_NORMAL_BUTTON;
15554       checked = FALSE;
15555       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15556     }
15557     else
15558     {
15559       button_type = GD_TYPE_CHECK_BUTTON;
15560       checked = (gamebutton_info[i].setup_value != NULL ?
15561                  *gamebutton_info[i].setup_value : FALSE);
15562       event_mask = GD_EVENT_PRESSED;
15563     }
15564
15565     gi = CreateGadget(GDI_CUSTOM_ID, id,
15566                       GDI_IMAGE_ID, graphic,
15567                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15568                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15569                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15570                       GDI_WIDTH, gfx->width,
15571                       GDI_HEIGHT, gfx->height,
15572                       GDI_TYPE, button_type,
15573                       GDI_STATE, GD_BUTTON_UNPRESSED,
15574                       GDI_CHECKED, checked,
15575                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15576                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15577                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15578                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15579                       GDI_DIRECT_DRAW, FALSE,
15580                       GDI_EVENT_MASK, event_mask,
15581                       GDI_CALLBACK_ACTION, HandleGameButtons,
15582                       GDI_END);
15583
15584     if (gi == NULL)
15585       Error(ERR_EXIT, "cannot create gadget");
15586
15587     game_gadget[id] = gi;
15588   }
15589 }
15590
15591 void FreeGameButtons(void)
15592 {
15593   int i;
15594
15595   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15596     FreeGadget(game_gadget[i]);
15597 }
15598
15599 static void UnmapGameButtonsAtSamePosition(int id)
15600 {
15601   int i;
15602
15603   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15604     if (i != id &&
15605         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15606         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15607       UnmapGadget(game_gadget[i]);
15608 }
15609
15610 static void UnmapGameButtonsAtSamePosition_All(void)
15611 {
15612   if (setup.show_snapshot_buttons)
15613   {
15614     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15615     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15616     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15617   }
15618   else
15619   {
15620     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15621     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15622     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15623
15624     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15625     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15626     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15627   }
15628 }
15629
15630 static void MapGameButtonsAtSamePosition(int id)
15631 {
15632   int i;
15633
15634   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15635     if (i != id &&
15636         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15637         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15638       MapGadget(game_gadget[i]);
15639
15640   UnmapGameButtonsAtSamePosition_All();
15641 }
15642
15643 void MapUndoRedoButtons(void)
15644 {
15645   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15646   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15647
15648   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15649   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15650
15651   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15652 }
15653
15654 void UnmapUndoRedoButtons(void)
15655 {
15656   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15657   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15658
15659   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15660   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15661
15662   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15663 }
15664
15665 static void MapGameButtonsExt(boolean on_tape)
15666 {
15667   int i;
15668
15669   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15670     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15671         i != GAME_CTRL_ID_UNDO &&
15672         i != GAME_CTRL_ID_REDO)
15673       MapGadget(game_gadget[i]);
15674
15675   UnmapGameButtonsAtSamePosition_All();
15676
15677   RedrawGameButtons();
15678 }
15679
15680 static void UnmapGameButtonsExt(boolean on_tape)
15681 {
15682   int i;
15683
15684   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15685     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15686       UnmapGadget(game_gadget[i]);
15687 }
15688
15689 static void RedrawGameButtonsExt(boolean on_tape)
15690 {
15691   int i;
15692
15693   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15694     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15695       RedrawGadget(game_gadget[i]);
15696
15697   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15698   redraw_mask &= ~REDRAW_ALL;
15699 }
15700
15701 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15702 {
15703   if (gi == NULL)
15704     return;
15705
15706   gi->checked = state;
15707 }
15708
15709 static void RedrawSoundButtonGadget(int id)
15710 {
15711   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15712              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15713              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15714              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15715              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15716              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15717              id);
15718
15719   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15720   RedrawGadget(game_gadget[id2]);
15721 }
15722
15723 void MapGameButtons(void)
15724 {
15725   MapGameButtonsExt(FALSE);
15726 }
15727
15728 void UnmapGameButtons(void)
15729 {
15730   UnmapGameButtonsExt(FALSE);
15731 }
15732
15733 void RedrawGameButtons(void)
15734 {
15735   RedrawGameButtonsExt(FALSE);
15736 }
15737
15738 void MapGameButtonsOnTape(void)
15739 {
15740   MapGameButtonsExt(TRUE);
15741 }
15742
15743 void UnmapGameButtonsOnTape(void)
15744 {
15745   UnmapGameButtonsExt(TRUE);
15746 }
15747
15748 void RedrawGameButtonsOnTape(void)
15749 {
15750   RedrawGameButtonsExt(TRUE);
15751 }
15752
15753 static void GameUndoRedoExt(void)
15754 {
15755   ClearPlayerAction();
15756
15757   tape.pausing = TRUE;
15758
15759   RedrawPlayfield();
15760   UpdateAndDisplayGameControlValues();
15761
15762   DrawCompleteVideoDisplay();
15763   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15764   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15765   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15766
15767   BackToFront();
15768 }
15769
15770 static void GameUndo(int steps)
15771 {
15772   if (!CheckEngineSnapshotList())
15773     return;
15774
15775   LoadEngineSnapshot_Undo(steps);
15776
15777   GameUndoRedoExt();
15778 }
15779
15780 static void GameRedo(int steps)
15781 {
15782   if (!CheckEngineSnapshotList())
15783     return;
15784
15785   LoadEngineSnapshot_Redo(steps);
15786
15787   GameUndoRedoExt();
15788 }
15789
15790 static void HandleGameButtonsExt(int id, int button)
15791 {
15792   static boolean game_undo_executed = FALSE;
15793   int steps = BUTTON_STEPSIZE(button);
15794   boolean handle_game_buttons =
15795     (game_status == GAME_MODE_PLAYING ||
15796      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15797
15798   if (!handle_game_buttons)
15799     return;
15800
15801   switch (id)
15802   {
15803     case GAME_CTRL_ID_STOP:
15804     case GAME_CTRL_ID_PANEL_STOP:
15805       if (game_status == GAME_MODE_MAIN)
15806         break;
15807
15808       if (tape.playing)
15809         TapeStop();
15810       else
15811         RequestQuitGame(TRUE);
15812
15813       break;
15814
15815     case GAME_CTRL_ID_PAUSE:
15816     case GAME_CTRL_ID_PAUSE2:
15817     case GAME_CTRL_ID_PANEL_PAUSE:
15818       if (network.enabled && game_status == GAME_MODE_PLAYING)
15819       {
15820         if (tape.pausing)
15821           SendToServer_ContinuePlaying();
15822         else
15823           SendToServer_PausePlaying();
15824       }
15825       else
15826         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15827
15828       game_undo_executed = FALSE;
15829
15830       break;
15831
15832     case GAME_CTRL_ID_PLAY:
15833     case GAME_CTRL_ID_PANEL_PLAY:
15834       if (game_status == GAME_MODE_MAIN)
15835       {
15836         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15837       }
15838       else if (tape.pausing)
15839       {
15840         if (network.enabled)
15841           SendToServer_ContinuePlaying();
15842         else
15843           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15844       }
15845       break;
15846
15847     case GAME_CTRL_ID_UNDO:
15848       // Important: When using "save snapshot when collecting an item" mode,
15849       // load last (current) snapshot for first "undo" after pressing "pause"
15850       // (else the last-but-one snapshot would be loaded, because the snapshot
15851       // pointer already points to the last snapshot when pressing "pause",
15852       // which is fine for "every step/move" mode, but not for "every collect")
15853       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15854           !game_undo_executed)
15855         steps--;
15856
15857       game_undo_executed = TRUE;
15858
15859       GameUndo(steps);
15860       break;
15861
15862     case GAME_CTRL_ID_REDO:
15863       GameRedo(steps);
15864       break;
15865
15866     case GAME_CTRL_ID_SAVE:
15867       TapeQuickSave();
15868       break;
15869
15870     case GAME_CTRL_ID_LOAD:
15871       TapeQuickLoad();
15872       break;
15873
15874     case SOUND_CTRL_ID_MUSIC:
15875     case SOUND_CTRL_ID_PANEL_MUSIC:
15876       if (setup.sound_music)
15877       { 
15878         setup.sound_music = FALSE;
15879
15880         FadeMusic();
15881       }
15882       else if (audio.music_available)
15883       { 
15884         setup.sound = setup.sound_music = TRUE;
15885
15886         SetAudioMode(setup.sound);
15887
15888         if (game_status == GAME_MODE_PLAYING)
15889           PlayLevelMusic();
15890       }
15891
15892       RedrawSoundButtonGadget(id);
15893
15894       break;
15895
15896     case SOUND_CTRL_ID_LOOPS:
15897     case SOUND_CTRL_ID_PANEL_LOOPS:
15898       if (setup.sound_loops)
15899         setup.sound_loops = FALSE;
15900       else if (audio.loops_available)
15901       {
15902         setup.sound = setup.sound_loops = TRUE;
15903
15904         SetAudioMode(setup.sound);
15905       }
15906
15907       RedrawSoundButtonGadget(id);
15908
15909       break;
15910
15911     case SOUND_CTRL_ID_SIMPLE:
15912     case SOUND_CTRL_ID_PANEL_SIMPLE:
15913       if (setup.sound_simple)
15914         setup.sound_simple = FALSE;
15915       else if (audio.sound_available)
15916       {
15917         setup.sound = setup.sound_simple = TRUE;
15918
15919         SetAudioMode(setup.sound);
15920       }
15921
15922       RedrawSoundButtonGadget(id);
15923
15924       break;
15925
15926     default:
15927       break;
15928   }
15929 }
15930
15931 static void HandleGameButtons(struct GadgetInfo *gi)
15932 {
15933   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15934 }
15935
15936 void HandleSoundButtonKeys(Key key)
15937 {
15938   if (key == setup.shortcut.sound_simple)
15939     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15940   else if (key == setup.shortcut.sound_loops)
15941     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15942   else if (key == setup.shortcut.sound_music)
15943     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15944 }