f4ae9303776da68ac2be4439c6b5ff651be2fe59
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebeNachbarNr(int, int);
1096 void AmoebeUmwandeln(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Feld[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Feld[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Feld[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Feld[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       if (options.debug)
1773       {
1774         printf("- player element %d activated", player->element_nr);
1775         printf(" (local player is %d and currently %s)\n",
1776                local_player->element_nr,
1777                local_player->active ? "active" : "not active");
1778       }
1779     }
1780 #endif
1781
1782     Feld[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788   if (!init_game)
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active && player->killed)
1794       player->reanimated = TRUE; // if player was just killed, reanimate him
1795   }
1796 }
1797
1798 static void InitField(int x, int y, boolean init_game)
1799 {
1800   int element = Feld[x][y];
1801
1802   switch (element)
1803   {
1804     case EL_SP_MURPHY:
1805     case EL_PLAYER_1:
1806     case EL_PLAYER_2:
1807     case EL_PLAYER_3:
1808     case EL_PLAYER_4:
1809       InitPlayerField(x, y, element, init_game);
1810       break;
1811
1812     case EL_SOKOBAN_FIELD_PLAYER:
1813       element = Feld[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817       InitField(x, y, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_EMPTY:
1821       IncrementSokobanFieldsNeeded();
1822       break;
1823
1824     case EL_SOKOBAN_OBJECT:
1825       IncrementSokobanObjectsNeeded();
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Feld[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_active)
1967         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_active)
1972         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Feld[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Feld[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Feld[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Feld[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return game_em.ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return game_sp.red_disk_count;
2132   else
2133     return stored_player[player_nr].inventory_size;
2134 }
2135
2136 static void InitGameControlValues(void)
2137 {
2138   int i;
2139
2140   for (i = 0; game_panel_controls[i].nr != -1; i++)
2141   {
2142     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144     struct TextPosInfo *pos = gpc->pos;
2145     int nr = gpc->nr;
2146     int type = gpc->type;
2147
2148     if (nr != i)
2149     {
2150       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2151       Error(ERR_EXIT, "this should not happen -- please debug");
2152     }
2153
2154     // force update of game controls after initialization
2155     gpc->value = gpc->last_value = -1;
2156     gpc->frame = gpc->last_frame = -1;
2157     gpc->gfx_frame = -1;
2158
2159     // determine panel value width for later calculation of alignment
2160     if (type == TYPE_INTEGER || type == TYPE_STRING)
2161     {
2162       pos->width = pos->size * getFontWidth(pos->font);
2163       pos->height = getFontHeight(pos->font);
2164     }
2165     else if (type == TYPE_ELEMENT)
2166     {
2167       pos->width = pos->size;
2168       pos->height = pos->size;
2169     }
2170
2171     // fill structure for game panel draw order
2172     gpo->nr = gpc->nr;
2173     gpo->sort_priority = pos->sort_priority;
2174   }
2175
2176   // sort game panel controls according to sort_priority and control number
2177   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2178         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2179 }
2180
2181 static void UpdatePlayfieldElementCount(void)
2182 {
2183   boolean use_element_count = FALSE;
2184   int i, j, x, y;
2185
2186   // first check if it is needed at all to calculate playfield element count
2187   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2188     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2189       use_element_count = TRUE;
2190
2191   if (!use_element_count)
2192     return;
2193
2194   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2195     element_info[i].element_count = 0;
2196
2197   SCAN_PLAYFIELD(x, y)
2198   {
2199     element_info[Feld[x][y]].element_count++;
2200   }
2201
2202   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2203     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2204       if (IS_IN_GROUP(j, i))
2205         element_info[EL_GROUP_START + i].element_count +=
2206           element_info[j].element_count;
2207 }
2208
2209 static void UpdateGameControlValues(void)
2210 {
2211   int i, k;
2212   int time = (game.LevelSolved ?
2213               game.LevelSolved_CountingTime :
2214               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2215               game_em.lev->time :
2216               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2217               game_sp.time_played :
2218               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219               game_mm.energy_left :
2220               game.no_time_limit ? TimePlayed : TimeLeft);
2221   int score = (game.LevelSolved ?
2222                game.LevelSolved_CountingScore :
2223                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2224                game_em.lev->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2226                game_sp.score :
2227                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2228                game_mm.score :
2229                game.score);
2230   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231               game_em.lev->gems_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2233               game_sp.infotrons_still_needed :
2234               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2235               game_mm.kettles_still_needed :
2236               game.gems_still_needed);
2237   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238                      game_em.lev->gems_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240                      game_sp.infotrons_still_needed > 0 :
2241                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2242                      game_mm.kettles_still_needed > 0 ||
2243                      game_mm.lights_still_needed > 0 :
2244                      game.gems_still_needed > 0 ||
2245                      game.sokoban_fields_still_needed > 0 ||
2246                      game.sokoban_objects_still_needed > 0 ||
2247                      game.lights_still_needed > 0);
2248   int health = (game.LevelSolved ?
2249                 game.LevelSolved_CountingHealth :
2250                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2251                 MM_HEALTH(game_mm.laser_overload_value) :
2252                 game.health);
2253
2254   UpdatePlayfieldElementCount();
2255
2256   // update game panel control values
2257
2258   // used instead of "level_nr" (for network games)
2259   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2260   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2261
2262   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2263   for (i = 0; i < MAX_NUM_KEYS; i++)
2264     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2265   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2266   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2267
2268   if (game.centered_player_nr == -1)
2269   {
2270     for (i = 0; i < MAX_PLAYERS; i++)
2271     {
2272       // only one player in Supaplex game engine
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2274         break;
2275
2276       for (k = 0; k < MAX_NUM_KEYS; k++)
2277       {
2278         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2279         {
2280           if (game_em.ply[i]->keys & (1 << k))
2281             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282               get_key_element_from_nr(k);
2283         }
2284         else if (stored_player[i].key[k])
2285           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286             get_key_element_from_nr(k);
2287       }
2288
2289       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290         getPlayerInventorySize(i);
2291
2292       if (stored_player[i].num_white_keys > 0)
2293         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2294           EL_DC_KEY_WHITE;
2295
2296       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2297         stored_player[i].num_white_keys;
2298     }
2299   }
2300   else
2301   {
2302     int player_nr = game.centered_player_nr;
2303
2304     for (k = 0; k < MAX_NUM_KEYS; k++)
2305     {
2306       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2307       {
2308         if (game_em.ply[player_nr]->keys & (1 << k))
2309           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2310             get_key_element_from_nr(k);
2311       }
2312       else if (stored_player[player_nr].key[k])
2313         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314           get_key_element_from_nr(k);
2315     }
2316
2317     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2318       getPlayerInventorySize(player_nr);
2319
2320     if (stored_player[player_nr].num_white_keys > 0)
2321       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2322
2323     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2324       stored_player[player_nr].num_white_keys;
2325   }
2326
2327   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, i);
2331     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2332       get_inventory_element_from_pos(local_player, -i - 1);
2333   }
2334
2335   game_panel_controls[GAME_PANEL_SCORE].value = score;
2336   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2337
2338   game_panel_controls[GAME_PANEL_TIME].value = time;
2339
2340   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2341   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2342   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2343
2344   if (level.time == 0)
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2346   else
2347     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2348
2349   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2350   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2351
2352   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2353
2354   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2355     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2356      EL_EMPTY);
2357   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2358     local_player->shield_normal_time_left;
2359   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2360     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2361      EL_EMPTY);
2362   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2363     local_player->shield_deadly_time_left;
2364
2365   game_panel_controls[GAME_PANEL_EXIT].value =
2366     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2367
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2369     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2370   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2371     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2372      EL_EMC_MAGIC_BALL_SWITCH);
2373
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2375     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2376   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2377     game.light_time_left;
2378
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2380     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2381   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2382     game.timegate_time_left;
2383
2384   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2385     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2386
2387   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2388     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2389   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2390     game.lenses_time_left;
2391
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2393     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2394   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2395     game.magnify_time_left;
2396
2397   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2398     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2399      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2400      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2401      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2402      EL_BALLOON_SWITCH_NONE);
2403
2404   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2405     local_player->dynabomb_count;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2407     local_player->dynabomb_size;
2408   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2409     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2410
2411   game_panel_controls[GAME_PANEL_PENGUINS].value =
2412     game.friends_still_needed;
2413
2414   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2415     game.sokoban_objects_still_needed;
2416   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2417     game.sokoban_fields_still_needed;
2418
2419   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2420     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2421
2422   for (i = 0; i < NUM_BELTS; i++)
2423   {
2424     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2425       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2426        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2427     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2428       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2429   }
2430
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2432     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2433   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2434     game.magic_wall_time_left;
2435
2436   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2437     local_player->gravity;
2438
2439   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2440     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2441
2442   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2443     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2444       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2445        game.panel.element[i].id : EL_UNDEFINED);
2446
2447   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2448     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2449       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2450        element_info[game.panel.element_count[i].id].element_count : 0);
2451
2452   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2453     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2454       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2455        element_info[game.panel.ce_score[i].id].collect_score : 0);
2456
2457   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2458     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2459       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2460        element_info[game.panel.ce_score_element[i].id].collect_score :
2461        EL_UNDEFINED);
2462
2463   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2464   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2465   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2466
2467   // update game panel control frames
2468
2469   for (i = 0; game_panel_controls[i].nr != -1; i++)
2470   {
2471     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2472
2473     if (gpc->type == TYPE_ELEMENT)
2474     {
2475       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2476       {
2477         int last_anim_random_frame = gfx.anim_random_frame;
2478         int element = gpc->value;
2479         int graphic = el2panelimg(element);
2480
2481         if (gpc->value != gpc->last_value)
2482         {
2483           gpc->gfx_frame = 0;
2484           gpc->gfx_random = INIT_GFX_RANDOM();
2485         }
2486         else
2487         {
2488           gpc->gfx_frame++;
2489
2490           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2491               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2492             gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494
2495         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496           gfx.anim_random_frame = gpc->gfx_random;
2497
2498         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2499           gpc->gfx_frame = element_info[element].collect_score;
2500
2501         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2502                                               gpc->gfx_frame);
2503
2504         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2505           gfx.anim_random_frame = last_anim_random_frame;
2506       }
2507     }
2508     else if (gpc->type == TYPE_GRAPHIC)
2509     {
2510       if (gpc->graphic != IMG_UNDEFINED)
2511       {
2512         int last_anim_random_frame = gfx.anim_random_frame;
2513         int graphic = gpc->graphic;
2514
2515         if (gpc->value != gpc->last_value)
2516         {
2517           gpc->gfx_frame = 0;
2518           gpc->gfx_random = INIT_GFX_RANDOM();
2519         }
2520         else
2521         {
2522           gpc->gfx_frame++;
2523
2524           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2525               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2526             gpc->gfx_random = INIT_GFX_RANDOM();
2527         }
2528
2529         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2530           gfx.anim_random_frame = gpc->gfx_random;
2531
2532         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2533
2534         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2535           gfx.anim_random_frame = last_anim_random_frame;
2536       }
2537     }
2538   }
2539 }
2540
2541 static void DisplayGameControlValues(void)
2542 {
2543   boolean redraw_panel = FALSE;
2544   int i;
2545
2546   for (i = 0; game_panel_controls[i].nr != -1; i++)
2547   {
2548     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2549
2550     if (PANEL_DEACTIVATED(gpc->pos))
2551       continue;
2552
2553     if (gpc->value == gpc->last_value &&
2554         gpc->frame == gpc->last_frame)
2555       continue;
2556
2557     redraw_panel = TRUE;
2558   }
2559
2560   if (!redraw_panel)
2561     return;
2562
2563   // copy default game door content to main double buffer
2564
2565   // !!! CHECK AGAIN !!!
2566   SetPanelBackground();
2567   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2568   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2569
2570   // redraw game control buttons
2571   RedrawGameButtons();
2572
2573   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2574
2575   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2576   {
2577     int nr = game_panel_order[i].nr;
2578     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2579     struct TextPosInfo *pos = gpc->pos;
2580     int type = gpc->type;
2581     int value = gpc->value;
2582     int frame = gpc->frame;
2583     int size = pos->size;
2584     int font = pos->font;
2585     boolean draw_masked = pos->draw_masked;
2586     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2587
2588     if (PANEL_DEACTIVATED(pos))
2589       continue;
2590
2591     gpc->last_value = value;
2592     gpc->last_frame = frame;
2593
2594     if (type == TYPE_INTEGER)
2595     {
2596       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2597           nr == GAME_PANEL_TIME)
2598       {
2599         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2600
2601         if (use_dynamic_size)           // use dynamic number of digits
2602         {
2603           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2604           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2605           int size2 = size1 + 1;
2606           int font1 = pos->font;
2607           int font2 = pos->font_alt;
2608
2609           size = (value < value_change ? size1 : size2);
2610           font = (value < value_change ? font1 : font2);
2611         }
2612       }
2613
2614       // correct text size if "digits" is zero or less
2615       if (size <= 0)
2616         size = strlen(int2str(value, size));
2617
2618       // dynamically correct text alignment
2619       pos->width = size * getFontWidth(font);
2620
2621       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2622                   int2str(value, size), font, mask_mode);
2623     }
2624     else if (type == TYPE_ELEMENT)
2625     {
2626       int element, graphic;
2627       Bitmap *src_bitmap;
2628       int src_x, src_y;
2629       int width, height;
2630       int dst_x = PANEL_XPOS(pos);
2631       int dst_y = PANEL_YPOS(pos);
2632
2633       if (value != EL_UNDEFINED && value != EL_EMPTY)
2634       {
2635         element = value;
2636         graphic = el2panelimg(value);
2637
2638         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2639
2640         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2641           size = TILESIZE;
2642
2643         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2644                               &src_x, &src_y);
2645
2646         width  = graphic_info[graphic].width  * size / TILESIZE;
2647         height = graphic_info[graphic].height * size / TILESIZE;
2648
2649         if (draw_masked)
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         else
2653           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2654                      dst_x, dst_y);
2655       }
2656     }
2657     else if (type == TYPE_GRAPHIC)
2658     {
2659       int graphic        = gpc->graphic;
2660       int graphic_active = gpc->graphic_active;
2661       Bitmap *src_bitmap;
2662       int src_x, src_y;
2663       int width, height;
2664       int dst_x = PANEL_XPOS(pos);
2665       int dst_y = PANEL_YPOS(pos);
2666       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2667                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2668
2669       if (graphic != IMG_UNDEFINED && !skip)
2670       {
2671         if (pos->style == STYLE_REVERSE)
2672           value = 100 - value;
2673
2674         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2675
2676         if (pos->direction & MV_HORIZONTAL)
2677         {
2678           width  = graphic_info[graphic_active].width * value / 100;
2679           height = graphic_info[graphic_active].height;
2680
2681           if (pos->direction == MV_LEFT)
2682           {
2683             src_x += graphic_info[graphic_active].width - width;
2684             dst_x += graphic_info[graphic_active].width - width;
2685           }
2686         }
2687         else
2688         {
2689           width  = graphic_info[graphic_active].width;
2690           height = graphic_info[graphic_active].height * value / 100;
2691
2692           if (pos->direction == MV_UP)
2693           {
2694             src_y += graphic_info[graphic_active].height - height;
2695             dst_y += graphic_info[graphic_active].height - height;
2696           }
2697         }
2698
2699         if (draw_masked)
2700           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2701                            dst_x, dst_y);
2702         else
2703           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2704                      dst_x, dst_y);
2705
2706         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2707
2708         if (pos->direction & MV_HORIZONTAL)
2709         {
2710           if (pos->direction == MV_RIGHT)
2711           {
2712             src_x += width;
2713             dst_x += width;
2714           }
2715           else
2716           {
2717             dst_x = PANEL_XPOS(pos);
2718           }
2719
2720           width = graphic_info[graphic].width - width;
2721         }
2722         else
2723         {
2724           if (pos->direction == MV_DOWN)
2725           {
2726             src_y += height;
2727             dst_y += height;
2728           }
2729           else
2730           {
2731             dst_y = PANEL_YPOS(pos);
2732           }
2733
2734           height = graphic_info[graphic].height - height;
2735         }
2736
2737         if (draw_masked)
2738           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2739                            dst_x, dst_y);
2740         else
2741           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2742                      dst_x, dst_y);
2743       }
2744     }
2745     else if (type == TYPE_STRING)
2746     {
2747       boolean active = (value != 0);
2748       char *state_normal = "off";
2749       char *state_active = "on";
2750       char *state = (active ? state_active : state_normal);
2751       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2752                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2753                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2754                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2755
2756       if (nr == GAME_PANEL_GRAVITY_STATE)
2757       {
2758         int font1 = pos->font;          // (used for normal state)
2759         int font2 = pos->font_alt;      // (used for active state)
2760
2761         font = (active ? font2 : font1);
2762       }
2763
2764       if (s != NULL)
2765       {
2766         char *s_cut;
2767
2768         if (size <= 0)
2769         {
2770           // don't truncate output if "chars" is zero or less
2771           size = strlen(s);
2772
2773           // dynamically correct text alignment
2774           pos->width = size * getFontWidth(font);
2775         }
2776
2777         s_cut = getStringCopyN(s, size);
2778
2779         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2780                     s_cut, font, mask_mode);
2781
2782         free(s_cut);
2783       }
2784     }
2785
2786     redraw_mask |= REDRAW_DOOR_1;
2787   }
2788
2789   SetGameStatus(GAME_MODE_PLAYING);
2790 }
2791
2792 void UpdateAndDisplayGameControlValues(void)
2793 {
2794   if (tape.deactivate_display)
2795     return;
2796
2797   UpdateGameControlValues();
2798   DisplayGameControlValues();
2799 }
2800
2801 #if 0
2802 static void UpdateGameDoorValues(void)
2803 {
2804   UpdateGameControlValues();
2805 }
2806 #endif
2807
2808 void DrawGameDoorValues(void)
2809 {
2810   DisplayGameControlValues();
2811 }
2812
2813
2814 // ============================================================================
2815 // InitGameEngine()
2816 // ----------------------------------------------------------------------------
2817 // initialize game engine due to level / tape version number
2818 // ============================================================================
2819
2820 static void InitGameEngine(void)
2821 {
2822   int i, j, k, l, x, y;
2823
2824   // set game engine from tape file when re-playing, else from level file
2825   game.engine_version = (tape.playing ? tape.engine_version :
2826                          level.game_version);
2827
2828   // set single or multi-player game mode (needed for re-playing tapes)
2829   game.team_mode = setup.team_mode;
2830
2831   if (tape.playing)
2832   {
2833     int num_players = 0;
2834
2835     for (i = 0; i < MAX_PLAYERS; i++)
2836       if (tape.player_participates[i])
2837         num_players++;
2838
2839     // multi-player tapes contain input data for more than one player
2840     game.team_mode = (num_players > 1);
2841   }
2842
2843 #if 0
2844   printf("level %d: level.game_version  == %06d\n", level_nr,
2845          level.game_version);
2846   printf("          tape.file_version   == %06d\n",
2847          tape.file_version);
2848   printf("          tape.game_version   == %06d\n",
2849          tape.game_version);
2850   printf("          tape.engine_version == %06d\n",
2851          tape.engine_version);
2852   printf("       => game.engine_version == %06d [tape mode: %s]\n",
2853          game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2854 #endif
2855
2856   // --------------------------------------------------------------------------
2857   // set flags for bugs and changes according to active game engine version
2858   // --------------------------------------------------------------------------
2859
2860   /*
2861     Summary of bugfix:
2862     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2863
2864     Bug was introduced in version:
2865     2.0.1
2866
2867     Bug was fixed in version:
2868     4.1.4.2
2869
2870     Description:
2871     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2872     but the property "can fall" was missing, which caused some levels to be
2873     unsolvable. This was fixed in version 4.1.4.2.
2874
2875     Affected levels/tapes:
2876     An example for a tape that was fixed by this bugfix is tape 029 from the
2877     level set "rnd_sam_bateman".
2878     The wrong behaviour will still be used for all levels or tapes that were
2879     created/recorded with it. An example for this is tape 023 from the level
2880     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2881   */
2882
2883   boolean use_amoeba_dropping_cannot_fall_bug =
2884     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2885       game.engine_version <= VERSION_IDENT(4,1,4,1)) ||
2886      (tape.playing &&
2887       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2888       tape.game_version <= VERSION_IDENT(4,1,4,1)));
2889
2890   /*
2891     Summary of bugfix/change:
2892     Fixed move speed of elements entering or leaving magic wall.
2893
2894     Fixed/changed in version:
2895     2.0.1
2896
2897     Description:
2898     Before 2.0.1, move speed of elements entering or leaving magic wall was
2899     twice as fast as it is now.
2900     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2901
2902     Affected levels/tapes:
2903     The first condition is generally needed for all levels/tapes before version
2904     2.0.1, which might use the old behaviour before it was changed; known tapes
2905     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2906     The second condition is an exception from the above case and is needed for
2907     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2908     above, but before it was known that this change would break tapes like the
2909     above and was fixed in 4.1.4.2, so that the changed behaviour was active
2910     although the engine version while recording maybe was before 2.0.1. There
2911     are a lot of tapes that are affected by this exception, like tape 006 from
2912     the level set "rnd_conor_mancone".
2913   */
2914
2915   boolean use_old_move_stepsize_for_magic_wall =
2916     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2917      !(tape.playing &&
2918        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2919        tape.game_version <  VERSION_IDENT(4,1,4,2)));
2920
2921   /*
2922     Summary of bugfix/change:
2923     Fixed handling for custom elements that change when pushed by the player.
2924
2925     Fixed/changed in version:
2926     3.1.0
2927
2928     Description:
2929     Before 3.1.0, custom elements that "change when pushing" changed directly
2930     after the player started pushing them (until then handled in "DigField()").
2931     Since 3.1.0, these custom elements are not changed until the "pushing"
2932     move of the element is finished (now handled in "ContinueMoving()").
2933
2934     Affected levels/tapes:
2935     The first condition is generally needed for all levels/tapes before version
2936     3.1.0, which might use the old behaviour before it was changed; known tapes
2937     that are affected are some tapes from the level set "Walpurgis Gardens" by
2938     Jamie Cullen.
2939     The second condition is an exception from the above case and is needed for
2940     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2941     above (including some development versions of 3.1.0), but before it was
2942     known that this change would break tapes like the above and was fixed in
2943     3.1.1, so that the changed behaviour was active although the engine version
2944     while recording maybe was before 3.1.0. There is at least one tape that is
2945     affected by this exception, which is the tape for the one-level set "Bug
2946     Machine" by Juergen Bonhagen.
2947   */
2948
2949   game.use_change_when_pushing_bug =
2950     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2951      !(tape.playing &&
2952        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2953        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2954
2955   /*
2956     Summary of bugfix/change:
2957     Fixed handling for blocking the field the player leaves when moving.
2958
2959     Fixed/changed in version:
2960     3.1.1
2961
2962     Description:
2963     Before 3.1.1, when "block last field when moving" was enabled, the field
2964     the player is leaving when moving was blocked for the time of the move,
2965     and was directly unblocked afterwards. This resulted in the last field
2966     being blocked for exactly one less than the number of frames of one player
2967     move. Additionally, even when blocking was disabled, the last field was
2968     blocked for exactly one frame.
2969     Since 3.1.1, due to changes in player movement handling, the last field
2970     is not blocked at all when blocking is disabled. When blocking is enabled,
2971     the last field is blocked for exactly the number of frames of one player
2972     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2973     last field is blocked for exactly one more than the number of frames of
2974     one player move.
2975
2976     Affected levels/tapes:
2977     (!!! yet to be determined -- probably many !!!)
2978   */
2979
2980   game.use_block_last_field_bug =
2981     (game.engine_version < VERSION_IDENT(3,1,1,0));
2982
2983   /* various special flags and settings for native Emerald Mine game engine */
2984
2985   game_em.use_single_button =
2986     (game.engine_version > VERSION_IDENT(4,0,0,2));
2987
2988   game_em.use_snap_key_bug =
2989     (game.engine_version < VERSION_IDENT(4,0,1,0));
2990
2991   game_em.use_old_explosions =
2992     (game.engine_version < VERSION_IDENT(4,1,4,2));
2993
2994   // --------------------------------------------------------------------------
2995
2996   // set maximal allowed number of custom element changes per game frame
2997   game.max_num_changes_per_frame = 1;
2998
2999   // default scan direction: scan playfield from top/left to bottom/right
3000   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3001
3002   // dynamically adjust element properties according to game engine version
3003   InitElementPropertiesEngine(game.engine_version);
3004
3005   // ---------- initialize special element properties -------------------------
3006
3007   // "EL_AMOEBA_DROPPING" missed property "can fall" between 2.0.1 and 4.1.4.1
3008   if (use_amoeba_dropping_cannot_fall_bug)
3009     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3010
3011   // ---------- initialize player's initial move delay ------------------------
3012
3013   // dynamically adjust player properties according to level information
3014   for (i = 0; i < MAX_PLAYERS; i++)
3015     game.initial_move_delay_value[i] =
3016       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3017
3018   // dynamically adjust player properties according to game engine version
3019   for (i = 0; i < MAX_PLAYERS; i++)
3020     game.initial_move_delay[i] =
3021       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3022        game.initial_move_delay_value[i] : 0);
3023
3024   // ---------- initialize player's initial push delay ------------------------
3025
3026   // dynamically adjust player properties according to game engine version
3027   game.initial_push_delay_value =
3028     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3029
3030   // ---------- initialize changing elements ----------------------------------
3031
3032   // initialize changing elements information
3033   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3034   {
3035     struct ElementInfo *ei = &element_info[i];
3036
3037     // this pointer might have been changed in the level editor
3038     ei->change = &ei->change_page[0];
3039
3040     if (!IS_CUSTOM_ELEMENT(i))
3041     {
3042       ei->change->target_element = EL_EMPTY_SPACE;
3043       ei->change->delay_fixed = 0;
3044       ei->change->delay_random = 0;
3045       ei->change->delay_frames = 1;
3046     }
3047
3048     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3049     {
3050       ei->has_change_event[j] = FALSE;
3051
3052       ei->event_page_nr[j] = 0;
3053       ei->event_page[j] = &ei->change_page[0];
3054     }
3055   }
3056
3057   // add changing elements from pre-defined list
3058   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3059   {
3060     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3061     struct ElementInfo *ei = &element_info[ch_delay->element];
3062
3063     ei->change->target_element       = ch_delay->target_element;
3064     ei->change->delay_fixed          = ch_delay->change_delay;
3065
3066     ei->change->pre_change_function  = ch_delay->pre_change_function;
3067     ei->change->change_function      = ch_delay->change_function;
3068     ei->change->post_change_function = ch_delay->post_change_function;
3069
3070     ei->change->can_change = TRUE;
3071     ei->change->can_change_or_has_action = TRUE;
3072
3073     ei->has_change_event[CE_DELAY] = TRUE;
3074
3075     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3076     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3077   }
3078
3079   // ---------- initialize internal run-time variables ------------------------
3080
3081   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3082   {
3083     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3084
3085     for (j = 0; j < ei->num_change_pages; j++)
3086     {
3087       ei->change_page[j].can_change_or_has_action =
3088         (ei->change_page[j].can_change |
3089          ei->change_page[j].has_action);
3090     }
3091   }
3092
3093   // add change events from custom element configuration
3094   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3095   {
3096     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3097
3098     for (j = 0; j < ei->num_change_pages; j++)
3099     {
3100       if (!ei->change_page[j].can_change_or_has_action)
3101         continue;
3102
3103       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3104       {
3105         // only add event page for the first page found with this event
3106         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3107         {
3108           ei->has_change_event[k] = TRUE;
3109
3110           ei->event_page_nr[k] = j;
3111           ei->event_page[k] = &ei->change_page[j];
3112         }
3113       }
3114     }
3115   }
3116
3117   // ---------- initialize reference elements in change conditions ------------
3118
3119   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3120   {
3121     int element = EL_CUSTOM_START + i;
3122     struct ElementInfo *ei = &element_info[element];
3123
3124     for (j = 0; j < ei->num_change_pages; j++)
3125     {
3126       int trigger_element = ei->change_page[j].initial_trigger_element;
3127
3128       if (trigger_element >= EL_PREV_CE_8 &&
3129           trigger_element <= EL_NEXT_CE_8)
3130         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3131
3132       ei->change_page[j].trigger_element = trigger_element;
3133     }
3134   }
3135
3136   // ---------- initialize run-time trigger player and element ----------------
3137
3138   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3139   {
3140     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3141
3142     for (j = 0; j < ei->num_change_pages; j++)
3143     {
3144       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3145       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3146       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3147       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3148       ei->change_page[j].actual_trigger_ce_value = 0;
3149       ei->change_page[j].actual_trigger_ce_score = 0;
3150     }
3151   }
3152
3153   // ---------- initialize trigger events -------------------------------------
3154
3155   // initialize trigger events information
3156   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3157     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3158       trigger_events[i][j] = FALSE;
3159
3160   // add trigger events from element change event properties
3161   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3162   {
3163     struct ElementInfo *ei = &element_info[i];
3164
3165     for (j = 0; j < ei->num_change_pages; j++)
3166     {
3167       if (!ei->change_page[j].can_change_or_has_action)
3168         continue;
3169
3170       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3171       {
3172         int trigger_element = ei->change_page[j].trigger_element;
3173
3174         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3175         {
3176           if (ei->change_page[j].has_event[k])
3177           {
3178             if (IS_GROUP_ELEMENT(trigger_element))
3179             {
3180               struct ElementGroupInfo *group =
3181                 element_info[trigger_element].group;
3182
3183               for (l = 0; l < group->num_elements_resolved; l++)
3184                 trigger_events[group->element_resolved[l]][k] = TRUE;
3185             }
3186             else if (trigger_element == EL_ANY_ELEMENT)
3187               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3188                 trigger_events[l][k] = TRUE;
3189             else
3190               trigger_events[trigger_element][k] = TRUE;
3191           }
3192         }
3193       }
3194     }
3195   }
3196
3197   // ---------- initialize push delay -----------------------------------------
3198
3199   // initialize push delay values to default
3200   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3201   {
3202     if (!IS_CUSTOM_ELEMENT(i))
3203     {
3204       // set default push delay values (corrected since version 3.0.7-1)
3205       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3206       {
3207         element_info[i].push_delay_fixed = 2;
3208         element_info[i].push_delay_random = 8;
3209       }
3210       else
3211       {
3212         element_info[i].push_delay_fixed = 8;
3213         element_info[i].push_delay_random = 8;
3214       }
3215     }
3216   }
3217
3218   // set push delay value for certain elements from pre-defined list
3219   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3220   {
3221     int e = push_delay_list[i].element;
3222
3223     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3224     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3225   }
3226
3227   // set push delay value for Supaplex elements for newer engine versions
3228   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3229   {
3230     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3231     {
3232       if (IS_SP_ELEMENT(i))
3233       {
3234         // set SP push delay to just enough to push under a falling zonk
3235         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3236
3237         element_info[i].push_delay_fixed  = delay;
3238         element_info[i].push_delay_random = 0;
3239       }
3240     }
3241   }
3242
3243   // ---------- initialize move stepsize --------------------------------------
3244
3245   // initialize move stepsize values to default
3246   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3247     if (!IS_CUSTOM_ELEMENT(i))
3248       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3249
3250   // set move stepsize value for certain elements from pre-defined list
3251   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3252   {
3253     int e = move_stepsize_list[i].element;
3254
3255     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3256
3257     // set move stepsize value for certain elements for older engine versions
3258     if (use_old_move_stepsize_for_magic_wall)
3259     {
3260       if (e == EL_MAGIC_WALL_FILLING ||
3261           e == EL_MAGIC_WALL_EMPTYING ||
3262           e == EL_BD_MAGIC_WALL_FILLING ||
3263           e == EL_BD_MAGIC_WALL_EMPTYING)
3264         element_info[e].move_stepsize *= 2;
3265     }
3266   }
3267
3268   // ---------- initialize collect score --------------------------------------
3269
3270   // initialize collect score values for custom elements from initial value
3271   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3272     if (IS_CUSTOM_ELEMENT(i))
3273       element_info[i].collect_score = element_info[i].collect_score_initial;
3274
3275   // ---------- initialize collect count --------------------------------------
3276
3277   // initialize collect count values for non-custom elements
3278   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3279     if (!IS_CUSTOM_ELEMENT(i))
3280       element_info[i].collect_count_initial = 0;
3281
3282   // add collect count values for all elements from pre-defined list
3283   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3284     element_info[collect_count_list[i].element].collect_count_initial =
3285       collect_count_list[i].count;
3286
3287   // ---------- initialize access direction -----------------------------------
3288
3289   // initialize access direction values to default (access from every side)
3290   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3291     if (!IS_CUSTOM_ELEMENT(i))
3292       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3293
3294   // set access direction value for certain elements from pre-defined list
3295   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3296     element_info[access_direction_list[i].element].access_direction =
3297       access_direction_list[i].direction;
3298
3299   // ---------- initialize explosion content ----------------------------------
3300   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3301   {
3302     if (IS_CUSTOM_ELEMENT(i))
3303       continue;
3304
3305     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3306     {
3307       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3308
3309       element_info[i].content.e[x][y] =
3310         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3311          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3312          i == EL_PLAYER_3 ? EL_EMERALD :
3313          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3314          i == EL_MOLE ? EL_EMERALD_RED :
3315          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3316          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3317          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3318          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3319          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3320          i == EL_WALL_EMERALD ? EL_EMERALD :
3321          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3322          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3323          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3324          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3325          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3326          i == EL_WALL_PEARL ? EL_PEARL :
3327          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3328          EL_EMPTY);
3329     }
3330   }
3331
3332   // ---------- initialize recursion detection --------------------------------
3333   recursion_loop_depth = 0;
3334   recursion_loop_detected = FALSE;
3335   recursion_loop_element = EL_UNDEFINED;
3336
3337   // ---------- initialize graphics engine ------------------------------------
3338   game.scroll_delay_value =
3339     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3340      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3341      !setup.forced_scroll_delay           ? 0 :
3342      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3343   game.scroll_delay_value =
3344     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3345
3346   // ---------- initialize game engine snapshots ------------------------------
3347   for (i = 0; i < MAX_PLAYERS; i++)
3348     game.snapshot.last_action[i] = 0;
3349   game.snapshot.changed_action = FALSE;
3350   game.snapshot.collected_item = FALSE;
3351   game.snapshot.mode =
3352     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3353      SNAPSHOT_MODE_EVERY_STEP :
3354      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3355      SNAPSHOT_MODE_EVERY_MOVE :
3356      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3357      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3358   game.snapshot.save_snapshot = FALSE;
3359
3360   // ---------- initialize level time for Supaplex engine ---------------------
3361   // Supaplex levels with time limit currently unsupported -- should be added
3362   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3363     level.time = 0;
3364
3365   // ---------- initialize flags for handling game actions --------------------
3366
3367   // set flags for game actions to default values
3368   game.use_key_actions = TRUE;
3369   game.use_mouse_actions = FALSE;
3370
3371   // when using Mirror Magic game engine, handle mouse events only
3372   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3373   {
3374     game.use_key_actions = FALSE;
3375     game.use_mouse_actions = TRUE;
3376   }
3377
3378   // check for custom elements with mouse click events
3379   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3380   {
3381     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3382     {
3383       int element = EL_CUSTOM_START + i;
3384
3385       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3386           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3387           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3388           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3389         game.use_mouse_actions = TRUE;
3390     }
3391   }
3392 }
3393
3394 static int get_num_special_action(int element, int action_first,
3395                                   int action_last)
3396 {
3397   int num_special_action = 0;
3398   int i, j;
3399
3400   for (i = action_first; i <= action_last; i++)
3401   {
3402     boolean found = FALSE;
3403
3404     for (j = 0; j < NUM_DIRECTIONS; j++)
3405       if (el_act_dir2img(element, i, j) !=
3406           el_act_dir2img(element, ACTION_DEFAULT, j))
3407         found = TRUE;
3408
3409     if (found)
3410       num_special_action++;
3411     else
3412       break;
3413   }
3414
3415   return num_special_action;
3416 }
3417
3418
3419 // ============================================================================
3420 // InitGame()
3421 // ----------------------------------------------------------------------------
3422 // initialize and start new game
3423 // ============================================================================
3424
3425 #if DEBUG_INIT_PLAYER
3426 static void DebugPrintPlayerStatus(char *message)
3427 {
3428   int i;
3429
3430   if (!options.debug)
3431     return;
3432
3433   printf("%s:\n", message);
3434
3435   for (i = 0; i < MAX_PLAYERS; i++)
3436   {
3437     struct PlayerInfo *player = &stored_player[i];
3438
3439     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3440            i + 1,
3441            player->present,
3442            player->connected,
3443            player->connected_locally,
3444            player->connected_network,
3445            player->active);
3446
3447     if (local_player == player)
3448       printf(" (local player)");
3449
3450     printf("\n");
3451   }
3452 }
3453 #endif
3454
3455 void InitGame(void)
3456 {
3457   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3458   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3459   int fade_mask = REDRAW_FIELD;
3460
3461   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3462   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3463   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3464   int initial_move_dir = MV_DOWN;
3465   int i, j, x, y;
3466
3467   // required here to update video display before fading (FIX THIS)
3468   DrawMaskedBorder(REDRAW_DOOR_2);
3469
3470   if (!game.restart_level)
3471     CloseDoor(DOOR_CLOSE_1);
3472
3473   SetGameStatus(GAME_MODE_PLAYING);
3474
3475   if (level_editor_test_game)
3476     FadeSkipNextFadeOut();
3477   else
3478     FadeSetEnterScreen();
3479
3480   if (CheckFadeAll())
3481     fade_mask = REDRAW_ALL;
3482
3483   FadeLevelSoundsAndMusic();
3484
3485   ExpireSoundLoops(TRUE);
3486
3487   FadeOut(fade_mask);
3488
3489   if (level_editor_test_game)
3490     FadeSkipNextFadeIn();
3491
3492   // needed if different viewport properties defined for playing
3493   ChangeViewportPropertiesIfNeeded();
3494
3495   ClearField();
3496
3497   DrawCompleteVideoDisplay();
3498
3499   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3500
3501   InitGameEngine();
3502   InitGameControlValues();
3503
3504   // initialize tape actions from game when recording tape
3505   if (tape.recording)
3506   {
3507     tape.use_key_actions   = game.use_key_actions;
3508     tape.use_mouse_actions = game.use_mouse_actions;
3509   }
3510
3511   // don't play tapes over network
3512   network_playing = (network.enabled && !tape.playing);
3513
3514   for (i = 0; i < MAX_PLAYERS; i++)
3515   {
3516     struct PlayerInfo *player = &stored_player[i];
3517
3518     player->index_nr = i;
3519     player->index_bit = (1 << i);
3520     player->element_nr = EL_PLAYER_1 + i;
3521
3522     player->present = FALSE;
3523     player->active = FALSE;
3524     player->mapped = FALSE;
3525
3526     player->killed = FALSE;
3527     player->reanimated = FALSE;
3528     player->buried = FALSE;
3529
3530     player->action = 0;
3531     player->effective_action = 0;
3532     player->programmed_action = 0;
3533     player->snap_action = 0;
3534
3535     player->mouse_action.lx = 0;
3536     player->mouse_action.ly = 0;
3537     player->mouse_action.button = 0;
3538     player->mouse_action.button_hint = 0;
3539
3540     player->effective_mouse_action.lx = 0;
3541     player->effective_mouse_action.ly = 0;
3542     player->effective_mouse_action.button = 0;
3543     player->effective_mouse_action.button_hint = 0;
3544
3545     for (j = 0; j < MAX_NUM_KEYS; j++)
3546       player->key[j] = FALSE;
3547
3548     player->num_white_keys = 0;
3549
3550     player->dynabomb_count = 0;
3551     player->dynabomb_size = 1;
3552     player->dynabombs_left = 0;
3553     player->dynabomb_xl = FALSE;
3554
3555     player->MovDir = initial_move_dir;
3556     player->MovPos = 0;
3557     player->GfxPos = 0;
3558     player->GfxDir = initial_move_dir;
3559     player->GfxAction = ACTION_DEFAULT;
3560     player->Frame = 0;
3561     player->StepFrame = 0;
3562
3563     player->initial_element = player->element_nr;
3564     player->artwork_element =
3565       (level.use_artwork_element[i] ? level.artwork_element[i] :
3566        player->element_nr);
3567     player->use_murphy = FALSE;
3568
3569     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3570     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3571
3572     player->gravity = level.initial_player_gravity[i];
3573
3574     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3575
3576     player->actual_frame_counter = 0;
3577
3578     player->step_counter = 0;
3579
3580     player->last_move_dir = initial_move_dir;
3581
3582     player->is_active = FALSE;
3583
3584     player->is_waiting = FALSE;
3585     player->is_moving = FALSE;
3586     player->is_auto_moving = FALSE;
3587     player->is_digging = FALSE;
3588     player->is_snapping = FALSE;
3589     player->is_collecting = FALSE;
3590     player->is_pushing = FALSE;
3591     player->is_switching = FALSE;
3592     player->is_dropping = FALSE;
3593     player->is_dropping_pressed = FALSE;
3594
3595     player->is_bored = FALSE;
3596     player->is_sleeping = FALSE;
3597
3598     player->was_waiting = TRUE;
3599     player->was_moving = FALSE;
3600     player->was_snapping = FALSE;
3601     player->was_dropping = FALSE;
3602
3603     player->force_dropping = FALSE;
3604
3605     player->frame_counter_bored = -1;
3606     player->frame_counter_sleeping = -1;
3607
3608     player->anim_delay_counter = 0;
3609     player->post_delay_counter = 0;
3610
3611     player->dir_waiting = initial_move_dir;
3612     player->action_waiting = ACTION_DEFAULT;
3613     player->last_action_waiting = ACTION_DEFAULT;
3614     player->special_action_bored = ACTION_DEFAULT;
3615     player->special_action_sleeping = ACTION_DEFAULT;
3616
3617     player->switch_x = -1;
3618     player->switch_y = -1;
3619
3620     player->drop_x = -1;
3621     player->drop_y = -1;
3622
3623     player->show_envelope = 0;
3624
3625     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3626
3627     player->push_delay       = -1;      // initialized when pushing starts
3628     player->push_delay_value = game.initial_push_delay_value;
3629
3630     player->drop_delay = 0;
3631     player->drop_pressed_delay = 0;
3632
3633     player->last_jx = -1;
3634     player->last_jy = -1;
3635     player->jx = -1;
3636     player->jy = -1;
3637
3638     player->shield_normal_time_left = 0;
3639     player->shield_deadly_time_left = 0;
3640
3641     player->inventory_infinite_element = EL_UNDEFINED;
3642     player->inventory_size = 0;
3643
3644     if (level.use_initial_inventory[i])
3645     {
3646       for (j = 0; j < level.initial_inventory_size[i]; j++)
3647       {
3648         int element = level.initial_inventory_content[i][j];
3649         int collect_count = element_info[element].collect_count_initial;
3650         int k;
3651
3652         if (!IS_CUSTOM_ELEMENT(element))
3653           collect_count = 1;
3654
3655         if (collect_count == 0)
3656           player->inventory_infinite_element = element;
3657         else
3658           for (k = 0; k < collect_count; k++)
3659             if (player->inventory_size < MAX_INVENTORY_SIZE)
3660               player->inventory_element[player->inventory_size++] = element;
3661       }
3662     }
3663
3664     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3665     SnapField(player, 0, 0);
3666
3667     map_player_action[i] = i;
3668   }
3669
3670   network_player_action_received = FALSE;
3671
3672   // initial null action
3673   if (network_playing)
3674     SendToServer_MovePlayer(MV_NONE);
3675
3676   FrameCounter = 0;
3677   TimeFrames = 0;
3678   TimePlayed = 0;
3679   TimeLeft = level.time;
3680   TapeTime = 0;
3681
3682   ScreenMovDir = MV_NONE;
3683   ScreenMovPos = 0;
3684   ScreenGfxPos = 0;
3685
3686   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3687
3688   game.robot_wheel_x = -1;
3689   game.robot_wheel_y = -1;
3690
3691   game.exit_x = -1;
3692   game.exit_y = -1;
3693
3694   game.all_players_gone = FALSE;
3695
3696   game.LevelSolved = FALSE;
3697   game.GameOver = FALSE;
3698
3699   game.GamePlayed = !tape.playing;
3700
3701   game.LevelSolved_GameWon = FALSE;
3702   game.LevelSolved_GameEnd = FALSE;
3703   game.LevelSolved_SaveTape = FALSE;
3704   game.LevelSolved_SaveScore = FALSE;
3705
3706   game.LevelSolved_CountingTime = 0;
3707   game.LevelSolved_CountingScore = 0;
3708   game.LevelSolved_CountingHealth = 0;
3709
3710   game.panel.active = TRUE;
3711
3712   game.no_time_limit = (level.time == 0);
3713
3714   game.yamyam_content_nr = 0;
3715   game.robot_wheel_active = FALSE;
3716   game.magic_wall_active = FALSE;
3717   game.magic_wall_time_left = 0;
3718   game.light_time_left = 0;
3719   game.timegate_time_left = 0;
3720   game.switchgate_pos = 0;
3721   game.wind_direction = level.wind_direction_initial;
3722
3723   game.score = 0;
3724   game.score_final = 0;
3725
3726   game.health = MAX_HEALTH;
3727   game.health_final = MAX_HEALTH;
3728
3729   game.gems_still_needed = level.gems_needed;
3730   game.sokoban_fields_still_needed = 0;
3731   game.sokoban_objects_still_needed = 0;
3732   game.lights_still_needed = 0;
3733   game.players_still_needed = 0;
3734   game.friends_still_needed = 0;
3735
3736   game.lenses_time_left = 0;
3737   game.magnify_time_left = 0;
3738
3739   game.ball_active = level.ball_active_initial;
3740   game.ball_content_nr = 0;
3741
3742   game.explosions_delayed = TRUE;
3743
3744   game.envelope_active = FALSE;
3745
3746   for (i = 0; i < NUM_BELTS; i++)
3747   {
3748     game.belt_dir[i] = MV_NONE;
3749     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3750   }
3751
3752   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3753     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3754
3755 #if DEBUG_INIT_PLAYER
3756   DebugPrintPlayerStatus("Player status at level initialization");
3757 #endif
3758
3759   SCAN_PLAYFIELD(x, y)
3760   {
3761     Feld[x][y] = Last[x][y] = level.field[x][y];
3762     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3763     ChangeDelay[x][y] = 0;
3764     ChangePage[x][y] = -1;
3765     CustomValue[x][y] = 0;              // initialized in InitField()
3766     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3767     AmoebaNr[x][y] = 0;
3768     WasJustMoving[x][y] = 0;
3769     WasJustFalling[x][y] = 0;
3770     CheckCollision[x][y] = 0;
3771     CheckImpact[x][y] = 0;
3772     Stop[x][y] = FALSE;
3773     Pushed[x][y] = FALSE;
3774
3775     ChangeCount[x][y] = 0;
3776     ChangeEvent[x][y] = -1;
3777
3778     ExplodePhase[x][y] = 0;
3779     ExplodeDelay[x][y] = 0;
3780     ExplodeField[x][y] = EX_TYPE_NONE;
3781
3782     RunnerVisit[x][y] = 0;
3783     PlayerVisit[x][y] = 0;
3784
3785     GfxFrame[x][y] = 0;
3786     GfxRandom[x][y] = INIT_GFX_RANDOM();
3787     GfxElement[x][y] = EL_UNDEFINED;
3788     GfxAction[x][y] = ACTION_DEFAULT;
3789     GfxDir[x][y] = MV_NONE;
3790     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3791   }
3792
3793   SCAN_PLAYFIELD(x, y)
3794   {
3795     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3796       emulate_bd = FALSE;
3797     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3798       emulate_sb = FALSE;
3799     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3800       emulate_sp = FALSE;
3801
3802     InitField(x, y, TRUE);
3803
3804     ResetGfxAnimation(x, y);
3805   }
3806
3807   InitBeltMovement();
3808
3809   for (i = 0; i < MAX_PLAYERS; i++)
3810   {
3811     struct PlayerInfo *player = &stored_player[i];
3812
3813     // set number of special actions for bored and sleeping animation
3814     player->num_special_action_bored =
3815       get_num_special_action(player->artwork_element,
3816                              ACTION_BORING_1, ACTION_BORING_LAST);
3817     player->num_special_action_sleeping =
3818       get_num_special_action(player->artwork_element,
3819                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3820   }
3821
3822   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3823                     emulate_sb ? EMU_SOKOBAN :
3824                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3825
3826   // initialize type of slippery elements
3827   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3828   {
3829     if (!IS_CUSTOM_ELEMENT(i))
3830     {
3831       // default: elements slip down either to the left or right randomly
3832       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3833
3834       // SP style elements prefer to slip down on the left side
3835       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3836         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3837
3838       // BD style elements prefer to slip down on the left side
3839       if (game.emulation == EMU_BOULDERDASH)
3840         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3841     }
3842   }
3843
3844   // initialize explosion and ignition delay
3845   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3846   {
3847     if (!IS_CUSTOM_ELEMENT(i))
3848     {
3849       int num_phase = 8;
3850       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3851                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3852                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3853       int last_phase = (num_phase + 1) * delay;
3854       int half_phase = (num_phase / 2) * delay;
3855
3856       element_info[i].explosion_delay = last_phase - 1;
3857       element_info[i].ignition_delay = half_phase;
3858
3859       if (i == EL_BLACK_ORB)
3860         element_info[i].ignition_delay = 1;
3861     }
3862   }
3863
3864   // correct non-moving belts to start moving left
3865   for (i = 0; i < NUM_BELTS; i++)
3866     if (game.belt_dir[i] == MV_NONE)
3867       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3868
3869 #if USE_NEW_PLAYER_ASSIGNMENTS
3870   // use preferred player also in local single-player mode
3871   if (!network.enabled && !game.team_mode)
3872   {
3873     int new_index_nr = setup.network_player_nr;
3874
3875     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3876     {
3877       for (i = 0; i < MAX_PLAYERS; i++)
3878         stored_player[i].connected_locally = FALSE;
3879
3880       stored_player[new_index_nr].connected_locally = TRUE;
3881     }
3882   }
3883
3884   for (i = 0; i < MAX_PLAYERS; i++)
3885   {
3886     stored_player[i].connected = FALSE;
3887
3888     // in network game mode, the local player might not be the first player
3889     if (stored_player[i].connected_locally)
3890       local_player = &stored_player[i];
3891   }
3892
3893   if (!network.enabled)
3894     local_player->connected = TRUE;
3895
3896   if (tape.playing)
3897   {
3898     for (i = 0; i < MAX_PLAYERS; i++)
3899       stored_player[i].connected = tape.player_participates[i];
3900   }
3901   else if (network.enabled)
3902   {
3903     // add team mode players connected over the network (needed for correct
3904     // assignment of player figures from level to locally playing players)
3905
3906     for (i = 0; i < MAX_PLAYERS; i++)
3907       if (stored_player[i].connected_network)
3908         stored_player[i].connected = TRUE;
3909   }
3910   else if (game.team_mode)
3911   {
3912     // try to guess locally connected team mode players (needed for correct
3913     // assignment of player figures from level to locally playing players)
3914
3915     for (i = 0; i < MAX_PLAYERS; i++)
3916       if (setup.input[i].use_joystick ||
3917           setup.input[i].key.left != KSYM_UNDEFINED)
3918         stored_player[i].connected = TRUE;
3919   }
3920
3921 #if DEBUG_INIT_PLAYER
3922   DebugPrintPlayerStatus("Player status after level initialization");
3923 #endif
3924
3925 #if DEBUG_INIT_PLAYER
3926   if (options.debug)
3927     printf("Reassigning players ...\n");
3928 #endif
3929
3930   // check if any connected player was not found in playfield
3931   for (i = 0; i < MAX_PLAYERS; i++)
3932   {
3933     struct PlayerInfo *player = &stored_player[i];
3934
3935     if (player->connected && !player->present)
3936     {
3937       struct PlayerInfo *field_player = NULL;
3938
3939 #if DEBUG_INIT_PLAYER
3940       if (options.debug)
3941         printf("- looking for field player for player %d ...\n", i + 1);
3942 #endif
3943
3944       // assign first free player found that is present in the playfield
3945
3946       // first try: look for unmapped playfield player that is not connected
3947       for (j = 0; j < MAX_PLAYERS; j++)
3948         if (field_player == NULL &&
3949             stored_player[j].present &&
3950             !stored_player[j].mapped &&
3951             !stored_player[j].connected)
3952           field_player = &stored_player[j];
3953
3954       // second try: look for *any* unmapped playfield player
3955       for (j = 0; j < MAX_PLAYERS; j++)
3956         if (field_player == NULL &&
3957             stored_player[j].present &&
3958             !stored_player[j].mapped)
3959           field_player = &stored_player[j];
3960
3961       if (field_player != NULL)
3962       {
3963         int jx = field_player->jx, jy = field_player->jy;
3964
3965 #if DEBUG_INIT_PLAYER
3966         if (options.debug)
3967           printf("- found player %d\n", field_player->index_nr + 1);
3968 #endif
3969
3970         player->present = FALSE;
3971         player->active = FALSE;
3972
3973         field_player->present = TRUE;
3974         field_player->active = TRUE;
3975
3976         /*
3977         player->initial_element = field_player->initial_element;
3978         player->artwork_element = field_player->artwork_element;
3979
3980         player->block_last_field       = field_player->block_last_field;
3981         player->block_delay_adjustment = field_player->block_delay_adjustment;
3982         */
3983
3984         StorePlayer[jx][jy] = field_player->element_nr;
3985
3986         field_player->jx = field_player->last_jx = jx;
3987         field_player->jy = field_player->last_jy = jy;
3988
3989         if (local_player == player)
3990           local_player = field_player;
3991
3992         map_player_action[field_player->index_nr] = i;
3993
3994         field_player->mapped = TRUE;
3995
3996 #if DEBUG_INIT_PLAYER
3997         if (options.debug)
3998           printf("- map_player_action[%d] == %d\n",
3999                  field_player->index_nr + 1, i + 1);
4000 #endif
4001       }
4002     }
4003
4004     if (player->connected && player->present)
4005       player->mapped = TRUE;
4006   }
4007
4008 #if DEBUG_INIT_PLAYER
4009   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4010 #endif
4011
4012 #else
4013
4014   // check if any connected player was not found in playfield
4015   for (i = 0; i < MAX_PLAYERS; i++)
4016   {
4017     struct PlayerInfo *player = &stored_player[i];
4018
4019     if (player->connected && !player->present)
4020     {
4021       for (j = 0; j < MAX_PLAYERS; j++)
4022       {
4023         struct PlayerInfo *field_player = &stored_player[j];
4024         int jx = field_player->jx, jy = field_player->jy;
4025
4026         // assign first free player found that is present in the playfield
4027         if (field_player->present && !field_player->connected)
4028         {
4029           player->present = TRUE;
4030           player->active = TRUE;
4031
4032           field_player->present = FALSE;
4033           field_player->active = FALSE;
4034
4035           player->initial_element = field_player->initial_element;
4036           player->artwork_element = field_player->artwork_element;
4037
4038           player->block_last_field       = field_player->block_last_field;
4039           player->block_delay_adjustment = field_player->block_delay_adjustment;
4040
4041           StorePlayer[jx][jy] = player->element_nr;
4042
4043           player->jx = player->last_jx = jx;
4044           player->jy = player->last_jy = jy;
4045
4046           break;
4047         }
4048       }
4049     }
4050   }
4051 #endif
4052
4053 #if 0
4054   printf("::: local_player->present == %d\n", local_player->present);
4055 #endif
4056
4057   // set focus to local player for network games, else to all players
4058   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4059   game.centered_player_nr_next = game.centered_player_nr;
4060   game.set_centered_player = FALSE;
4061   game.set_centered_player_wrap = FALSE;
4062
4063   if (network_playing && tape.recording)
4064   {
4065     // store client dependent player focus when recording network games
4066     tape.centered_player_nr_next = game.centered_player_nr_next;
4067     tape.set_centered_player = TRUE;
4068   }
4069
4070   if (tape.playing)
4071   {
4072     // when playing a tape, eliminate all players who do not participate
4073
4074 #if USE_NEW_PLAYER_ASSIGNMENTS
4075
4076     if (!game.team_mode)
4077     {
4078       for (i = 0; i < MAX_PLAYERS; i++)
4079       {
4080         if (stored_player[i].active &&
4081             !tape.player_participates[map_player_action[i]])
4082         {
4083           struct PlayerInfo *player = &stored_player[i];
4084           int jx = player->jx, jy = player->jy;
4085
4086 #if DEBUG_INIT_PLAYER
4087           if (options.debug)
4088             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4089 #endif
4090
4091           player->active = FALSE;
4092           StorePlayer[jx][jy] = 0;
4093           Feld[jx][jy] = EL_EMPTY;
4094         }
4095       }
4096     }
4097
4098 #else
4099
4100     for (i = 0; i < MAX_PLAYERS; i++)
4101     {
4102       if (stored_player[i].active &&
4103           !tape.player_participates[i])
4104       {
4105         struct PlayerInfo *player = &stored_player[i];
4106         int jx = player->jx, jy = player->jy;
4107
4108         player->active = FALSE;
4109         StorePlayer[jx][jy] = 0;
4110         Feld[jx][jy] = EL_EMPTY;
4111       }
4112     }
4113 #endif
4114   }
4115   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4116   {
4117     // when in single player mode, eliminate all but the local player
4118
4119     for (i = 0; i < MAX_PLAYERS; i++)
4120     {
4121       struct PlayerInfo *player = &stored_player[i];
4122
4123       if (player->active && player != local_player)
4124       {
4125         int jx = player->jx, jy = player->jy;
4126
4127         player->active = FALSE;
4128         player->present = FALSE;
4129
4130         StorePlayer[jx][jy] = 0;
4131         Feld[jx][jy] = EL_EMPTY;
4132       }
4133     }
4134   }
4135
4136   for (i = 0; i < MAX_PLAYERS; i++)
4137     if (stored_player[i].active)
4138       game.players_still_needed++;
4139
4140   if (level.solved_by_one_player)
4141     game.players_still_needed = 1;
4142
4143   // when recording the game, store which players take part in the game
4144   if (tape.recording)
4145   {
4146 #if USE_NEW_PLAYER_ASSIGNMENTS
4147     for (i = 0; i < MAX_PLAYERS; i++)
4148       if (stored_player[i].connected)
4149         tape.player_participates[i] = TRUE;
4150 #else
4151     for (i = 0; i < MAX_PLAYERS; i++)
4152       if (stored_player[i].active)
4153         tape.player_participates[i] = TRUE;
4154 #endif
4155   }
4156
4157 #if DEBUG_INIT_PLAYER
4158   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4159 #endif
4160
4161   if (BorderElement == EL_EMPTY)
4162   {
4163     SBX_Left = 0;
4164     SBX_Right = lev_fieldx - SCR_FIELDX;
4165     SBY_Upper = 0;
4166     SBY_Lower = lev_fieldy - SCR_FIELDY;
4167   }
4168   else
4169   {
4170     SBX_Left = -1;
4171     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4172     SBY_Upper = -1;
4173     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4174   }
4175
4176   if (full_lev_fieldx <= SCR_FIELDX)
4177     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4178   if (full_lev_fieldy <= SCR_FIELDY)
4179     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4180
4181   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4182     SBX_Left--;
4183   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4184     SBY_Upper--;
4185
4186   // if local player not found, look for custom element that might create
4187   // the player (make some assumptions about the right custom element)
4188   if (!local_player->present)
4189   {
4190     int start_x = 0, start_y = 0;
4191     int found_rating = 0;
4192     int found_element = EL_UNDEFINED;
4193     int player_nr = local_player->index_nr;
4194
4195     SCAN_PLAYFIELD(x, y)
4196     {
4197       int element = Feld[x][y];
4198       int content;
4199       int xx, yy;
4200       boolean is_player;
4201
4202       if (level.use_start_element[player_nr] &&
4203           level.start_element[player_nr] == element &&
4204           found_rating < 4)
4205       {
4206         start_x = x;
4207         start_y = y;
4208
4209         found_rating = 4;
4210         found_element = element;
4211       }
4212
4213       if (!IS_CUSTOM_ELEMENT(element))
4214         continue;
4215
4216       if (CAN_CHANGE(element))
4217       {
4218         for (i = 0; i < element_info[element].num_change_pages; i++)
4219         {
4220           // check for player created from custom element as single target
4221           content = element_info[element].change_page[i].target_element;
4222           is_player = ELEM_IS_PLAYER(content);
4223
4224           if (is_player && (found_rating < 3 ||
4225                             (found_rating == 3 && element < found_element)))
4226           {
4227             start_x = x;
4228             start_y = y;
4229
4230             found_rating = 3;
4231             found_element = element;
4232           }
4233         }
4234       }
4235
4236       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4237       {
4238         // check for player created from custom element as explosion content
4239         content = element_info[element].content.e[xx][yy];
4240         is_player = ELEM_IS_PLAYER(content);
4241
4242         if (is_player && (found_rating < 2 ||
4243                           (found_rating == 2 && element < found_element)))
4244         {
4245           start_x = x + xx - 1;
4246           start_y = y + yy - 1;
4247
4248           found_rating = 2;
4249           found_element = element;
4250         }
4251
4252         if (!CAN_CHANGE(element))
4253           continue;
4254
4255         for (i = 0; i < element_info[element].num_change_pages; i++)
4256         {
4257           // check for player created from custom element as extended target
4258           content =
4259             element_info[element].change_page[i].target_content.e[xx][yy];
4260
4261           is_player = ELEM_IS_PLAYER(content);
4262
4263           if (is_player && (found_rating < 1 ||
4264                             (found_rating == 1 && element < found_element)))
4265           {
4266             start_x = x + xx - 1;
4267             start_y = y + yy - 1;
4268
4269             found_rating = 1;
4270             found_element = element;
4271           }
4272         }
4273       }
4274     }
4275
4276     scroll_x = SCROLL_POSITION_X(start_x);
4277     scroll_y = SCROLL_POSITION_Y(start_y);
4278   }
4279   else
4280   {
4281     scroll_x = SCROLL_POSITION_X(local_player->jx);
4282     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4283   }
4284
4285   // !!! FIX THIS (START) !!!
4286   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4287   {
4288     InitGameEngine_EM();
4289   }
4290   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4291   {
4292     InitGameEngine_SP();
4293   }
4294   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4295   {
4296     InitGameEngine_MM();
4297   }
4298   else
4299   {
4300     DrawLevel(REDRAW_FIELD);
4301     DrawAllPlayers();
4302
4303     // after drawing the level, correct some elements
4304     if (game.timegate_time_left == 0)
4305       CloseAllOpenTimegates();
4306   }
4307
4308   // blit playfield from scroll buffer to normal back buffer for fading in
4309   BlitScreenToBitmap(backbuffer);
4310   // !!! FIX THIS (END) !!!
4311
4312   DrawMaskedBorder(fade_mask);
4313
4314   FadeIn(fade_mask);
4315
4316 #if 1
4317   // full screen redraw is required at this point in the following cases:
4318   // - special editor door undrawn when game was started from level editor
4319   // - drawing area (playfield) was changed and has to be removed completely
4320   redraw_mask = REDRAW_ALL;
4321   BackToFront();
4322 #endif
4323
4324   if (!game.restart_level)
4325   {
4326     // copy default game door content to main double buffer
4327
4328     // !!! CHECK AGAIN !!!
4329     SetPanelBackground();
4330     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4331     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4332   }
4333
4334   SetPanelBackground();
4335   SetDrawBackgroundMask(REDRAW_DOOR_1);
4336
4337   UpdateAndDisplayGameControlValues();
4338
4339   if (!game.restart_level)
4340   {
4341     UnmapGameButtons();
4342     UnmapTapeButtons();
4343
4344     FreeGameButtons();
4345     CreateGameButtons();
4346
4347     MapGameButtons();
4348     MapTapeButtons();
4349
4350     // copy actual game door content to door double buffer for OpenDoor()
4351     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4352
4353     OpenDoor(DOOR_OPEN_ALL);
4354
4355     KeyboardAutoRepeatOffUnlessAutoplay();
4356
4357 #if DEBUG_INIT_PLAYER
4358     DebugPrintPlayerStatus("Player status (final)");
4359 #endif
4360   }
4361
4362   UnmapAllGadgets();
4363
4364   MapGameButtons();
4365   MapTapeButtons();
4366
4367   if (!game.restart_level && !tape.playing)
4368   {
4369     LevelStats_incPlayed(level_nr);
4370
4371     SaveLevelSetup_SeriesInfo();
4372   }
4373
4374   game.restart_level = FALSE;
4375   game.restart_game_message = NULL;
4376   game.request_active = FALSE;
4377
4378   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4379     InitGameActions_MM();
4380
4381   SaveEngineSnapshotToListInitial();
4382
4383   if (!game.restart_level)
4384   {
4385     PlaySound(SND_GAME_STARTING);
4386
4387     if (setup.sound_music)
4388       PlayLevelMusic();
4389   }
4390 }
4391
4392 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4393                         int actual_player_x, int actual_player_y)
4394 {
4395   // this is used for non-R'n'D game engines to update certain engine values
4396
4397   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4398   {
4399     actual_scroll_x = correctLevelPosX_EM(actual_scroll_x);
4400     actual_scroll_y = correctLevelPosY_EM(actual_scroll_y);
4401
4402     actual_player_x = correctLevelPosX_EM(actual_player_x);
4403     actual_player_y = correctLevelPosY_EM(actual_player_y);
4404   }
4405
4406   // needed to determine if sounds are played within the visible screen area
4407   scroll_x = actual_scroll_x;
4408   scroll_y = actual_scroll_y;
4409
4410   // needed to get player position for "follow finger" playing input method
4411   local_player->jx = actual_player_x;
4412   local_player->jy = actual_player_y;
4413 }
4414
4415 void InitMovDir(int x, int y)
4416 {
4417   int i, element = Feld[x][y];
4418   static int xy[4][2] =
4419   {
4420     {  0, +1 },
4421     { +1,  0 },
4422     {  0, -1 },
4423     { -1,  0 }
4424   };
4425   static int direction[3][4] =
4426   {
4427     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4428     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4429     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4430   };
4431
4432   switch (element)
4433   {
4434     case EL_BUG_RIGHT:
4435     case EL_BUG_UP:
4436     case EL_BUG_LEFT:
4437     case EL_BUG_DOWN:
4438       Feld[x][y] = EL_BUG;
4439       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4440       break;
4441
4442     case EL_SPACESHIP_RIGHT:
4443     case EL_SPACESHIP_UP:
4444     case EL_SPACESHIP_LEFT:
4445     case EL_SPACESHIP_DOWN:
4446       Feld[x][y] = EL_SPACESHIP;
4447       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4448       break;
4449
4450     case EL_BD_BUTTERFLY_RIGHT:
4451     case EL_BD_BUTTERFLY_UP:
4452     case EL_BD_BUTTERFLY_LEFT:
4453     case EL_BD_BUTTERFLY_DOWN:
4454       Feld[x][y] = EL_BD_BUTTERFLY;
4455       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4456       break;
4457
4458     case EL_BD_FIREFLY_RIGHT:
4459     case EL_BD_FIREFLY_UP:
4460     case EL_BD_FIREFLY_LEFT:
4461     case EL_BD_FIREFLY_DOWN:
4462       Feld[x][y] = EL_BD_FIREFLY;
4463       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4464       break;
4465
4466     case EL_PACMAN_RIGHT:
4467     case EL_PACMAN_UP:
4468     case EL_PACMAN_LEFT:
4469     case EL_PACMAN_DOWN:
4470       Feld[x][y] = EL_PACMAN;
4471       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4472       break;
4473
4474     case EL_YAMYAM_LEFT:
4475     case EL_YAMYAM_RIGHT:
4476     case EL_YAMYAM_UP:
4477     case EL_YAMYAM_DOWN:
4478       Feld[x][y] = EL_YAMYAM;
4479       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4480       break;
4481
4482     case EL_SP_SNIKSNAK:
4483       MovDir[x][y] = MV_UP;
4484       break;
4485
4486     case EL_SP_ELECTRON:
4487       MovDir[x][y] = MV_LEFT;
4488       break;
4489
4490     case EL_MOLE_LEFT:
4491     case EL_MOLE_RIGHT:
4492     case EL_MOLE_UP:
4493     case EL_MOLE_DOWN:
4494       Feld[x][y] = EL_MOLE;
4495       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4496       break;
4497
4498     default:
4499       if (IS_CUSTOM_ELEMENT(element))
4500       {
4501         struct ElementInfo *ei = &element_info[element];
4502         int move_direction_initial = ei->move_direction_initial;
4503         int move_pattern = ei->move_pattern;
4504
4505         if (move_direction_initial == MV_START_PREVIOUS)
4506         {
4507           if (MovDir[x][y] != MV_NONE)
4508             return;
4509
4510           move_direction_initial = MV_START_AUTOMATIC;
4511         }
4512
4513         if (move_direction_initial == MV_START_RANDOM)
4514           MovDir[x][y] = 1 << RND(4);
4515         else if (move_direction_initial & MV_ANY_DIRECTION)
4516           MovDir[x][y] = move_direction_initial;
4517         else if (move_pattern == MV_ALL_DIRECTIONS ||
4518                  move_pattern == MV_TURNING_LEFT ||
4519                  move_pattern == MV_TURNING_RIGHT ||
4520                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4521                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4522                  move_pattern == MV_TURNING_RANDOM)
4523           MovDir[x][y] = 1 << RND(4);
4524         else if (move_pattern == MV_HORIZONTAL)
4525           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4526         else if (move_pattern == MV_VERTICAL)
4527           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4528         else if (move_pattern & MV_ANY_DIRECTION)
4529           MovDir[x][y] = element_info[element].move_pattern;
4530         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4531                  move_pattern == MV_ALONG_RIGHT_SIDE)
4532         {
4533           // use random direction as default start direction
4534           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4535             MovDir[x][y] = 1 << RND(4);
4536
4537           for (i = 0; i < NUM_DIRECTIONS; i++)
4538           {
4539             int x1 = x + xy[i][0];
4540             int y1 = y + xy[i][1];
4541
4542             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4543             {
4544               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4545                 MovDir[x][y] = direction[0][i];
4546               else
4547                 MovDir[x][y] = direction[1][i];
4548
4549               break;
4550             }
4551           }
4552         }                
4553       }
4554       else
4555       {
4556         MovDir[x][y] = 1 << RND(4);
4557
4558         if (element != EL_BUG &&
4559             element != EL_SPACESHIP &&
4560             element != EL_BD_BUTTERFLY &&
4561             element != EL_BD_FIREFLY)
4562           break;
4563
4564         for (i = 0; i < NUM_DIRECTIONS; i++)
4565         {
4566           int x1 = x + xy[i][0];
4567           int y1 = y + xy[i][1];
4568
4569           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4570           {
4571             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4572             {
4573               MovDir[x][y] = direction[0][i];
4574               break;
4575             }
4576             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4577                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4578             {
4579               MovDir[x][y] = direction[1][i];
4580               break;
4581             }
4582           }
4583         }
4584       }
4585       break;
4586   }
4587
4588   GfxDir[x][y] = MovDir[x][y];
4589 }
4590
4591 void InitAmoebaNr(int x, int y)
4592 {
4593   int i;
4594   int group_nr = AmoebeNachbarNr(x, y);
4595
4596   if (group_nr == 0)
4597   {
4598     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4599     {
4600       if (AmoebaCnt[i] == 0)
4601       {
4602         group_nr = i;
4603         break;
4604       }
4605     }
4606   }
4607
4608   AmoebaNr[x][y] = group_nr;
4609   AmoebaCnt[group_nr]++;
4610   AmoebaCnt2[group_nr]++;
4611 }
4612
4613 static void LevelSolved(void)
4614 {
4615   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4616       game.players_still_needed > 0)
4617     return;
4618
4619   game.LevelSolved = TRUE;
4620   game.GameOver = TRUE;
4621
4622   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4623                       game_em.lev->score :
4624                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4625                       game_mm.score :
4626                       game.score);
4627   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4628                        MM_HEALTH(game_mm.laser_overload_value) :
4629                        game.health);
4630
4631   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4632   game.LevelSolved_CountingScore = game.score_final;
4633   game.LevelSolved_CountingHealth = game.health_final;
4634 }
4635
4636 void GameWon(void)
4637 {
4638   static int time_count_steps;
4639   static int time, time_final;
4640   static int score, score_final;
4641   static int health, health_final;
4642   static int game_over_delay_1 = 0;
4643   static int game_over_delay_2 = 0;
4644   static int game_over_delay_3 = 0;
4645   int game_over_delay_value_1 = 50;
4646   int game_over_delay_value_2 = 25;
4647   int game_over_delay_value_3 = 50;
4648
4649   if (!game.LevelSolved_GameWon)
4650   {
4651     int i;
4652
4653     // do not start end game actions before the player stops moving (to exit)
4654     if (local_player->active && local_player->MovPos)
4655       return;
4656
4657     game.LevelSolved_GameWon = TRUE;
4658     game.LevelSolved_SaveTape = tape.recording;
4659     game.LevelSolved_SaveScore = !tape.playing;
4660
4661     if (!tape.playing)
4662     {
4663       LevelStats_incSolved(level_nr);
4664
4665       SaveLevelSetup_SeriesInfo();
4666     }
4667
4668     if (tape.auto_play)         // tape might already be stopped here
4669       tape.auto_play_level_solved = TRUE;
4670
4671     TapeStop();
4672
4673     game_over_delay_1 = 0;
4674     game_over_delay_2 = 0;
4675     game_over_delay_3 = game_over_delay_value_3;
4676
4677     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4678     score = score_final = game.score_final;
4679     health = health_final = game.health_final;
4680
4681     if (level.score[SC_TIME_BONUS] > 0)
4682     {
4683       if (TimeLeft > 0)
4684       {
4685         time_final = 0;
4686         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4687       }
4688       else if (game.no_time_limit && TimePlayed < 999)
4689       {
4690         time_final = 999;
4691         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4692       }
4693
4694       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4695
4696       game_over_delay_1 = game_over_delay_value_1;
4697
4698       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4699       {
4700         health_final = 0;
4701         score_final += health * level.score[SC_TIME_BONUS];
4702
4703         game_over_delay_2 = game_over_delay_value_2;
4704       }
4705
4706       game.score_final = score_final;
4707       game.health_final = health_final;
4708     }
4709
4710     if (level_editor_test_game)
4711     {
4712       time = time_final;
4713       score = score_final;
4714
4715       game.LevelSolved_CountingTime = time;
4716       game.LevelSolved_CountingScore = score;
4717
4718       game_panel_controls[GAME_PANEL_TIME].value = time;
4719       game_panel_controls[GAME_PANEL_SCORE].value = score;
4720
4721       DisplayGameControlValues();
4722     }
4723
4724     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4725     {
4726       // check if last player has left the level
4727       if (game.exit_x >= 0 &&
4728           game.exit_y >= 0)
4729       {
4730         int x = game.exit_x;
4731         int y = game.exit_y;
4732         int element = Feld[x][y];
4733
4734         // close exit door after last player
4735         if ((game.all_players_gone &&
4736              (element == EL_EXIT_OPEN ||
4737               element == EL_SP_EXIT_OPEN ||
4738               element == EL_STEEL_EXIT_OPEN)) ||
4739             element == EL_EM_EXIT_OPEN ||
4740             element == EL_EM_STEEL_EXIT_OPEN)
4741         {
4742
4743           Feld[x][y] =
4744             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4745              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4746              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4747              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4748              EL_EM_STEEL_EXIT_CLOSING);
4749
4750           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4751         }
4752
4753         // player disappears
4754         DrawLevelField(x, y);
4755       }
4756
4757       for (i = 0; i < MAX_PLAYERS; i++)
4758       {
4759         struct PlayerInfo *player = &stored_player[i];
4760
4761         if (player->present)
4762         {
4763           RemovePlayer(player);
4764
4765           // player disappears
4766           DrawLevelField(player->jx, player->jy);
4767         }
4768       }
4769     }
4770
4771     PlaySound(SND_GAME_WINNING);
4772   }
4773
4774   if (game_over_delay_1 > 0)
4775   {
4776     game_over_delay_1--;
4777
4778     return;
4779   }
4780
4781   if (time != time_final)
4782   {
4783     int time_to_go = ABS(time_final - time);
4784     int time_count_dir = (time < time_final ? +1 : -1);
4785
4786     if (time_to_go < time_count_steps)
4787       time_count_steps = 1;
4788
4789     time  += time_count_steps * time_count_dir;
4790     score += time_count_steps * level.score[SC_TIME_BONUS];
4791
4792     game.LevelSolved_CountingTime = time;
4793     game.LevelSolved_CountingScore = score;
4794
4795     game_panel_controls[GAME_PANEL_TIME].value = time;
4796     game_panel_controls[GAME_PANEL_SCORE].value = score;
4797
4798     DisplayGameControlValues();
4799
4800     if (time == time_final)
4801       StopSound(SND_GAME_LEVELTIME_BONUS);
4802     else if (setup.sound_loops)
4803       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4804     else
4805       PlaySound(SND_GAME_LEVELTIME_BONUS);
4806
4807     return;
4808   }
4809
4810   if (game_over_delay_2 > 0)
4811   {
4812     game_over_delay_2--;
4813
4814     return;
4815   }
4816
4817   if (health != health_final)
4818   {
4819     int health_count_dir = (health < health_final ? +1 : -1);
4820
4821     health += health_count_dir;
4822     score  += level.score[SC_TIME_BONUS];
4823
4824     game.LevelSolved_CountingHealth = health;
4825     game.LevelSolved_CountingScore = score;
4826
4827     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4828     game_panel_controls[GAME_PANEL_SCORE].value = score;
4829
4830     DisplayGameControlValues();
4831
4832     if (health == health_final)
4833       StopSound(SND_GAME_LEVELTIME_BONUS);
4834     else if (setup.sound_loops)
4835       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4836     else
4837       PlaySound(SND_GAME_LEVELTIME_BONUS);
4838
4839     return;
4840   }
4841
4842   game.panel.active = FALSE;
4843
4844   if (game_over_delay_3 > 0)
4845   {
4846     game_over_delay_3--;
4847
4848     return;
4849   }
4850
4851   GameEnd();
4852 }
4853
4854 void GameEnd(void)
4855 {
4856   // used instead of "level_nr" (needed for network games)
4857   int last_level_nr = levelset.level_nr;
4858   int hi_pos;
4859
4860   game.LevelSolved_GameEnd = TRUE;
4861
4862   if (game.LevelSolved_SaveTape)
4863   {
4864     // make sure that request dialog to save tape does not open door again
4865     if (!global.use_envelope_request)
4866       CloseDoor(DOOR_CLOSE_1);
4867
4868     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4869   }
4870
4871   // if no tape is to be saved, close both doors simultaneously
4872   CloseDoor(DOOR_CLOSE_ALL);
4873
4874   if (level_editor_test_game)
4875   {
4876     SetGameStatus(GAME_MODE_MAIN);
4877
4878     DrawMainMenu();
4879
4880     return;
4881   }
4882
4883   if (!game.LevelSolved_SaveScore)
4884   {
4885     SetGameStatus(GAME_MODE_MAIN);
4886
4887     DrawMainMenu();
4888
4889     return;
4890   }
4891
4892   if (level_nr == leveldir_current->handicap_level)
4893   {
4894     leveldir_current->handicap_level++;
4895
4896     SaveLevelSetup_SeriesInfo();
4897   }
4898
4899   if (setup.increment_levels &&
4900       level_nr < leveldir_current->last_level &&
4901       !network_playing)
4902   {
4903     level_nr++;         // advance to next level
4904     TapeErase();        // start with empty tape
4905
4906     if (setup.auto_play_next_level)
4907     {
4908       LoadLevel(level_nr);
4909
4910       SaveLevelSetup_SeriesInfo();
4911     }
4912   }
4913
4914   hi_pos = NewHiScore(last_level_nr);
4915
4916   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4917   {
4918     SetGameStatus(GAME_MODE_SCORES);
4919
4920     DrawHallOfFame(last_level_nr, hi_pos);
4921   }
4922   else if (setup.auto_play_next_level && setup.increment_levels &&
4923            last_level_nr < leveldir_current->last_level &&
4924            !network_playing)
4925   {
4926     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4927   }
4928   else
4929   {
4930     SetGameStatus(GAME_MODE_MAIN);
4931
4932     DrawMainMenu();
4933   }
4934 }
4935
4936 int NewHiScore(int level_nr)
4937 {
4938   int k, l;
4939   int position = -1;
4940   boolean one_score_entry_per_name = !program.many_scores_per_name;
4941
4942   LoadScore(level_nr);
4943
4944   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4945       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4946     return -1;
4947
4948   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4949   {
4950     if (game.score_final > highscore[k].Score)
4951     {
4952       // player has made it to the hall of fame
4953
4954       if (k < MAX_SCORE_ENTRIES - 1)
4955       {
4956         int m = MAX_SCORE_ENTRIES - 1;
4957
4958         if (one_score_entry_per_name)
4959         {
4960           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4961             if (strEqual(setup.player_name, highscore[l].Name))
4962               m = l;
4963
4964           if (m == k)   // player's new highscore overwrites his old one
4965             goto put_into_list;
4966         }
4967
4968         for (l = m; l > k; l--)
4969         {
4970           strcpy(highscore[l].Name, highscore[l - 1].Name);
4971           highscore[l].Score = highscore[l - 1].Score;
4972         }
4973       }
4974
4975       put_into_list:
4976
4977       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4978       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4979       highscore[k].Score = game.score_final;
4980       position = k;
4981
4982       break;
4983     }
4984     else if (one_score_entry_per_name &&
4985              !strncmp(setup.player_name, highscore[k].Name,
4986                       MAX_PLAYER_NAME_LEN))
4987       break;    // player already there with a higher score
4988   }
4989
4990   if (position >= 0) 
4991     SaveScore(level_nr);
4992
4993   return position;
4994 }
4995
4996 static int getElementMoveStepsizeExt(int x, int y, int direction)
4997 {
4998   int element = Feld[x][y];
4999   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5000   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5001   int horiz_move = (dx != 0);
5002   int sign = (horiz_move ? dx : dy);
5003   int step = sign * element_info[element].move_stepsize;
5004
5005   // special values for move stepsize for spring and things on conveyor belt
5006   if (horiz_move)
5007   {
5008     if (CAN_FALL(element) &&
5009         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5010       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5011     else if (element == EL_SPRING)
5012       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5013   }
5014
5015   return step;
5016 }
5017
5018 static int getElementMoveStepsize(int x, int y)
5019 {
5020   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5021 }
5022
5023 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5024 {
5025   if (player->GfxAction != action || player->GfxDir != dir)
5026   {
5027     player->GfxAction = action;
5028     player->GfxDir = dir;
5029     player->Frame = 0;
5030     player->StepFrame = 0;
5031   }
5032 }
5033
5034 static void ResetGfxFrame(int x, int y)
5035 {
5036   // profiling showed that "autotest" spends 10~20% of its time in this function
5037   if (DrawingDeactivatedField())
5038     return;
5039
5040   int element = Feld[x][y];
5041   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5042
5043   if (graphic_info[graphic].anim_global_sync)
5044     GfxFrame[x][y] = FrameCounter;
5045   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5046     GfxFrame[x][y] = CustomValue[x][y];
5047   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5048     GfxFrame[x][y] = element_info[element].collect_score;
5049   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5050     GfxFrame[x][y] = ChangeDelay[x][y];
5051 }
5052
5053 static void ResetGfxAnimation(int x, int y)
5054 {
5055   GfxAction[x][y] = ACTION_DEFAULT;
5056   GfxDir[x][y] = MovDir[x][y];
5057   GfxFrame[x][y] = 0;
5058
5059   ResetGfxFrame(x, y);
5060 }
5061
5062 static void ResetRandomAnimationValue(int x, int y)
5063 {
5064   GfxRandom[x][y] = INIT_GFX_RANDOM();
5065 }
5066
5067 static void InitMovingField(int x, int y, int direction)
5068 {
5069   int element = Feld[x][y];
5070   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5071   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5072   int newx = x + dx;
5073   int newy = y + dy;
5074   boolean is_moving_before, is_moving_after;
5075
5076   // check if element was/is moving or being moved before/after mode change
5077   is_moving_before = (WasJustMoving[x][y] != 0);
5078   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5079
5080   // reset animation only for moving elements which change direction of moving
5081   // or which just started or stopped moving
5082   // (else CEs with property "can move" / "not moving" are reset each frame)
5083   if (is_moving_before != is_moving_after ||
5084       direction != MovDir[x][y])
5085     ResetGfxAnimation(x, y);
5086
5087   MovDir[x][y] = direction;
5088   GfxDir[x][y] = direction;
5089
5090   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5091                      direction == MV_DOWN && CAN_FALL(element) ?
5092                      ACTION_FALLING : ACTION_MOVING);
5093
5094   // this is needed for CEs with property "can move" / "not moving"
5095
5096   if (is_moving_after)
5097   {
5098     if (Feld[newx][newy] == EL_EMPTY)
5099       Feld[newx][newy] = EL_BLOCKED;
5100
5101     MovDir[newx][newy] = MovDir[x][y];
5102
5103     CustomValue[newx][newy] = CustomValue[x][y];
5104
5105     GfxFrame[newx][newy] = GfxFrame[x][y];
5106     GfxRandom[newx][newy] = GfxRandom[x][y];
5107     GfxAction[newx][newy] = GfxAction[x][y];
5108     GfxDir[newx][newy] = GfxDir[x][y];
5109   }
5110 }
5111
5112 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5113 {
5114   int direction = MovDir[x][y];
5115   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5116   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5117
5118   *goes_to_x = newx;
5119   *goes_to_y = newy;
5120 }
5121
5122 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5123 {
5124   int oldx = x, oldy = y;
5125   int direction = MovDir[x][y];
5126
5127   if (direction == MV_LEFT)
5128     oldx++;
5129   else if (direction == MV_RIGHT)
5130     oldx--;
5131   else if (direction == MV_UP)
5132     oldy++;
5133   else if (direction == MV_DOWN)
5134     oldy--;
5135
5136   *comes_from_x = oldx;
5137   *comes_from_y = oldy;
5138 }
5139
5140 static int MovingOrBlocked2Element(int x, int y)
5141 {
5142   int element = Feld[x][y];
5143
5144   if (element == EL_BLOCKED)
5145   {
5146     int oldx, oldy;
5147
5148     Blocked2Moving(x, y, &oldx, &oldy);
5149     return Feld[oldx][oldy];
5150   }
5151   else
5152     return element;
5153 }
5154
5155 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5156 {
5157   // like MovingOrBlocked2Element(), but if element is moving
5158   // and (x,y) is the field the moving element is just leaving,
5159   // return EL_BLOCKED instead of the element value
5160   int element = Feld[x][y];
5161
5162   if (IS_MOVING(x, y))
5163   {
5164     if (element == EL_BLOCKED)
5165     {
5166       int oldx, oldy;
5167
5168       Blocked2Moving(x, y, &oldx, &oldy);
5169       return Feld[oldx][oldy];
5170     }
5171     else
5172       return EL_BLOCKED;
5173   }
5174   else
5175     return element;
5176 }
5177
5178 static void RemoveField(int x, int y)
5179 {
5180   Feld[x][y] = EL_EMPTY;
5181
5182   MovPos[x][y] = 0;
5183   MovDir[x][y] = 0;
5184   MovDelay[x][y] = 0;
5185
5186   CustomValue[x][y] = 0;
5187
5188   AmoebaNr[x][y] = 0;
5189   ChangeDelay[x][y] = 0;
5190   ChangePage[x][y] = -1;
5191   Pushed[x][y] = FALSE;
5192
5193   GfxElement[x][y] = EL_UNDEFINED;
5194   GfxAction[x][y] = ACTION_DEFAULT;
5195   GfxDir[x][y] = MV_NONE;
5196 }
5197
5198 static void RemoveMovingField(int x, int y)
5199 {
5200   int oldx = x, oldy = y, newx = x, newy = y;
5201   int element = Feld[x][y];
5202   int next_element = EL_UNDEFINED;
5203
5204   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5205     return;
5206
5207   if (IS_MOVING(x, y))
5208   {
5209     Moving2Blocked(x, y, &newx, &newy);
5210
5211     if (Feld[newx][newy] != EL_BLOCKED)
5212     {
5213       // element is moving, but target field is not free (blocked), but
5214       // already occupied by something different (example: acid pool);
5215       // in this case, only remove the moving field, but not the target
5216
5217       RemoveField(oldx, oldy);
5218
5219       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5220
5221       TEST_DrawLevelField(oldx, oldy);
5222
5223       return;
5224     }
5225   }
5226   else if (element == EL_BLOCKED)
5227   {
5228     Blocked2Moving(x, y, &oldx, &oldy);
5229     if (!IS_MOVING(oldx, oldy))
5230       return;
5231   }
5232
5233   if (element == EL_BLOCKED &&
5234       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5235        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5236        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5237        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5238        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5239        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5240     next_element = get_next_element(Feld[oldx][oldy]);
5241
5242   RemoveField(oldx, oldy);
5243   RemoveField(newx, newy);
5244
5245   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5246
5247   if (next_element != EL_UNDEFINED)
5248     Feld[oldx][oldy] = next_element;
5249
5250   TEST_DrawLevelField(oldx, oldy);
5251   TEST_DrawLevelField(newx, newy);
5252 }
5253
5254 void DrawDynamite(int x, int y)
5255 {
5256   int sx = SCREENX(x), sy = SCREENY(y);
5257   int graphic = el2img(Feld[x][y]);
5258   int frame;
5259
5260   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5261     return;
5262
5263   if (IS_WALKABLE_INSIDE(Back[x][y]))
5264     return;
5265
5266   if (Back[x][y])
5267     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5268   else if (Store[x][y])
5269     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5270
5271   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5272
5273   if (Back[x][y] || Store[x][y])
5274     DrawGraphicThruMask(sx, sy, graphic, frame);
5275   else
5276     DrawGraphic(sx, sy, graphic, frame);
5277 }
5278
5279 static void CheckDynamite(int x, int y)
5280 {
5281   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5282   {
5283     MovDelay[x][y]--;
5284
5285     if (MovDelay[x][y] != 0)
5286     {
5287       DrawDynamite(x, y);
5288       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5289
5290       return;
5291     }
5292   }
5293
5294   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5295
5296   Bang(x, y);
5297 }
5298
5299 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5300 {
5301   boolean num_checked_players = 0;
5302   int i;
5303
5304   for (i = 0; i < MAX_PLAYERS; i++)
5305   {
5306     if (stored_player[i].active)
5307     {
5308       int sx = stored_player[i].jx;
5309       int sy = stored_player[i].jy;
5310
5311       if (num_checked_players == 0)
5312       {
5313         *sx1 = *sx2 = sx;
5314         *sy1 = *sy2 = sy;
5315       }
5316       else
5317       {
5318         *sx1 = MIN(*sx1, sx);
5319         *sy1 = MIN(*sy1, sy);
5320         *sx2 = MAX(*sx2, sx);
5321         *sy2 = MAX(*sy2, sy);
5322       }
5323
5324       num_checked_players++;
5325     }
5326   }
5327 }
5328
5329 static boolean checkIfAllPlayersFitToScreen_RND(void)
5330 {
5331   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5332
5333   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5334
5335   return (sx2 - sx1 < SCR_FIELDX &&
5336           sy2 - sy1 < SCR_FIELDY);
5337 }
5338
5339 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5340 {
5341   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5342
5343   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5344
5345   *sx = (sx1 + sx2) / 2;
5346   *sy = (sy1 + sy2) / 2;
5347 }
5348
5349 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5350                                boolean center_screen, boolean quick_relocation)
5351 {
5352   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5353   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5354   boolean no_delay = (tape.warp_forward);
5355   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5356   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5357   int new_scroll_x, new_scroll_y;
5358
5359   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5360   {
5361     // case 1: quick relocation inside visible screen (without scrolling)
5362
5363     RedrawPlayfield();
5364
5365     return;
5366   }
5367
5368   if (!level.shifted_relocation || center_screen)
5369   {
5370     // relocation _with_ centering of screen
5371
5372     new_scroll_x = SCROLL_POSITION_X(x);
5373     new_scroll_y = SCROLL_POSITION_Y(y);
5374   }
5375   else
5376   {
5377     // relocation _without_ centering of screen
5378
5379     int center_scroll_x = SCROLL_POSITION_X(old_x);
5380     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5381     int offset_x = x + (scroll_x - center_scroll_x);
5382     int offset_y = y + (scroll_y - center_scroll_y);
5383
5384     // for new screen position, apply previous offset to center position
5385     new_scroll_x = SCROLL_POSITION_X(offset_x);
5386     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5387   }
5388
5389   if (quick_relocation)
5390   {
5391     // case 2: quick relocation (redraw without visible scrolling)
5392
5393     scroll_x = new_scroll_x;
5394     scroll_y = new_scroll_y;
5395
5396     RedrawPlayfield();
5397
5398     return;
5399   }
5400
5401   // case 3: visible relocation (with scrolling to new position)
5402
5403   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5404
5405   SetVideoFrameDelay(wait_delay_value);
5406
5407   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5408   {
5409     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5410     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5411
5412     if (dx == 0 && dy == 0)             // no scrolling needed at all
5413       break;
5414
5415     scroll_x -= dx;
5416     scroll_y -= dy;
5417
5418     // set values for horizontal/vertical screen scrolling (half tile size)
5419     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5420     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5421     int pos_x = dx * TILEX / 2;
5422     int pos_y = dy * TILEY / 2;
5423     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5424     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5425
5426     ScrollLevel(dx, dy);
5427     DrawAllPlayers();
5428
5429     // scroll in two steps of half tile size to make things smoother
5430     BlitScreenToBitmapExt_RND(window, fx, fy);
5431
5432     // scroll second step to align at full tile size
5433     BlitScreenToBitmap(window);
5434   }
5435
5436   DrawAllPlayers();
5437   BackToFront();
5438
5439   SetVideoFrameDelay(frame_delay_value_old);
5440 }
5441
5442 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5443 {
5444   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5445   int player_nr = GET_PLAYER_NR(el_player);
5446   struct PlayerInfo *player = &stored_player[player_nr];
5447   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5448   boolean no_delay = (tape.warp_forward);
5449   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5450   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5451   int old_jx = player->jx;
5452   int old_jy = player->jy;
5453   int old_element = Feld[old_jx][old_jy];
5454   int element = Feld[jx][jy];
5455   boolean player_relocated = (old_jx != jx || old_jy != jy);
5456
5457   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5458   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5459   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5460   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5461   int leave_side_horiz = move_dir_horiz;
5462   int leave_side_vert  = move_dir_vert;
5463   int enter_side = enter_side_horiz | enter_side_vert;
5464   int leave_side = leave_side_horiz | leave_side_vert;
5465
5466   if (player->buried)           // do not reanimate dead player
5467     return;
5468
5469   if (!player_relocated)        // no need to relocate the player
5470     return;
5471
5472   if (IS_PLAYER(jx, jy))        // player already placed at new position
5473   {
5474     RemoveField(jx, jy);        // temporarily remove newly placed player
5475     DrawLevelField(jx, jy);
5476   }
5477
5478   if (player->present)
5479   {
5480     while (player->MovPos)
5481     {
5482       ScrollPlayer(player, SCROLL_GO_ON);
5483       ScrollScreen(NULL, SCROLL_GO_ON);
5484
5485       AdvanceFrameAndPlayerCounters(player->index_nr);
5486
5487       DrawPlayer(player);
5488
5489       BackToFront_WithFrameDelay(wait_delay_value);
5490     }
5491
5492     DrawPlayer(player);         // needed here only to cleanup last field
5493     DrawLevelField(player->jx, player->jy);     // remove player graphic
5494
5495     player->is_moving = FALSE;
5496   }
5497
5498   if (IS_CUSTOM_ELEMENT(old_element))
5499     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5500                                CE_LEFT_BY_PLAYER,
5501                                player->index_bit, leave_side);
5502
5503   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5504                                       CE_PLAYER_LEAVES_X,
5505                                       player->index_bit, leave_side);
5506
5507   Feld[jx][jy] = el_player;
5508   InitPlayerField(jx, jy, el_player, TRUE);
5509
5510   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5511      possible that the relocation target field did not contain a player element,
5512      but a walkable element, to which the new player was relocated -- in this
5513      case, restore that (already initialized!) element on the player field */
5514   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5515   {
5516     Feld[jx][jy] = element;     // restore previously existing element
5517   }
5518
5519   // only visually relocate centered player
5520   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5521                      FALSE, level.instant_relocation);
5522
5523   TestIfPlayerTouchesBadThing(jx, jy);
5524   TestIfPlayerTouchesCustomElement(jx, jy);
5525
5526   if (IS_CUSTOM_ELEMENT(element))
5527     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5528                                player->index_bit, enter_side);
5529
5530   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5531                                       player->index_bit, enter_side);
5532
5533   if (player->is_switching)
5534   {
5535     /* ensure that relocation while still switching an element does not cause
5536        a new element to be treated as also switched directly after relocation
5537        (this is important for teleporter switches that teleport the player to
5538        a place where another teleporter switch is in the same direction, which
5539        would then incorrectly be treated as immediately switched before the
5540        direction key that caused the switch was released) */
5541
5542     player->switch_x += jx - old_jx;
5543     player->switch_y += jy - old_jy;
5544   }
5545 }
5546
5547 static void Explode(int ex, int ey, int phase, int mode)
5548 {
5549   int x, y;
5550   int last_phase;
5551   int border_element;
5552
5553   // !!! eliminate this variable !!!
5554   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5555
5556   if (game.explosions_delayed)
5557   {
5558     ExplodeField[ex][ey] = mode;
5559     return;
5560   }
5561
5562   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5563   {
5564     int center_element = Feld[ex][ey];
5565     int artwork_element, explosion_element;     // set these values later
5566
5567     // remove things displayed in background while burning dynamite
5568     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5569       Back[ex][ey] = 0;
5570
5571     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5572     {
5573       // put moving element to center field (and let it explode there)
5574       center_element = MovingOrBlocked2Element(ex, ey);
5575       RemoveMovingField(ex, ey);
5576       Feld[ex][ey] = center_element;
5577     }
5578
5579     // now "center_element" is finally determined -- set related values now
5580     artwork_element = center_element;           // for custom player artwork
5581     explosion_element = center_element;         // for custom player artwork
5582
5583     if (IS_PLAYER(ex, ey))
5584     {
5585       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5586
5587       artwork_element = stored_player[player_nr].artwork_element;
5588
5589       if (level.use_explosion_element[player_nr])
5590       {
5591         explosion_element = level.explosion_element[player_nr];
5592         artwork_element = explosion_element;
5593       }
5594     }
5595
5596     if (mode == EX_TYPE_NORMAL ||
5597         mode == EX_TYPE_CENTER ||
5598         mode == EX_TYPE_CROSS)
5599       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5600
5601     last_phase = element_info[explosion_element].explosion_delay + 1;
5602
5603     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5604     {
5605       int xx = x - ex + 1;
5606       int yy = y - ey + 1;
5607       int element;
5608
5609       if (!IN_LEV_FIELD(x, y) ||
5610           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5611           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5612         continue;
5613
5614       element = Feld[x][y];
5615
5616       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5617       {
5618         element = MovingOrBlocked2Element(x, y);
5619
5620         if (!IS_EXPLOSION_PROOF(element))
5621           RemoveMovingField(x, y);
5622       }
5623
5624       // indestructible elements can only explode in center (but not flames)
5625       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5626                                            mode == EX_TYPE_BORDER)) ||
5627           element == EL_FLAMES)
5628         continue;
5629
5630       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5631          behaviour, for example when touching a yamyam that explodes to rocks
5632          with active deadly shield, a rock is created under the player !!! */
5633       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5634 #if 0
5635       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5636           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5637            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5638 #else
5639       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5640 #endif
5641       {
5642         if (IS_ACTIVE_BOMB(element))
5643         {
5644           // re-activate things under the bomb like gate or penguin
5645           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5646           Back[x][y] = 0;
5647         }
5648
5649         continue;
5650       }
5651
5652       // save walkable background elements while explosion on same tile
5653       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5654           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5655         Back[x][y] = element;
5656
5657       // ignite explodable elements reached by other explosion
5658       if (element == EL_EXPLOSION)
5659         element = Store2[x][y];
5660
5661       if (AmoebaNr[x][y] &&
5662           (element == EL_AMOEBA_FULL ||
5663            element == EL_BD_AMOEBA ||
5664            element == EL_AMOEBA_GROWING))
5665       {
5666         AmoebaCnt[AmoebaNr[x][y]]--;
5667         AmoebaCnt2[AmoebaNr[x][y]]--;
5668       }
5669
5670       RemoveField(x, y);
5671
5672       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5673       {
5674         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5675
5676         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5677
5678         if (PLAYERINFO(ex, ey)->use_murphy)
5679           Store[x][y] = EL_EMPTY;
5680       }
5681
5682       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5683       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5684       else if (ELEM_IS_PLAYER(center_element))
5685         Store[x][y] = EL_EMPTY;
5686       else if (center_element == EL_YAMYAM)
5687         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5688       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5689         Store[x][y] = element_info[center_element].content.e[xx][yy];
5690 #if 1
5691       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5692       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5693       // otherwise) -- FIX THIS !!!
5694       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5695         Store[x][y] = element_info[element].content.e[1][1];
5696 #else
5697       else if (!CAN_EXPLODE(element))
5698         Store[x][y] = element_info[element].content.e[1][1];
5699 #endif
5700       else
5701         Store[x][y] = EL_EMPTY;
5702
5703       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5704           center_element == EL_AMOEBA_TO_DIAMOND)
5705         Store2[x][y] = element;
5706
5707       Feld[x][y] = EL_EXPLOSION;
5708       GfxElement[x][y] = artwork_element;
5709
5710       ExplodePhase[x][y] = 1;
5711       ExplodeDelay[x][y] = last_phase;
5712
5713       Stop[x][y] = TRUE;
5714     }
5715
5716     if (center_element == EL_YAMYAM)
5717       game.yamyam_content_nr =
5718         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5719
5720     return;
5721   }
5722
5723   if (Stop[ex][ey])
5724     return;
5725
5726   x = ex;
5727   y = ey;
5728
5729   if (phase == 1)
5730     GfxFrame[x][y] = 0;         // restart explosion animation
5731
5732   last_phase = ExplodeDelay[x][y];
5733
5734   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5735
5736   // this can happen if the player leaves an explosion just in time
5737   if (GfxElement[x][y] == EL_UNDEFINED)
5738     GfxElement[x][y] = EL_EMPTY;
5739
5740   border_element = Store2[x][y];
5741   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5742     border_element = StorePlayer[x][y];
5743
5744   if (phase == element_info[border_element].ignition_delay ||
5745       phase == last_phase)
5746   {
5747     boolean border_explosion = FALSE;
5748
5749     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5750         !PLAYER_EXPLOSION_PROTECTED(x, y))
5751     {
5752       KillPlayerUnlessExplosionProtected(x, y);
5753       border_explosion = TRUE;
5754     }
5755     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5756     {
5757       Feld[x][y] = Store2[x][y];
5758       Store2[x][y] = 0;
5759       Bang(x, y);
5760       border_explosion = TRUE;
5761     }
5762     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5763     {
5764       AmoebeUmwandeln(x, y);
5765       Store2[x][y] = 0;
5766       border_explosion = TRUE;
5767     }
5768
5769     // if an element just explodes due to another explosion (chain-reaction),
5770     // do not immediately end the new explosion when it was the last frame of
5771     // the explosion (as it would be done in the following "if"-statement!)
5772     if (border_explosion && phase == last_phase)
5773       return;
5774   }
5775
5776   if (phase == last_phase)
5777   {
5778     int element;
5779
5780     element = Feld[x][y] = Store[x][y];
5781     Store[x][y] = Store2[x][y] = 0;
5782     GfxElement[x][y] = EL_UNDEFINED;
5783
5784     // player can escape from explosions and might therefore be still alive
5785     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5786         element <= EL_PLAYER_IS_EXPLODING_4)
5787     {
5788       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5789       int explosion_element = EL_PLAYER_1 + player_nr;
5790       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5791       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5792
5793       if (level.use_explosion_element[player_nr])
5794         explosion_element = level.explosion_element[player_nr];
5795
5796       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5797                     element_info[explosion_element].content.e[xx][yy]);
5798     }
5799
5800     // restore probably existing indestructible background element
5801     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5802       element = Feld[x][y] = Back[x][y];
5803     Back[x][y] = 0;
5804
5805     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5806     GfxDir[x][y] = MV_NONE;
5807     ChangeDelay[x][y] = 0;
5808     ChangePage[x][y] = -1;
5809
5810     CustomValue[x][y] = 0;
5811
5812     InitField_WithBug2(x, y, FALSE);
5813
5814     TEST_DrawLevelField(x, y);
5815
5816     TestIfElementTouchesCustomElement(x, y);
5817
5818     if (GFX_CRUMBLED(element))
5819       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5820
5821     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5822       StorePlayer[x][y] = 0;
5823
5824     if (ELEM_IS_PLAYER(element))
5825       RelocatePlayer(x, y, element);
5826   }
5827   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5828   {
5829     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5830     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5831
5832     if (phase == delay)
5833       TEST_DrawLevelFieldCrumbled(x, y);
5834
5835     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5836     {
5837       DrawLevelElement(x, y, Back[x][y]);
5838       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5839     }
5840     else if (IS_WALKABLE_UNDER(Back[x][y]))
5841     {
5842       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5843       DrawLevelElementThruMask(x, y, Back[x][y]);
5844     }
5845     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5846       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5847   }
5848 }
5849
5850 static void DynaExplode(int ex, int ey)
5851 {
5852   int i, j;
5853   int dynabomb_element = Feld[ex][ey];
5854   int dynabomb_size = 1;
5855   boolean dynabomb_xl = FALSE;
5856   struct PlayerInfo *player;
5857   static int xy[4][2] =
5858   {
5859     { 0, -1 },
5860     { -1, 0 },
5861     { +1, 0 },
5862     { 0, +1 }
5863   };
5864
5865   if (IS_ACTIVE_BOMB(dynabomb_element))
5866   {
5867     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5868     dynabomb_size = player->dynabomb_size;
5869     dynabomb_xl = player->dynabomb_xl;
5870     player->dynabombs_left++;
5871   }
5872
5873   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5874
5875   for (i = 0; i < NUM_DIRECTIONS; i++)
5876   {
5877     for (j = 1; j <= dynabomb_size; j++)
5878     {
5879       int x = ex + j * xy[i][0];
5880       int y = ey + j * xy[i][1];
5881       int element;
5882
5883       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5884         break;
5885
5886       element = Feld[x][y];
5887
5888       // do not restart explosions of fields with active bombs
5889       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5890         continue;
5891
5892       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5893
5894       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5895           !IS_DIGGABLE(element) && !dynabomb_xl)
5896         break;
5897     }
5898   }
5899 }
5900
5901 void Bang(int x, int y)
5902 {
5903   int element = MovingOrBlocked2Element(x, y);
5904   int explosion_type = EX_TYPE_NORMAL;
5905
5906   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5907   {
5908     struct PlayerInfo *player = PLAYERINFO(x, y);
5909
5910     element = Feld[x][y] = player->initial_element;
5911
5912     if (level.use_explosion_element[player->index_nr])
5913     {
5914       int explosion_element = level.explosion_element[player->index_nr];
5915
5916       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5917         explosion_type = EX_TYPE_CROSS;
5918       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5919         explosion_type = EX_TYPE_CENTER;
5920     }
5921   }
5922
5923   switch (element)
5924   {
5925     case EL_BUG:
5926     case EL_SPACESHIP:
5927     case EL_BD_BUTTERFLY:
5928     case EL_BD_FIREFLY:
5929     case EL_YAMYAM:
5930     case EL_DARK_YAMYAM:
5931     case EL_ROBOT:
5932     case EL_PACMAN:
5933     case EL_MOLE:
5934       RaiseScoreElement(element);
5935       break;
5936
5937     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5938     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5939     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5940     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5941     case EL_DYNABOMB_INCREASE_NUMBER:
5942     case EL_DYNABOMB_INCREASE_SIZE:
5943     case EL_DYNABOMB_INCREASE_POWER:
5944       explosion_type = EX_TYPE_DYNA;
5945       break;
5946
5947     case EL_DC_LANDMINE:
5948       explosion_type = EX_TYPE_CENTER;
5949       break;
5950
5951     case EL_PENGUIN:
5952     case EL_LAMP:
5953     case EL_LAMP_ACTIVE:
5954     case EL_AMOEBA_TO_DIAMOND:
5955       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5956         explosion_type = EX_TYPE_CENTER;
5957       break;
5958
5959     default:
5960       if (element_info[element].explosion_type == EXPLODES_CROSS)
5961         explosion_type = EX_TYPE_CROSS;
5962       else if (element_info[element].explosion_type == EXPLODES_1X1)
5963         explosion_type = EX_TYPE_CENTER;
5964       break;
5965   }
5966
5967   if (explosion_type == EX_TYPE_DYNA)
5968     DynaExplode(x, y);
5969   else
5970     Explode(x, y, EX_PHASE_START, explosion_type);
5971
5972   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5973 }
5974
5975 static void SplashAcid(int x, int y)
5976 {
5977   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5978       (!IN_LEV_FIELD(x - 1, y - 2) ||
5979        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5980     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5981
5982   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5983       (!IN_LEV_FIELD(x + 1, y - 2) ||
5984        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5985     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5986
5987   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5988 }
5989
5990 static void InitBeltMovement(void)
5991 {
5992   static int belt_base_element[4] =
5993   {
5994     EL_CONVEYOR_BELT_1_LEFT,
5995     EL_CONVEYOR_BELT_2_LEFT,
5996     EL_CONVEYOR_BELT_3_LEFT,
5997     EL_CONVEYOR_BELT_4_LEFT
5998   };
5999   static int belt_base_active_element[4] =
6000   {
6001     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6002     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6003     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6004     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6005   };
6006
6007   int x, y, i, j;
6008
6009   // set frame order for belt animation graphic according to belt direction
6010   for (i = 0; i < NUM_BELTS; i++)
6011   {
6012     int belt_nr = i;
6013
6014     for (j = 0; j < NUM_BELT_PARTS; j++)
6015     {
6016       int element = belt_base_active_element[belt_nr] + j;
6017       int graphic_1 = el2img(element);
6018       int graphic_2 = el2panelimg(element);
6019
6020       if (game.belt_dir[i] == MV_LEFT)
6021       {
6022         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6023         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6024       }
6025       else
6026       {
6027         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6028         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6029       }
6030     }
6031   }
6032
6033   SCAN_PLAYFIELD(x, y)
6034   {
6035     int element = Feld[x][y];
6036
6037     for (i = 0; i < NUM_BELTS; i++)
6038     {
6039       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6040       {
6041         int e_belt_nr = getBeltNrFromBeltElement(element);
6042         int belt_nr = i;
6043
6044         if (e_belt_nr == belt_nr)
6045         {
6046           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6047
6048           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6049         }
6050       }
6051     }
6052   }
6053 }
6054
6055 static void ToggleBeltSwitch(int x, int y)
6056 {
6057   static int belt_base_element[4] =
6058   {
6059     EL_CONVEYOR_BELT_1_LEFT,
6060     EL_CONVEYOR_BELT_2_LEFT,
6061     EL_CONVEYOR_BELT_3_LEFT,
6062     EL_CONVEYOR_BELT_4_LEFT
6063   };
6064   static int belt_base_active_element[4] =
6065   {
6066     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6067     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6068     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6069     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6070   };
6071   static int belt_base_switch_element[4] =
6072   {
6073     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6074     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6075     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6076     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6077   };
6078   static int belt_move_dir[4] =
6079   {
6080     MV_LEFT,
6081     MV_NONE,
6082     MV_RIGHT,
6083     MV_NONE,
6084   };
6085
6086   int element = Feld[x][y];
6087   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6088   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6089   int belt_dir = belt_move_dir[belt_dir_nr];
6090   int xx, yy, i;
6091
6092   if (!IS_BELT_SWITCH(element))
6093     return;
6094
6095   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6096   game.belt_dir[belt_nr] = belt_dir;
6097
6098   if (belt_dir_nr == 3)
6099     belt_dir_nr = 1;
6100
6101   // set frame order for belt animation graphic according to belt direction
6102   for (i = 0; i < NUM_BELT_PARTS; i++)
6103   {
6104     int element = belt_base_active_element[belt_nr] + i;
6105     int graphic_1 = el2img(element);
6106     int graphic_2 = el2panelimg(element);
6107
6108     if (belt_dir == MV_LEFT)
6109     {
6110       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6111       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6112     }
6113     else
6114     {
6115       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6116       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6117     }
6118   }
6119
6120   SCAN_PLAYFIELD(xx, yy)
6121   {
6122     int element = Feld[xx][yy];
6123
6124     if (IS_BELT_SWITCH(element))
6125     {
6126       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6127
6128       if (e_belt_nr == belt_nr)
6129       {
6130         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6131         TEST_DrawLevelField(xx, yy);
6132       }
6133     }
6134     else if (IS_BELT(element) && belt_dir != MV_NONE)
6135     {
6136       int e_belt_nr = getBeltNrFromBeltElement(element);
6137
6138       if (e_belt_nr == belt_nr)
6139       {
6140         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6141
6142         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6143         TEST_DrawLevelField(xx, yy);
6144       }
6145     }
6146     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6147     {
6148       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6149
6150       if (e_belt_nr == belt_nr)
6151       {
6152         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6153
6154         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6155         TEST_DrawLevelField(xx, yy);
6156       }
6157     }
6158   }
6159 }
6160
6161 static void ToggleSwitchgateSwitch(int x, int y)
6162 {
6163   int xx, yy;
6164
6165   game.switchgate_pos = !game.switchgate_pos;
6166
6167   SCAN_PLAYFIELD(xx, yy)
6168   {
6169     int element = Feld[xx][yy];
6170
6171     if (element == EL_SWITCHGATE_SWITCH_UP)
6172     {
6173       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6174       TEST_DrawLevelField(xx, yy);
6175     }
6176     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6177     {
6178       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6179       TEST_DrawLevelField(xx, yy);
6180     }
6181     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6182     {
6183       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6184       TEST_DrawLevelField(xx, yy);
6185     }
6186     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6187     {
6188       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6189       TEST_DrawLevelField(xx, yy);
6190     }
6191     else if (element == EL_SWITCHGATE_OPEN ||
6192              element == EL_SWITCHGATE_OPENING)
6193     {
6194       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6195
6196       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6197     }
6198     else if (element == EL_SWITCHGATE_CLOSED ||
6199              element == EL_SWITCHGATE_CLOSING)
6200     {
6201       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6202
6203       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6204     }
6205   }
6206 }
6207
6208 static int getInvisibleActiveFromInvisibleElement(int element)
6209 {
6210   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6211           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6212           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6213           element);
6214 }
6215
6216 static int getInvisibleFromInvisibleActiveElement(int element)
6217 {
6218   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6219           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6220           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6221           element);
6222 }
6223
6224 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6225 {
6226   int x, y;
6227
6228   SCAN_PLAYFIELD(x, y)
6229   {
6230     int element = Feld[x][y];
6231
6232     if (element == EL_LIGHT_SWITCH &&
6233         game.light_time_left > 0)
6234     {
6235       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6236       TEST_DrawLevelField(x, y);
6237     }
6238     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6239              game.light_time_left == 0)
6240     {
6241       Feld[x][y] = EL_LIGHT_SWITCH;
6242       TEST_DrawLevelField(x, y);
6243     }
6244     else if (element == EL_EMC_DRIPPER &&
6245              game.light_time_left > 0)
6246     {
6247       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6248       TEST_DrawLevelField(x, y);
6249     }
6250     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6251              game.light_time_left == 0)
6252     {
6253       Feld[x][y] = EL_EMC_DRIPPER;
6254       TEST_DrawLevelField(x, y);
6255     }
6256     else if (element == EL_INVISIBLE_STEELWALL ||
6257              element == EL_INVISIBLE_WALL ||
6258              element == EL_INVISIBLE_SAND)
6259     {
6260       if (game.light_time_left > 0)
6261         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6262
6263       TEST_DrawLevelField(x, y);
6264
6265       // uncrumble neighbour fields, if needed
6266       if (element == EL_INVISIBLE_SAND)
6267         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6268     }
6269     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6270              element == EL_INVISIBLE_WALL_ACTIVE ||
6271              element == EL_INVISIBLE_SAND_ACTIVE)
6272     {
6273       if (game.light_time_left == 0)
6274         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6275
6276       TEST_DrawLevelField(x, y);
6277
6278       // re-crumble neighbour fields, if needed
6279       if (element == EL_INVISIBLE_SAND)
6280         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6281     }
6282   }
6283 }
6284
6285 static void RedrawAllInvisibleElementsForLenses(void)
6286 {
6287   int x, y;
6288
6289   SCAN_PLAYFIELD(x, y)
6290   {
6291     int element = Feld[x][y];
6292
6293     if (element == EL_EMC_DRIPPER &&
6294         game.lenses_time_left > 0)
6295     {
6296       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6297       TEST_DrawLevelField(x, y);
6298     }
6299     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6300              game.lenses_time_left == 0)
6301     {
6302       Feld[x][y] = EL_EMC_DRIPPER;
6303       TEST_DrawLevelField(x, y);
6304     }
6305     else if (element == EL_INVISIBLE_STEELWALL ||
6306              element == EL_INVISIBLE_WALL ||
6307              element == EL_INVISIBLE_SAND)
6308     {
6309       if (game.lenses_time_left > 0)
6310         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6311
6312       TEST_DrawLevelField(x, y);
6313
6314       // uncrumble neighbour fields, if needed
6315       if (element == EL_INVISIBLE_SAND)
6316         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6317     }
6318     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6319              element == EL_INVISIBLE_WALL_ACTIVE ||
6320              element == EL_INVISIBLE_SAND_ACTIVE)
6321     {
6322       if (game.lenses_time_left == 0)
6323         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6324
6325       TEST_DrawLevelField(x, y);
6326
6327       // re-crumble neighbour fields, if needed
6328       if (element == EL_INVISIBLE_SAND)
6329         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6330     }
6331   }
6332 }
6333
6334 static void RedrawAllInvisibleElementsForMagnifier(void)
6335 {
6336   int x, y;
6337
6338   SCAN_PLAYFIELD(x, y)
6339   {
6340     int element = Feld[x][y];
6341
6342     if (element == EL_EMC_FAKE_GRASS &&
6343         game.magnify_time_left > 0)
6344     {
6345       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6346       TEST_DrawLevelField(x, y);
6347     }
6348     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6349              game.magnify_time_left == 0)
6350     {
6351       Feld[x][y] = EL_EMC_FAKE_GRASS;
6352       TEST_DrawLevelField(x, y);
6353     }
6354     else if (IS_GATE_GRAY(element) &&
6355              game.magnify_time_left > 0)
6356     {
6357       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6358                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6359                     IS_EM_GATE_GRAY(element) ?
6360                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6361                     IS_EMC_GATE_GRAY(element) ?
6362                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6363                     IS_DC_GATE_GRAY(element) ?
6364                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6365                     element);
6366       TEST_DrawLevelField(x, y);
6367     }
6368     else if (IS_GATE_GRAY_ACTIVE(element) &&
6369              game.magnify_time_left == 0)
6370     {
6371       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6372                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6373                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6374                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6375                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6376                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6377                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6378                     EL_DC_GATE_WHITE_GRAY :
6379                     element);
6380       TEST_DrawLevelField(x, y);
6381     }
6382   }
6383 }
6384
6385 static void ToggleLightSwitch(int x, int y)
6386 {
6387   int element = Feld[x][y];
6388
6389   game.light_time_left =
6390     (element == EL_LIGHT_SWITCH ?
6391      level.time_light * FRAMES_PER_SECOND : 0);
6392
6393   RedrawAllLightSwitchesAndInvisibleElements();
6394 }
6395
6396 static void ActivateTimegateSwitch(int x, int y)
6397 {
6398   int xx, yy;
6399
6400   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6401
6402   SCAN_PLAYFIELD(xx, yy)
6403   {
6404     int element = Feld[xx][yy];
6405
6406     if (element == EL_TIMEGATE_CLOSED ||
6407         element == EL_TIMEGATE_CLOSING)
6408     {
6409       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6410       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6411     }
6412
6413     /*
6414     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6415     {
6416       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6417       TEST_DrawLevelField(xx, yy);
6418     }
6419     */
6420
6421   }
6422
6423   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6424                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6425 }
6426
6427 static void Impact(int x, int y)
6428 {
6429   boolean last_line = (y == lev_fieldy - 1);
6430   boolean object_hit = FALSE;
6431   boolean impact = (last_line || object_hit);
6432   int element = Feld[x][y];
6433   int smashed = EL_STEELWALL;
6434
6435   if (!last_line)       // check if element below was hit
6436   {
6437     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6438       return;
6439
6440     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6441                                          MovDir[x][y + 1] != MV_DOWN ||
6442                                          MovPos[x][y + 1] <= TILEY / 2));
6443
6444     // do not smash moving elements that left the smashed field in time
6445     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6446         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6447       object_hit = FALSE;
6448
6449 #if USE_QUICKSAND_IMPACT_BUGFIX
6450     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6451     {
6452       RemoveMovingField(x, y + 1);
6453       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6454       Feld[x][y + 2] = EL_ROCK;
6455       TEST_DrawLevelField(x, y + 2);
6456
6457       object_hit = TRUE;
6458     }
6459
6460     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6461     {
6462       RemoveMovingField(x, y + 1);
6463       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6464       Feld[x][y + 2] = EL_ROCK;
6465       TEST_DrawLevelField(x, y + 2);
6466
6467       object_hit = TRUE;
6468     }
6469 #endif
6470
6471     if (object_hit)
6472       smashed = MovingOrBlocked2Element(x, y + 1);
6473
6474     impact = (last_line || object_hit);
6475   }
6476
6477   if (!last_line && smashed == EL_ACID) // element falls into acid
6478   {
6479     SplashAcid(x, y + 1);
6480     return;
6481   }
6482
6483   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6484   // only reset graphic animation if graphic really changes after impact
6485   if (impact &&
6486       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6487   {
6488     ResetGfxAnimation(x, y);
6489     TEST_DrawLevelField(x, y);
6490   }
6491
6492   if (impact && CAN_EXPLODE_IMPACT(element))
6493   {
6494     Bang(x, y);
6495     return;
6496   }
6497   else if (impact && element == EL_PEARL &&
6498            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6499   {
6500     ResetGfxAnimation(x, y);
6501
6502     Feld[x][y] = EL_PEARL_BREAKING;
6503     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6504     return;
6505   }
6506   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6507   {
6508     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6509
6510     return;
6511   }
6512
6513   if (impact && element == EL_AMOEBA_DROP)
6514   {
6515     if (object_hit && IS_PLAYER(x, y + 1))
6516       KillPlayerUnlessEnemyProtected(x, y + 1);
6517     else if (object_hit && smashed == EL_PENGUIN)
6518       Bang(x, y + 1);
6519     else
6520     {
6521       Feld[x][y] = EL_AMOEBA_GROWING;
6522       Store[x][y] = EL_AMOEBA_WET;
6523
6524       ResetRandomAnimationValue(x, y);
6525     }
6526     return;
6527   }
6528
6529   if (object_hit)               // check which object was hit
6530   {
6531     if ((CAN_PASS_MAGIC_WALL(element) && 
6532          (smashed == EL_MAGIC_WALL ||
6533           smashed == EL_BD_MAGIC_WALL)) ||
6534         (CAN_PASS_DC_MAGIC_WALL(element) &&
6535          smashed == EL_DC_MAGIC_WALL))
6536     {
6537       int xx, yy;
6538       int activated_magic_wall =
6539         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6540          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6541          EL_DC_MAGIC_WALL_ACTIVE);
6542
6543       // activate magic wall / mill
6544       SCAN_PLAYFIELD(xx, yy)
6545       {
6546         if (Feld[xx][yy] == smashed)
6547           Feld[xx][yy] = activated_magic_wall;
6548       }
6549
6550       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6551       game.magic_wall_active = TRUE;
6552
6553       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6554                             SND_MAGIC_WALL_ACTIVATING :
6555                             smashed == EL_BD_MAGIC_WALL ?
6556                             SND_BD_MAGIC_WALL_ACTIVATING :
6557                             SND_DC_MAGIC_WALL_ACTIVATING));
6558     }
6559
6560     if (IS_PLAYER(x, y + 1))
6561     {
6562       if (CAN_SMASH_PLAYER(element))
6563       {
6564         KillPlayerUnlessEnemyProtected(x, y + 1);
6565         return;
6566       }
6567     }
6568     else if (smashed == EL_PENGUIN)
6569     {
6570       if (CAN_SMASH_PLAYER(element))
6571       {
6572         Bang(x, y + 1);
6573         return;
6574       }
6575     }
6576     else if (element == EL_BD_DIAMOND)
6577     {
6578       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6579       {
6580         Bang(x, y + 1);
6581         return;
6582       }
6583     }
6584     else if (((element == EL_SP_INFOTRON ||
6585                element == EL_SP_ZONK) &&
6586               (smashed == EL_SP_SNIKSNAK ||
6587                smashed == EL_SP_ELECTRON ||
6588                smashed == EL_SP_DISK_ORANGE)) ||
6589              (element == EL_SP_INFOTRON &&
6590               smashed == EL_SP_DISK_YELLOW))
6591     {
6592       Bang(x, y + 1);
6593       return;
6594     }
6595     else if (CAN_SMASH_EVERYTHING(element))
6596     {
6597       if (IS_CLASSIC_ENEMY(smashed) ||
6598           CAN_EXPLODE_SMASHED(smashed))
6599       {
6600         Bang(x, y + 1);
6601         return;
6602       }
6603       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6604       {
6605         if (smashed == EL_LAMP ||
6606             smashed == EL_LAMP_ACTIVE)
6607         {
6608           Bang(x, y + 1);
6609           return;
6610         }
6611         else if (smashed == EL_NUT)
6612         {
6613           Feld[x][y + 1] = EL_NUT_BREAKING;
6614           PlayLevelSound(x, y, SND_NUT_BREAKING);
6615           RaiseScoreElement(EL_NUT);
6616           return;
6617         }
6618         else if (smashed == EL_PEARL)
6619         {
6620           ResetGfxAnimation(x, y);
6621
6622           Feld[x][y + 1] = EL_PEARL_BREAKING;
6623           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6624           return;
6625         }
6626         else if (smashed == EL_DIAMOND)
6627         {
6628           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6629           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6630           return;
6631         }
6632         else if (IS_BELT_SWITCH(smashed))
6633         {
6634           ToggleBeltSwitch(x, y + 1);
6635         }
6636         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6637                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6638                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6639                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6640         {
6641           ToggleSwitchgateSwitch(x, y + 1);
6642         }
6643         else if (smashed == EL_LIGHT_SWITCH ||
6644                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6645         {
6646           ToggleLightSwitch(x, y + 1);
6647         }
6648         else
6649         {
6650           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6651
6652           CheckElementChangeBySide(x, y + 1, smashed, element,
6653                                    CE_SWITCHED, CH_SIDE_TOP);
6654           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6655                                             CH_SIDE_TOP);
6656         }
6657       }
6658       else
6659       {
6660         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6661       }
6662     }
6663   }
6664
6665   // play sound of magic wall / mill
6666   if (!last_line &&
6667       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6668        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6669        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6670   {
6671     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6672       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6673     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6674       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6675     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6676       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6677
6678     return;
6679   }
6680
6681   // play sound of object that hits the ground
6682   if (last_line || object_hit)
6683     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6684 }
6685
6686 static void TurnRoundExt(int x, int y)
6687 {
6688   static struct
6689   {
6690     int dx, dy;
6691   } move_xy[] =
6692   {
6693     {  0,  0 },
6694     { -1,  0 },
6695     { +1,  0 },
6696     {  0,  0 },
6697     {  0, -1 },
6698     {  0,  0 }, { 0, 0 }, { 0, 0 },
6699     {  0, +1 }
6700   };
6701   static struct
6702   {
6703     int left, right, back;
6704   } turn[] =
6705   {
6706     { 0,        0,              0        },
6707     { MV_DOWN,  MV_UP,          MV_RIGHT },
6708     { MV_UP,    MV_DOWN,        MV_LEFT  },
6709     { 0,        0,              0        },
6710     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6711     { 0,        0,              0        },
6712     { 0,        0,              0        },
6713     { 0,        0,              0        },
6714     { MV_RIGHT, MV_LEFT,        MV_UP    }
6715   };
6716
6717   int element = Feld[x][y];
6718   int move_pattern = element_info[element].move_pattern;
6719
6720   int old_move_dir = MovDir[x][y];
6721   int left_dir  = turn[old_move_dir].left;
6722   int right_dir = turn[old_move_dir].right;
6723   int back_dir  = turn[old_move_dir].back;
6724
6725   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6726   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6727   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6728   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6729
6730   int left_x  = x + left_dx,  left_y  = y + left_dy;
6731   int right_x = x + right_dx, right_y = y + right_dy;
6732   int move_x  = x + move_dx,  move_y  = y + move_dy;
6733
6734   int xx, yy;
6735
6736   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6737   {
6738     TestIfBadThingTouchesOtherBadThing(x, y);
6739
6740     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6741       MovDir[x][y] = right_dir;
6742     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6743       MovDir[x][y] = left_dir;
6744
6745     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6746       MovDelay[x][y] = 9;
6747     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6748       MovDelay[x][y] = 1;
6749   }
6750   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6751   {
6752     TestIfBadThingTouchesOtherBadThing(x, y);
6753
6754     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6755       MovDir[x][y] = left_dir;
6756     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6757       MovDir[x][y] = right_dir;
6758
6759     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6760       MovDelay[x][y] = 9;
6761     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6762       MovDelay[x][y] = 1;
6763   }
6764   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6765   {
6766     TestIfBadThingTouchesOtherBadThing(x, y);
6767
6768     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6769       MovDir[x][y] = left_dir;
6770     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6771       MovDir[x][y] = right_dir;
6772
6773     if (MovDir[x][y] != old_move_dir)
6774       MovDelay[x][y] = 9;
6775   }
6776   else if (element == EL_YAMYAM)
6777   {
6778     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6779     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6780
6781     if (can_turn_left && can_turn_right)
6782       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6783     else if (can_turn_left)
6784       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6785     else if (can_turn_right)
6786       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6787     else
6788       MovDir[x][y] = back_dir;
6789
6790     MovDelay[x][y] = 16 + 16 * RND(3);
6791   }
6792   else if (element == EL_DARK_YAMYAM)
6793   {
6794     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6795                                                          left_x, left_y);
6796     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6797                                                          right_x, right_y);
6798
6799     if (can_turn_left && can_turn_right)
6800       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6801     else if (can_turn_left)
6802       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6803     else if (can_turn_right)
6804       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6805     else
6806       MovDir[x][y] = back_dir;
6807
6808     MovDelay[x][y] = 16 + 16 * RND(3);
6809   }
6810   else if (element == EL_PACMAN)
6811   {
6812     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6813     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6814
6815     if (can_turn_left && can_turn_right)
6816       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6817     else if (can_turn_left)
6818       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6819     else if (can_turn_right)
6820       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6821     else
6822       MovDir[x][y] = back_dir;
6823
6824     MovDelay[x][y] = 6 + RND(40);
6825   }
6826   else if (element == EL_PIG)
6827   {
6828     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6829     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6830     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6831     boolean should_turn_left, should_turn_right, should_move_on;
6832     int rnd_value = 24;
6833     int rnd = RND(rnd_value);
6834
6835     should_turn_left = (can_turn_left &&
6836                         (!can_move_on ||
6837                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6838                                                    y + back_dy + left_dy)));
6839     should_turn_right = (can_turn_right &&
6840                          (!can_move_on ||
6841                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6842                                                     y + back_dy + right_dy)));
6843     should_move_on = (can_move_on &&
6844                       (!can_turn_left ||
6845                        !can_turn_right ||
6846                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6847                                                  y + move_dy + left_dy) ||
6848                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6849                                                  y + move_dy + right_dy)));
6850
6851     if (should_turn_left || should_turn_right || should_move_on)
6852     {
6853       if (should_turn_left && should_turn_right && should_move_on)
6854         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6855                         rnd < 2 * rnd_value / 3 ? right_dir :
6856                         old_move_dir);
6857       else if (should_turn_left && should_turn_right)
6858         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6859       else if (should_turn_left && should_move_on)
6860         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6861       else if (should_turn_right && should_move_on)
6862         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6863       else if (should_turn_left)
6864         MovDir[x][y] = left_dir;
6865       else if (should_turn_right)
6866         MovDir[x][y] = right_dir;
6867       else if (should_move_on)
6868         MovDir[x][y] = old_move_dir;
6869     }
6870     else if (can_move_on && rnd > rnd_value / 8)
6871       MovDir[x][y] = old_move_dir;
6872     else if (can_turn_left && can_turn_right)
6873       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6874     else if (can_turn_left && rnd > rnd_value / 8)
6875       MovDir[x][y] = left_dir;
6876     else if (can_turn_right && rnd > rnd_value/8)
6877       MovDir[x][y] = right_dir;
6878     else
6879       MovDir[x][y] = back_dir;
6880
6881     xx = x + move_xy[MovDir[x][y]].dx;
6882     yy = y + move_xy[MovDir[x][y]].dy;
6883
6884     if (!IN_LEV_FIELD(xx, yy) ||
6885         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6886       MovDir[x][y] = old_move_dir;
6887
6888     MovDelay[x][y] = 0;
6889   }
6890   else if (element == EL_DRAGON)
6891   {
6892     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6893     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6894     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6895     int rnd_value = 24;
6896     int rnd = RND(rnd_value);
6897
6898     if (can_move_on && rnd > rnd_value / 8)
6899       MovDir[x][y] = old_move_dir;
6900     else if (can_turn_left && can_turn_right)
6901       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6902     else if (can_turn_left && rnd > rnd_value / 8)
6903       MovDir[x][y] = left_dir;
6904     else if (can_turn_right && rnd > rnd_value / 8)
6905       MovDir[x][y] = right_dir;
6906     else
6907       MovDir[x][y] = back_dir;
6908
6909     xx = x + move_xy[MovDir[x][y]].dx;
6910     yy = y + move_xy[MovDir[x][y]].dy;
6911
6912     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6913       MovDir[x][y] = old_move_dir;
6914
6915     MovDelay[x][y] = 0;
6916   }
6917   else if (element == EL_MOLE)
6918   {
6919     boolean can_move_on =
6920       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6921                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6922                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6923     if (!can_move_on)
6924     {
6925       boolean can_turn_left =
6926         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6927                               IS_AMOEBOID(Feld[left_x][left_y])));
6928
6929       boolean can_turn_right =
6930         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6931                               IS_AMOEBOID(Feld[right_x][right_y])));
6932
6933       if (can_turn_left && can_turn_right)
6934         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6935       else if (can_turn_left)
6936         MovDir[x][y] = left_dir;
6937       else
6938         MovDir[x][y] = right_dir;
6939     }
6940
6941     if (MovDir[x][y] != old_move_dir)
6942       MovDelay[x][y] = 9;
6943   }
6944   else if (element == EL_BALLOON)
6945   {
6946     MovDir[x][y] = game.wind_direction;
6947     MovDelay[x][y] = 0;
6948   }
6949   else if (element == EL_SPRING)
6950   {
6951     if (MovDir[x][y] & MV_HORIZONTAL)
6952     {
6953       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6954           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6955       {
6956         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6957         ResetGfxAnimation(move_x, move_y);
6958         TEST_DrawLevelField(move_x, move_y);
6959
6960         MovDir[x][y] = back_dir;
6961       }
6962       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6963                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6964         MovDir[x][y] = MV_NONE;
6965     }
6966
6967     MovDelay[x][y] = 0;
6968   }
6969   else if (element == EL_ROBOT ||
6970            element == EL_SATELLITE ||
6971            element == EL_PENGUIN ||
6972            element == EL_EMC_ANDROID)
6973   {
6974     int attr_x = -1, attr_y = -1;
6975
6976     if (game.all_players_gone)
6977     {
6978       attr_x = game.exit_x;
6979       attr_y = game.exit_y;
6980     }
6981     else
6982     {
6983       int i;
6984
6985       for (i = 0; i < MAX_PLAYERS; i++)
6986       {
6987         struct PlayerInfo *player = &stored_player[i];
6988         int jx = player->jx, jy = player->jy;
6989
6990         if (!player->active)
6991           continue;
6992
6993         if (attr_x == -1 ||
6994             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6995         {
6996           attr_x = jx;
6997           attr_y = jy;
6998         }
6999       }
7000     }
7001
7002     if (element == EL_ROBOT &&
7003         game.robot_wheel_x >= 0 &&
7004         game.robot_wheel_y >= 0 &&
7005         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7006          game.engine_version < VERSION_IDENT(3,1,0,0)))
7007     {
7008       attr_x = game.robot_wheel_x;
7009       attr_y = game.robot_wheel_y;
7010     }
7011
7012     if (element == EL_PENGUIN)
7013     {
7014       int i;
7015       static int xy[4][2] =
7016       {
7017         { 0, -1 },
7018         { -1, 0 },
7019         { +1, 0 },
7020         { 0, +1 }
7021       };
7022
7023       for (i = 0; i < NUM_DIRECTIONS; i++)
7024       {
7025         int ex = x + xy[i][0];
7026         int ey = y + xy[i][1];
7027
7028         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7029                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7030                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7031                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7032         {
7033           attr_x = ex;
7034           attr_y = ey;
7035           break;
7036         }
7037       }
7038     }
7039
7040     MovDir[x][y] = MV_NONE;
7041     if (attr_x < x)
7042       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7043     else if (attr_x > x)
7044       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7045     if (attr_y < y)
7046       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7047     else if (attr_y > y)
7048       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7049
7050     if (element == EL_ROBOT)
7051     {
7052       int newx, newy;
7053
7054       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7055         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7056       Moving2Blocked(x, y, &newx, &newy);
7057
7058       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7059         MovDelay[x][y] = 8 + 8 * !RND(3);
7060       else
7061         MovDelay[x][y] = 16;
7062     }
7063     else if (element == EL_PENGUIN)
7064     {
7065       int newx, newy;
7066
7067       MovDelay[x][y] = 1;
7068
7069       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7070       {
7071         boolean first_horiz = RND(2);
7072         int new_move_dir = MovDir[x][y];
7073
7074         MovDir[x][y] =
7075           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7076         Moving2Blocked(x, y, &newx, &newy);
7077
7078         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7079           return;
7080
7081         MovDir[x][y] =
7082           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7083         Moving2Blocked(x, y, &newx, &newy);
7084
7085         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7086           return;
7087
7088         MovDir[x][y] = old_move_dir;
7089         return;
7090       }
7091     }
7092     else if (element == EL_SATELLITE)
7093     {
7094       int newx, newy;
7095
7096       MovDelay[x][y] = 1;
7097
7098       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7099       {
7100         boolean first_horiz = RND(2);
7101         int new_move_dir = MovDir[x][y];
7102
7103         MovDir[x][y] =
7104           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7105         Moving2Blocked(x, y, &newx, &newy);
7106
7107         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7108           return;
7109
7110         MovDir[x][y] =
7111           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7112         Moving2Blocked(x, y, &newx, &newy);
7113
7114         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7115           return;
7116
7117         MovDir[x][y] = old_move_dir;
7118         return;
7119       }
7120     }
7121     else if (element == EL_EMC_ANDROID)
7122     {
7123       static int check_pos[16] =
7124       {
7125         -1,             //  0 => (invalid)
7126         7,              //  1 => MV_LEFT
7127         3,              //  2 => MV_RIGHT
7128         -1,             //  3 => (invalid)
7129         1,              //  4 =>            MV_UP
7130         0,              //  5 => MV_LEFT  | MV_UP
7131         2,              //  6 => MV_RIGHT | MV_UP
7132         -1,             //  7 => (invalid)
7133         5,              //  8 =>            MV_DOWN
7134         6,              //  9 => MV_LEFT  | MV_DOWN
7135         4,              // 10 => MV_RIGHT | MV_DOWN
7136         -1,             // 11 => (invalid)
7137         -1,             // 12 => (invalid)
7138         -1,             // 13 => (invalid)
7139         -1,             // 14 => (invalid)
7140         -1,             // 15 => (invalid)
7141       };
7142       static struct
7143       {
7144         int dx, dy;
7145         int dir;
7146       } check_xy[8] =
7147       {
7148         { -1, -1,       MV_LEFT  | MV_UP   },
7149         {  0, -1,                  MV_UP   },
7150         { +1, -1,       MV_RIGHT | MV_UP   },
7151         { +1,  0,       MV_RIGHT           },
7152         { +1, +1,       MV_RIGHT | MV_DOWN },
7153         {  0, +1,                  MV_DOWN },
7154         { -1, +1,       MV_LEFT  | MV_DOWN },
7155         { -1,  0,       MV_LEFT            },
7156       };
7157       int start_pos, check_order;
7158       boolean can_clone = FALSE;
7159       int i;
7160
7161       // check if there is any free field around current position
7162       for (i = 0; i < 8; i++)
7163       {
7164         int newx = x + check_xy[i].dx;
7165         int newy = y + check_xy[i].dy;
7166
7167         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7168         {
7169           can_clone = TRUE;
7170
7171           break;
7172         }
7173       }
7174
7175       if (can_clone)            // randomly find an element to clone
7176       {
7177         can_clone = FALSE;
7178
7179         start_pos = check_pos[RND(8)];
7180         check_order = (RND(2) ? -1 : +1);
7181
7182         for (i = 0; i < 8; i++)
7183         {
7184           int pos_raw = start_pos + i * check_order;
7185           int pos = (pos_raw + 8) % 8;
7186           int newx = x + check_xy[pos].dx;
7187           int newy = y + check_xy[pos].dy;
7188
7189           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7190           {
7191             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7192             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7193
7194             Store[x][y] = Feld[newx][newy];
7195
7196             can_clone = TRUE;
7197
7198             break;
7199           }
7200         }
7201       }
7202
7203       if (can_clone)            // randomly find a direction to move
7204       {
7205         can_clone = FALSE;
7206
7207         start_pos = check_pos[RND(8)];
7208         check_order = (RND(2) ? -1 : +1);
7209
7210         for (i = 0; i < 8; i++)
7211         {
7212           int pos_raw = start_pos + i * check_order;
7213           int pos = (pos_raw + 8) % 8;
7214           int newx = x + check_xy[pos].dx;
7215           int newy = y + check_xy[pos].dy;
7216           int new_move_dir = check_xy[pos].dir;
7217
7218           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7219           {
7220             MovDir[x][y] = new_move_dir;
7221             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7222
7223             can_clone = TRUE;
7224
7225             break;
7226           }
7227         }
7228       }
7229
7230       if (can_clone)            // cloning and moving successful
7231         return;
7232
7233       // cannot clone -- try to move towards player
7234
7235       start_pos = check_pos[MovDir[x][y] & 0x0f];
7236       check_order = (RND(2) ? -1 : +1);
7237
7238       for (i = 0; i < 3; i++)
7239       {
7240         // first check start_pos, then previous/next or (next/previous) pos
7241         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7242         int pos = (pos_raw + 8) % 8;
7243         int newx = x + check_xy[pos].dx;
7244         int newy = y + check_xy[pos].dy;
7245         int new_move_dir = check_xy[pos].dir;
7246
7247         if (IS_PLAYER(newx, newy))
7248           break;
7249
7250         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7251         {
7252           MovDir[x][y] = new_move_dir;
7253           MovDelay[x][y] = level.android_move_time * 8 + 1;
7254
7255           break;
7256         }
7257       }
7258     }
7259   }
7260   else if (move_pattern == MV_TURNING_LEFT ||
7261            move_pattern == MV_TURNING_RIGHT ||
7262            move_pattern == MV_TURNING_LEFT_RIGHT ||
7263            move_pattern == MV_TURNING_RIGHT_LEFT ||
7264            move_pattern == MV_TURNING_RANDOM ||
7265            move_pattern == MV_ALL_DIRECTIONS)
7266   {
7267     boolean can_turn_left =
7268       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7269     boolean can_turn_right =
7270       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7271
7272     if (element_info[element].move_stepsize == 0)       // "not moving"
7273       return;
7274
7275     if (move_pattern == MV_TURNING_LEFT)
7276       MovDir[x][y] = left_dir;
7277     else if (move_pattern == MV_TURNING_RIGHT)
7278       MovDir[x][y] = right_dir;
7279     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7280       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7281     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7282       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7283     else if (move_pattern == MV_TURNING_RANDOM)
7284       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7285                       can_turn_right && !can_turn_left ? right_dir :
7286                       RND(2) ? left_dir : right_dir);
7287     else if (can_turn_left && can_turn_right)
7288       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7289     else if (can_turn_left)
7290       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7291     else if (can_turn_right)
7292       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7293     else
7294       MovDir[x][y] = back_dir;
7295
7296     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7297   }
7298   else if (move_pattern == MV_HORIZONTAL ||
7299            move_pattern == MV_VERTICAL)
7300   {
7301     if (move_pattern & old_move_dir)
7302       MovDir[x][y] = back_dir;
7303     else if (move_pattern == MV_HORIZONTAL)
7304       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7305     else if (move_pattern == MV_VERTICAL)
7306       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7307
7308     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7309   }
7310   else if (move_pattern & MV_ANY_DIRECTION)
7311   {
7312     MovDir[x][y] = move_pattern;
7313     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7314   }
7315   else if (move_pattern & MV_WIND_DIRECTION)
7316   {
7317     MovDir[x][y] = game.wind_direction;
7318     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7319   }
7320   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7321   {
7322     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7323       MovDir[x][y] = left_dir;
7324     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7325       MovDir[x][y] = right_dir;
7326
7327     if (MovDir[x][y] != old_move_dir)
7328       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7329   }
7330   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7331   {
7332     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7333       MovDir[x][y] = right_dir;
7334     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7335       MovDir[x][y] = left_dir;
7336
7337     if (MovDir[x][y] != old_move_dir)
7338       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7339   }
7340   else if (move_pattern == MV_TOWARDS_PLAYER ||
7341            move_pattern == MV_AWAY_FROM_PLAYER)
7342   {
7343     int attr_x = -1, attr_y = -1;
7344     int newx, newy;
7345     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7346
7347     if (game.all_players_gone)
7348     {
7349       attr_x = game.exit_x;
7350       attr_y = game.exit_y;
7351     }
7352     else
7353     {
7354       int i;
7355
7356       for (i = 0; i < MAX_PLAYERS; i++)
7357       {
7358         struct PlayerInfo *player = &stored_player[i];
7359         int jx = player->jx, jy = player->jy;
7360
7361         if (!player->active)
7362           continue;
7363
7364         if (attr_x == -1 ||
7365             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7366         {
7367           attr_x = jx;
7368           attr_y = jy;
7369         }
7370       }
7371     }
7372
7373     MovDir[x][y] = MV_NONE;
7374     if (attr_x < x)
7375       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7376     else if (attr_x > x)
7377       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7378     if (attr_y < y)
7379       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7380     else if (attr_y > y)
7381       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7382
7383     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7384
7385     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7386     {
7387       boolean first_horiz = RND(2);
7388       int new_move_dir = MovDir[x][y];
7389
7390       if (element_info[element].move_stepsize == 0)     // "not moving"
7391       {
7392         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7393         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7394
7395         return;
7396       }
7397
7398       MovDir[x][y] =
7399         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7400       Moving2Blocked(x, y, &newx, &newy);
7401
7402       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7403         return;
7404
7405       MovDir[x][y] =
7406         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7407       Moving2Blocked(x, y, &newx, &newy);
7408
7409       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7410         return;
7411
7412       MovDir[x][y] = old_move_dir;
7413     }
7414   }
7415   else if (move_pattern == MV_WHEN_PUSHED ||
7416            move_pattern == MV_WHEN_DROPPED)
7417   {
7418     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7419       MovDir[x][y] = MV_NONE;
7420
7421     MovDelay[x][y] = 0;
7422   }
7423   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7424   {
7425     static int test_xy[7][2] =
7426     {
7427       { 0, -1 },
7428       { -1, 0 },
7429       { +1, 0 },
7430       { 0, +1 },
7431       { 0, -1 },
7432       { -1, 0 },
7433       { +1, 0 },
7434     };
7435     static int test_dir[7] =
7436     {
7437       MV_UP,
7438       MV_LEFT,
7439       MV_RIGHT,
7440       MV_DOWN,
7441       MV_UP,
7442       MV_LEFT,
7443       MV_RIGHT,
7444     };
7445     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7446     int move_preference = -1000000;     // start with very low preference
7447     int new_move_dir = MV_NONE;
7448     int start_test = RND(4);
7449     int i;
7450
7451     for (i = 0; i < NUM_DIRECTIONS; i++)
7452     {
7453       int move_dir = test_dir[start_test + i];
7454       int move_dir_preference;
7455
7456       xx = x + test_xy[start_test + i][0];
7457       yy = y + test_xy[start_test + i][1];
7458
7459       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7460           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7461       {
7462         new_move_dir = move_dir;
7463
7464         break;
7465       }
7466
7467       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7468         continue;
7469
7470       move_dir_preference = -1 * RunnerVisit[xx][yy];
7471       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7472         move_dir_preference = PlayerVisit[xx][yy];
7473
7474       if (move_dir_preference > move_preference)
7475       {
7476         // prefer field that has not been visited for the longest time
7477         move_preference = move_dir_preference;
7478         new_move_dir = move_dir;
7479       }
7480       else if (move_dir_preference == move_preference &&
7481                move_dir == old_move_dir)
7482       {
7483         // prefer last direction when all directions are preferred equally
7484         move_preference = move_dir_preference;
7485         new_move_dir = move_dir;
7486       }
7487     }
7488
7489     MovDir[x][y] = new_move_dir;
7490     if (old_move_dir != new_move_dir)
7491       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7492   }
7493 }
7494
7495 static void TurnRound(int x, int y)
7496 {
7497   int direction = MovDir[x][y];
7498
7499   TurnRoundExt(x, y);
7500
7501   GfxDir[x][y] = MovDir[x][y];
7502
7503   if (direction != MovDir[x][y])
7504     GfxFrame[x][y] = 0;
7505
7506   if (MovDelay[x][y])
7507     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7508
7509   ResetGfxFrame(x, y);
7510 }
7511
7512 static boolean JustBeingPushed(int x, int y)
7513 {
7514   int i;
7515
7516   for (i = 0; i < MAX_PLAYERS; i++)
7517   {
7518     struct PlayerInfo *player = &stored_player[i];
7519
7520     if (player->active && player->is_pushing && player->MovPos)
7521     {
7522       int next_jx = player->jx + (player->jx - player->last_jx);
7523       int next_jy = player->jy + (player->jy - player->last_jy);
7524
7525       if (x == next_jx && y == next_jy)
7526         return TRUE;
7527     }
7528   }
7529
7530   return FALSE;
7531 }
7532
7533 static void StartMoving(int x, int y)
7534 {
7535   boolean started_moving = FALSE;       // some elements can fall _and_ move
7536   int element = Feld[x][y];
7537
7538   if (Stop[x][y])
7539     return;
7540
7541   if (MovDelay[x][y] == 0)
7542     GfxAction[x][y] = ACTION_DEFAULT;
7543
7544   if (CAN_FALL(element) && y < lev_fieldy - 1)
7545   {
7546     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7547         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7548       if (JustBeingPushed(x, y))
7549         return;
7550
7551     if (element == EL_QUICKSAND_FULL)
7552     {
7553       if (IS_FREE(x, y + 1))
7554       {
7555         InitMovingField(x, y, MV_DOWN);
7556         started_moving = TRUE;
7557
7558         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7559 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7560         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7561           Store[x][y] = EL_ROCK;
7562 #else
7563         Store[x][y] = EL_ROCK;
7564 #endif
7565
7566         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7567       }
7568       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7569       {
7570         if (!MovDelay[x][y])
7571         {
7572           MovDelay[x][y] = TILEY + 1;
7573
7574           ResetGfxAnimation(x, y);
7575           ResetGfxAnimation(x, y + 1);
7576         }
7577
7578         if (MovDelay[x][y])
7579         {
7580           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7581           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7582
7583           MovDelay[x][y]--;
7584           if (MovDelay[x][y])
7585             return;
7586         }
7587
7588         Feld[x][y] = EL_QUICKSAND_EMPTY;
7589         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7590         Store[x][y + 1] = Store[x][y];
7591         Store[x][y] = 0;
7592
7593         PlayLevelSoundAction(x, y, ACTION_FILLING);
7594       }
7595       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7596       {
7597         if (!MovDelay[x][y])
7598         {
7599           MovDelay[x][y] = TILEY + 1;
7600
7601           ResetGfxAnimation(x, y);
7602           ResetGfxAnimation(x, y + 1);
7603         }
7604
7605         if (MovDelay[x][y])
7606         {
7607           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7608           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7609
7610           MovDelay[x][y]--;
7611           if (MovDelay[x][y])
7612             return;
7613         }
7614
7615         Feld[x][y] = EL_QUICKSAND_EMPTY;
7616         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7617         Store[x][y + 1] = Store[x][y];
7618         Store[x][y] = 0;
7619
7620         PlayLevelSoundAction(x, y, ACTION_FILLING);
7621       }
7622     }
7623     else if (element == EL_QUICKSAND_FAST_FULL)
7624     {
7625       if (IS_FREE(x, y + 1))
7626       {
7627         InitMovingField(x, y, MV_DOWN);
7628         started_moving = TRUE;
7629
7630         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7631 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7632         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7633           Store[x][y] = EL_ROCK;
7634 #else
7635         Store[x][y] = EL_ROCK;
7636 #endif
7637
7638         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7639       }
7640       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7641       {
7642         if (!MovDelay[x][y])
7643         {
7644           MovDelay[x][y] = TILEY + 1;
7645
7646           ResetGfxAnimation(x, y);
7647           ResetGfxAnimation(x, y + 1);
7648         }
7649
7650         if (MovDelay[x][y])
7651         {
7652           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7653           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7654
7655           MovDelay[x][y]--;
7656           if (MovDelay[x][y])
7657             return;
7658         }
7659
7660         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7661         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7662         Store[x][y + 1] = Store[x][y];
7663         Store[x][y] = 0;
7664
7665         PlayLevelSoundAction(x, y, ACTION_FILLING);
7666       }
7667       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7668       {
7669         if (!MovDelay[x][y])
7670         {
7671           MovDelay[x][y] = TILEY + 1;
7672
7673           ResetGfxAnimation(x, y);
7674           ResetGfxAnimation(x, y + 1);
7675         }
7676
7677         if (MovDelay[x][y])
7678         {
7679           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7680           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7681
7682           MovDelay[x][y]--;
7683           if (MovDelay[x][y])
7684             return;
7685         }
7686
7687         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7688         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7689         Store[x][y + 1] = Store[x][y];
7690         Store[x][y] = 0;
7691
7692         PlayLevelSoundAction(x, y, ACTION_FILLING);
7693       }
7694     }
7695     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7696              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7697     {
7698       InitMovingField(x, y, MV_DOWN);
7699       started_moving = TRUE;
7700
7701       Feld[x][y] = EL_QUICKSAND_FILLING;
7702       Store[x][y] = element;
7703
7704       PlayLevelSoundAction(x, y, ACTION_FILLING);
7705     }
7706     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7707              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7708     {
7709       InitMovingField(x, y, MV_DOWN);
7710       started_moving = TRUE;
7711
7712       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7713       Store[x][y] = element;
7714
7715       PlayLevelSoundAction(x, y, ACTION_FILLING);
7716     }
7717     else if (element == EL_MAGIC_WALL_FULL)
7718     {
7719       if (IS_FREE(x, y + 1))
7720       {
7721         InitMovingField(x, y, MV_DOWN);
7722         started_moving = TRUE;
7723
7724         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7725         Store[x][y] = EL_CHANGED(Store[x][y]);
7726       }
7727       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7728       {
7729         if (!MovDelay[x][y])
7730           MovDelay[x][y] = TILEY / 4 + 1;
7731
7732         if (MovDelay[x][y])
7733         {
7734           MovDelay[x][y]--;
7735           if (MovDelay[x][y])
7736             return;
7737         }
7738
7739         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7740         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7741         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7742         Store[x][y] = 0;
7743       }
7744     }
7745     else if (element == EL_BD_MAGIC_WALL_FULL)
7746     {
7747       if (IS_FREE(x, y + 1))
7748       {
7749         InitMovingField(x, y, MV_DOWN);
7750         started_moving = TRUE;
7751
7752         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7753         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7754       }
7755       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7756       {
7757         if (!MovDelay[x][y])
7758           MovDelay[x][y] = TILEY / 4 + 1;
7759
7760         if (MovDelay[x][y])
7761         {
7762           MovDelay[x][y]--;
7763           if (MovDelay[x][y])
7764             return;
7765         }
7766
7767         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7768         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7769         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7770         Store[x][y] = 0;
7771       }
7772     }
7773     else if (element == EL_DC_MAGIC_WALL_FULL)
7774     {
7775       if (IS_FREE(x, y + 1))
7776       {
7777         InitMovingField(x, y, MV_DOWN);
7778         started_moving = TRUE;
7779
7780         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7781         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7782       }
7783       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7784       {
7785         if (!MovDelay[x][y])
7786           MovDelay[x][y] = TILEY / 4 + 1;
7787
7788         if (MovDelay[x][y])
7789         {
7790           MovDelay[x][y]--;
7791           if (MovDelay[x][y])
7792             return;
7793         }
7794
7795         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7796         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7797         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7798         Store[x][y] = 0;
7799       }
7800     }
7801     else if ((CAN_PASS_MAGIC_WALL(element) &&
7802               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7803                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7804              (CAN_PASS_DC_MAGIC_WALL(element) &&
7805               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7806
7807     {
7808       InitMovingField(x, y, MV_DOWN);
7809       started_moving = TRUE;
7810
7811       Feld[x][y] =
7812         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7813          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7814          EL_DC_MAGIC_WALL_FILLING);
7815       Store[x][y] = element;
7816     }
7817     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7818     {
7819       SplashAcid(x, y + 1);
7820
7821       InitMovingField(x, y, MV_DOWN);
7822       started_moving = TRUE;
7823
7824       Store[x][y] = EL_ACID;
7825     }
7826     else if (
7827              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7828               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7829              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7830               CAN_FALL(element) && WasJustFalling[x][y] &&
7831               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7832
7833              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7834               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7835               (Feld[x][y + 1] == EL_BLOCKED)))
7836     {
7837       /* this is needed for a special case not covered by calling "Impact()"
7838          from "ContinueMoving()": if an element moves to a tile directly below
7839          another element which was just falling on that tile (which was empty
7840          in the previous frame), the falling element above would just stop
7841          instead of smashing the element below (in previous version, the above
7842          element was just checked for "moving" instead of "falling", resulting
7843          in incorrect smashes caused by horizontal movement of the above
7844          element; also, the case of the player being the element to smash was
7845          simply not covered here... :-/ ) */
7846
7847       CheckCollision[x][y] = 0;
7848       CheckImpact[x][y] = 0;
7849
7850       Impact(x, y);
7851     }
7852     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7853     {
7854       if (MovDir[x][y] == MV_NONE)
7855       {
7856         InitMovingField(x, y, MV_DOWN);
7857         started_moving = TRUE;
7858       }
7859     }
7860     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7861     {
7862       if (WasJustFalling[x][y]) // prevent animation from being restarted
7863         MovDir[x][y] = MV_DOWN;
7864
7865       InitMovingField(x, y, MV_DOWN);
7866       started_moving = TRUE;
7867     }
7868     else if (element == EL_AMOEBA_DROP)
7869     {
7870       Feld[x][y] = EL_AMOEBA_GROWING;
7871       Store[x][y] = EL_AMOEBA_WET;
7872     }
7873     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7874               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7875              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7876              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7877     {
7878       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7879                                 (IS_FREE(x - 1, y + 1) ||
7880                                  Feld[x - 1][y + 1] == EL_ACID));
7881       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7882                                 (IS_FREE(x + 1, y + 1) ||
7883                                  Feld[x + 1][y + 1] == EL_ACID));
7884       boolean can_fall_any  = (can_fall_left || can_fall_right);
7885       boolean can_fall_both = (can_fall_left && can_fall_right);
7886       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7887
7888       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7889       {
7890         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7891           can_fall_right = FALSE;
7892         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7893           can_fall_left = FALSE;
7894         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7895           can_fall_right = FALSE;
7896         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7897           can_fall_left = FALSE;
7898
7899         can_fall_any  = (can_fall_left || can_fall_right);
7900         can_fall_both = FALSE;
7901       }
7902
7903       if (can_fall_both)
7904       {
7905         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7906           can_fall_right = FALSE;       // slip down on left side
7907         else
7908           can_fall_left = !(can_fall_right = RND(2));
7909
7910         can_fall_both = FALSE;
7911       }
7912
7913       if (can_fall_any)
7914       {
7915         // if not determined otherwise, prefer left side for slipping down
7916         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7917         started_moving = TRUE;
7918       }
7919     }
7920     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7921     {
7922       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7923       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7924       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7925       int belt_dir = game.belt_dir[belt_nr];
7926
7927       if ((belt_dir == MV_LEFT  && left_is_free) ||
7928           (belt_dir == MV_RIGHT && right_is_free))
7929       {
7930         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7931
7932         InitMovingField(x, y, belt_dir);
7933         started_moving = TRUE;
7934
7935         Pushed[x][y] = TRUE;
7936         Pushed[nextx][y] = TRUE;
7937
7938         GfxAction[x][y] = ACTION_DEFAULT;
7939       }
7940       else
7941       {
7942         MovDir[x][y] = 0;       // if element was moving, stop it
7943       }
7944     }
7945   }
7946
7947   // not "else if" because of elements that can fall and move (EL_SPRING)
7948   if (CAN_MOVE(element) && !started_moving)
7949   {
7950     int move_pattern = element_info[element].move_pattern;
7951     int newx, newy;
7952
7953     Moving2Blocked(x, y, &newx, &newy);
7954
7955     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7956       return;
7957
7958     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7959         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7960     {
7961       WasJustMoving[x][y] = 0;
7962       CheckCollision[x][y] = 0;
7963
7964       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7965
7966       if (Feld[x][y] != element)        // element has changed
7967         return;
7968     }
7969
7970     if (!MovDelay[x][y])        // start new movement phase
7971     {
7972       // all objects that can change their move direction after each step
7973       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7974
7975       if (element != EL_YAMYAM &&
7976           element != EL_DARK_YAMYAM &&
7977           element != EL_PACMAN &&
7978           !(move_pattern & MV_ANY_DIRECTION) &&
7979           move_pattern != MV_TURNING_LEFT &&
7980           move_pattern != MV_TURNING_RIGHT &&
7981           move_pattern != MV_TURNING_LEFT_RIGHT &&
7982           move_pattern != MV_TURNING_RIGHT_LEFT &&
7983           move_pattern != MV_TURNING_RANDOM)
7984       {
7985         TurnRound(x, y);
7986
7987         if (MovDelay[x][y] && (element == EL_BUG ||
7988                                element == EL_SPACESHIP ||
7989                                element == EL_SP_SNIKSNAK ||
7990                                element == EL_SP_ELECTRON ||
7991                                element == EL_MOLE))
7992           TEST_DrawLevelField(x, y);
7993       }
7994     }
7995
7996     if (MovDelay[x][y])         // wait some time before next movement
7997     {
7998       MovDelay[x][y]--;
7999
8000       if (element == EL_ROBOT ||
8001           element == EL_YAMYAM ||
8002           element == EL_DARK_YAMYAM)
8003       {
8004         DrawLevelElementAnimationIfNeeded(x, y, element);
8005         PlayLevelSoundAction(x, y, ACTION_WAITING);
8006       }
8007       else if (element == EL_SP_ELECTRON)
8008         DrawLevelElementAnimationIfNeeded(x, y, element);
8009       else if (element == EL_DRAGON)
8010       {
8011         int i;
8012         int dir = MovDir[x][y];
8013         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8014         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8015         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8016                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8017                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8018                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8019         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8020
8021         GfxAction[x][y] = ACTION_ATTACKING;
8022
8023         if (IS_PLAYER(x, y))
8024           DrawPlayerField(x, y);
8025         else
8026           TEST_DrawLevelField(x, y);
8027
8028         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8029
8030         for (i = 1; i <= 3; i++)
8031         {
8032           int xx = x + i * dx;
8033           int yy = y + i * dy;
8034           int sx = SCREENX(xx);
8035           int sy = SCREENY(yy);
8036           int flame_graphic = graphic + (i - 1);
8037
8038           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8039             break;
8040
8041           if (MovDelay[x][y])
8042           {
8043             int flamed = MovingOrBlocked2Element(xx, yy);
8044
8045             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8046               Bang(xx, yy);
8047             else
8048               RemoveMovingField(xx, yy);
8049
8050             ChangeDelay[xx][yy] = 0;
8051
8052             Feld[xx][yy] = EL_FLAMES;
8053
8054             if (IN_SCR_FIELD(sx, sy))
8055             {
8056               TEST_DrawLevelFieldCrumbled(xx, yy);
8057               DrawGraphic(sx, sy, flame_graphic, frame);
8058             }
8059           }
8060           else
8061           {
8062             if (Feld[xx][yy] == EL_FLAMES)
8063               Feld[xx][yy] = EL_EMPTY;
8064             TEST_DrawLevelField(xx, yy);
8065           }
8066         }
8067       }
8068
8069       if (MovDelay[x][y])       // element still has to wait some time
8070       {
8071         PlayLevelSoundAction(x, y, ACTION_WAITING);
8072
8073         return;
8074       }
8075     }
8076
8077     // now make next step
8078
8079     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8080
8081     if (DONT_COLLIDE_WITH(element) &&
8082         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8083         !PLAYER_ENEMY_PROTECTED(newx, newy))
8084     {
8085       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8086
8087       return;
8088     }
8089
8090     else if (CAN_MOVE_INTO_ACID(element) &&
8091              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8092              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8093              (MovDir[x][y] == MV_DOWN ||
8094               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8095     {
8096       SplashAcid(newx, newy);
8097       Store[x][y] = EL_ACID;
8098     }
8099     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8100     {
8101       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8102           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8103           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8104           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8105       {
8106         RemoveField(x, y);
8107         TEST_DrawLevelField(x, y);
8108
8109         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8110         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8111           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8112
8113         game.friends_still_needed--;
8114         if (!game.friends_still_needed &&
8115             !game.GameOver &&
8116             game.all_players_gone)
8117           LevelSolved();
8118
8119         return;
8120       }
8121       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8122       {
8123         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8124           TEST_DrawLevelField(newx, newy);
8125         else
8126           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8127       }
8128       else if (!IS_FREE(newx, newy))
8129       {
8130         GfxAction[x][y] = ACTION_WAITING;
8131
8132         if (IS_PLAYER(x, y))
8133           DrawPlayerField(x, y);
8134         else
8135           TEST_DrawLevelField(x, y);
8136
8137         return;
8138       }
8139     }
8140     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8141     {
8142       if (IS_FOOD_PIG(Feld[newx][newy]))
8143       {
8144         if (IS_MOVING(newx, newy))
8145           RemoveMovingField(newx, newy);
8146         else
8147         {
8148           Feld[newx][newy] = EL_EMPTY;
8149           TEST_DrawLevelField(newx, newy);
8150         }
8151
8152         PlayLevelSound(x, y, SND_PIG_DIGGING);
8153       }
8154       else if (!IS_FREE(newx, newy))
8155       {
8156         if (IS_PLAYER(x, y))
8157           DrawPlayerField(x, y);
8158         else
8159           TEST_DrawLevelField(x, y);
8160
8161         return;
8162       }
8163     }
8164     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8165     {
8166       if (Store[x][y] != EL_EMPTY)
8167       {
8168         boolean can_clone = FALSE;
8169         int xx, yy;
8170
8171         // check if element to clone is still there
8172         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8173         {
8174           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8175           {
8176             can_clone = TRUE;
8177
8178             break;
8179           }
8180         }
8181
8182         // cannot clone or target field not free anymore -- do not clone
8183         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8184           Store[x][y] = EL_EMPTY;
8185       }
8186
8187       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8188       {
8189         if (IS_MV_DIAGONAL(MovDir[x][y]))
8190         {
8191           int diagonal_move_dir = MovDir[x][y];
8192           int stored = Store[x][y];
8193           int change_delay = 8;
8194           int graphic;
8195
8196           // android is moving diagonally
8197
8198           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8199
8200           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8201           GfxElement[x][y] = EL_EMC_ANDROID;
8202           GfxAction[x][y] = ACTION_SHRINKING;
8203           GfxDir[x][y] = diagonal_move_dir;
8204           ChangeDelay[x][y] = change_delay;
8205
8206           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8207                                    GfxDir[x][y]);
8208
8209           DrawLevelGraphicAnimation(x, y, graphic);
8210           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8211
8212           if (Feld[newx][newy] == EL_ACID)
8213           {
8214             SplashAcid(newx, newy);
8215
8216             return;
8217           }
8218
8219           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8220
8221           Store[newx][newy] = EL_EMC_ANDROID;
8222           GfxElement[newx][newy] = EL_EMC_ANDROID;
8223           GfxAction[newx][newy] = ACTION_GROWING;
8224           GfxDir[newx][newy] = diagonal_move_dir;
8225           ChangeDelay[newx][newy] = change_delay;
8226
8227           graphic = el_act_dir2img(GfxElement[newx][newy],
8228                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8229
8230           DrawLevelGraphicAnimation(newx, newy, graphic);
8231           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8232
8233           return;
8234         }
8235         else
8236         {
8237           Feld[newx][newy] = EL_EMPTY;
8238           TEST_DrawLevelField(newx, newy);
8239
8240           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8241         }
8242       }
8243       else if (!IS_FREE(newx, newy))
8244       {
8245         return;
8246       }
8247     }
8248     else if (IS_CUSTOM_ELEMENT(element) &&
8249              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8250     {
8251       if (!DigFieldByCE(newx, newy, element))
8252         return;
8253
8254       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8255       {
8256         RunnerVisit[x][y] = FrameCounter;
8257         PlayerVisit[x][y] /= 8;         // expire player visit path
8258       }
8259     }
8260     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8261     {
8262       if (!IS_FREE(newx, newy))
8263       {
8264         if (IS_PLAYER(x, y))
8265           DrawPlayerField(x, y);
8266         else
8267           TEST_DrawLevelField(x, y);
8268
8269         return;
8270       }
8271       else
8272       {
8273         boolean wanna_flame = !RND(10);
8274         int dx = newx - x, dy = newy - y;
8275         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8276         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8277         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8278                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8279         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8280                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8281
8282         if ((wanna_flame ||
8283              IS_CLASSIC_ENEMY(element1) ||
8284              IS_CLASSIC_ENEMY(element2)) &&
8285             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8286             element1 != EL_FLAMES && element2 != EL_FLAMES)
8287         {
8288           ResetGfxAnimation(x, y);
8289           GfxAction[x][y] = ACTION_ATTACKING;
8290
8291           if (IS_PLAYER(x, y))
8292             DrawPlayerField(x, y);
8293           else
8294             TEST_DrawLevelField(x, y);
8295
8296           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8297
8298           MovDelay[x][y] = 50;
8299
8300           Feld[newx][newy] = EL_FLAMES;
8301           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8302             Feld[newx1][newy1] = EL_FLAMES;
8303           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8304             Feld[newx2][newy2] = EL_FLAMES;
8305
8306           return;
8307         }
8308       }
8309     }
8310     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8311              Feld[newx][newy] == EL_DIAMOND)
8312     {
8313       if (IS_MOVING(newx, newy))
8314         RemoveMovingField(newx, newy);
8315       else
8316       {
8317         Feld[newx][newy] = EL_EMPTY;
8318         TEST_DrawLevelField(newx, newy);
8319       }
8320
8321       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8322     }
8323     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8324              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8325     {
8326       if (AmoebaNr[newx][newy])
8327       {
8328         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8329         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8330             Feld[newx][newy] == EL_BD_AMOEBA)
8331           AmoebaCnt[AmoebaNr[newx][newy]]--;
8332       }
8333
8334       if (IS_MOVING(newx, newy))
8335       {
8336         RemoveMovingField(newx, newy);
8337       }
8338       else
8339       {
8340         Feld[newx][newy] = EL_EMPTY;
8341         TEST_DrawLevelField(newx, newy);
8342       }
8343
8344       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8345     }
8346     else if ((element == EL_PACMAN || element == EL_MOLE)
8347              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8348     {
8349       if (AmoebaNr[newx][newy])
8350       {
8351         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8352         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8353             Feld[newx][newy] == EL_BD_AMOEBA)
8354           AmoebaCnt[AmoebaNr[newx][newy]]--;
8355       }
8356
8357       if (element == EL_MOLE)
8358       {
8359         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8360         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8361
8362         ResetGfxAnimation(x, y);
8363         GfxAction[x][y] = ACTION_DIGGING;
8364         TEST_DrawLevelField(x, y);
8365
8366         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8367
8368         return;                         // wait for shrinking amoeba
8369       }
8370       else      // element == EL_PACMAN
8371       {
8372         Feld[newx][newy] = EL_EMPTY;
8373         TEST_DrawLevelField(newx, newy);
8374         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8375       }
8376     }
8377     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8378              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8379               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8380     {
8381       // wait for shrinking amoeba to completely disappear
8382       return;
8383     }
8384     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8385     {
8386       // object was running against a wall
8387
8388       TurnRound(x, y);
8389
8390       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8391         DrawLevelElementAnimation(x, y, element);
8392
8393       if (DONT_TOUCH(element))
8394         TestIfBadThingTouchesPlayer(x, y);
8395
8396       return;
8397     }
8398
8399     InitMovingField(x, y, MovDir[x][y]);
8400
8401     PlayLevelSoundAction(x, y, ACTION_MOVING);
8402   }
8403
8404   if (MovDir[x][y])
8405     ContinueMoving(x, y);
8406 }
8407
8408 void ContinueMoving(int x, int y)
8409 {
8410   int element = Feld[x][y];
8411   struct ElementInfo *ei = &element_info[element];
8412   int direction = MovDir[x][y];
8413   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8414   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8415   int newx = x + dx, newy = y + dy;
8416   int stored = Store[x][y];
8417   int stored_new = Store[newx][newy];
8418   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8419   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8420   boolean last_line = (newy == lev_fieldy - 1);
8421
8422   MovPos[x][y] += getElementMoveStepsize(x, y);
8423
8424   if (pushed_by_player) // special case: moving object pushed by player
8425     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8426
8427   if (ABS(MovPos[x][y]) < TILEX)
8428   {
8429     TEST_DrawLevelField(x, y);
8430
8431     return;     // element is still moving
8432   }
8433
8434   // element reached destination field
8435
8436   Feld[x][y] = EL_EMPTY;
8437   Feld[newx][newy] = element;
8438   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8439
8440   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8441   {
8442     element = Feld[newx][newy] = EL_ACID;
8443   }
8444   else if (element == EL_MOLE)
8445   {
8446     Feld[x][y] = EL_SAND;
8447
8448     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8449   }
8450   else if (element == EL_QUICKSAND_FILLING)
8451   {
8452     element = Feld[newx][newy] = get_next_element(element);
8453     Store[newx][newy] = Store[x][y];
8454   }
8455   else if (element == EL_QUICKSAND_EMPTYING)
8456   {
8457     Feld[x][y] = get_next_element(element);
8458     element = Feld[newx][newy] = Store[x][y];
8459   }
8460   else if (element == EL_QUICKSAND_FAST_FILLING)
8461   {
8462     element = Feld[newx][newy] = get_next_element(element);
8463     Store[newx][newy] = Store[x][y];
8464   }
8465   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8466   {
8467     Feld[x][y] = get_next_element(element);
8468     element = Feld[newx][newy] = Store[x][y];
8469   }
8470   else if (element == EL_MAGIC_WALL_FILLING)
8471   {
8472     element = Feld[newx][newy] = get_next_element(element);
8473     if (!game.magic_wall_active)
8474       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8475     Store[newx][newy] = Store[x][y];
8476   }
8477   else if (element == EL_MAGIC_WALL_EMPTYING)
8478   {
8479     Feld[x][y] = get_next_element(element);
8480     if (!game.magic_wall_active)
8481       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8482     element = Feld[newx][newy] = Store[x][y];
8483
8484     InitField(newx, newy, FALSE);
8485   }
8486   else if (element == EL_BD_MAGIC_WALL_FILLING)
8487   {
8488     element = Feld[newx][newy] = get_next_element(element);
8489     if (!game.magic_wall_active)
8490       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8491     Store[newx][newy] = Store[x][y];
8492   }
8493   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8494   {
8495     Feld[x][y] = get_next_element(element);
8496     if (!game.magic_wall_active)
8497       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8498     element = Feld[newx][newy] = Store[x][y];
8499
8500     InitField(newx, newy, FALSE);
8501   }
8502   else if (element == EL_DC_MAGIC_WALL_FILLING)
8503   {
8504     element = Feld[newx][newy] = get_next_element(element);
8505     if (!game.magic_wall_active)
8506       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8507     Store[newx][newy] = Store[x][y];
8508   }
8509   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8510   {
8511     Feld[x][y] = get_next_element(element);
8512     if (!game.magic_wall_active)
8513       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8514     element = Feld[newx][newy] = Store[x][y];
8515
8516     InitField(newx, newy, FALSE);
8517   }
8518   else if (element == EL_AMOEBA_DROPPING)
8519   {
8520     Feld[x][y] = get_next_element(element);
8521     element = Feld[newx][newy] = Store[x][y];
8522   }
8523   else if (element == EL_SOKOBAN_OBJECT)
8524   {
8525     if (Back[x][y])
8526       Feld[x][y] = Back[x][y];
8527
8528     if (Back[newx][newy])
8529       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8530
8531     Back[x][y] = Back[newx][newy] = 0;
8532   }
8533
8534   Store[x][y] = EL_EMPTY;
8535   MovPos[x][y] = 0;
8536   MovDir[x][y] = 0;
8537   MovDelay[x][y] = 0;
8538
8539   MovDelay[newx][newy] = 0;
8540
8541   if (CAN_CHANGE_OR_HAS_ACTION(element))
8542   {
8543     // copy element change control values to new field
8544     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8545     ChangePage[newx][newy]  = ChangePage[x][y];
8546     ChangeCount[newx][newy] = ChangeCount[x][y];
8547     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8548   }
8549
8550   CustomValue[newx][newy] = CustomValue[x][y];
8551
8552   ChangeDelay[x][y] = 0;
8553   ChangePage[x][y] = -1;
8554   ChangeCount[x][y] = 0;
8555   ChangeEvent[x][y] = -1;
8556
8557   CustomValue[x][y] = 0;
8558
8559   // copy animation control values to new field
8560   GfxFrame[newx][newy]  = GfxFrame[x][y];
8561   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8562   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8563   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8564
8565   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8566
8567   // some elements can leave other elements behind after moving
8568   if (ei->move_leave_element != EL_EMPTY &&
8569       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8570       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8571   {
8572     int move_leave_element = ei->move_leave_element;
8573
8574     // this makes it possible to leave the removed element again
8575     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8576       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8577
8578     Feld[x][y] = move_leave_element;
8579
8580     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8581       MovDir[x][y] = direction;
8582
8583     InitField(x, y, FALSE);
8584
8585     if (GFX_CRUMBLED(Feld[x][y]))
8586       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8587
8588     if (ELEM_IS_PLAYER(move_leave_element))
8589       RelocatePlayer(x, y, move_leave_element);
8590   }
8591
8592   // do this after checking for left-behind element
8593   ResetGfxAnimation(x, y);      // reset animation values for old field
8594
8595   if (!CAN_MOVE(element) ||
8596       (CAN_FALL(element) && direction == MV_DOWN &&
8597        (element == EL_SPRING ||
8598         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8599         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8600     GfxDir[x][y] = MovDir[newx][newy] = 0;
8601
8602   TEST_DrawLevelField(x, y);
8603   TEST_DrawLevelField(newx, newy);
8604
8605   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8606
8607   // prevent pushed element from moving on in pushed direction
8608   if (pushed_by_player && CAN_MOVE(element) &&
8609       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8610       !(element_info[element].move_pattern & direction))
8611     TurnRound(newx, newy);
8612
8613   // prevent elements on conveyor belt from moving on in last direction
8614   if (pushed_by_conveyor && CAN_FALL(element) &&
8615       direction & MV_HORIZONTAL)
8616     MovDir[newx][newy] = 0;
8617
8618   if (!pushed_by_player)
8619   {
8620     int nextx = newx + dx, nexty = newy + dy;
8621     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8622
8623     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8624
8625     if (CAN_FALL(element) && direction == MV_DOWN)
8626       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8627
8628     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8629       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8630
8631     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8632       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8633   }
8634
8635   if (DONT_TOUCH(element))      // object may be nasty to player or others
8636   {
8637     TestIfBadThingTouchesPlayer(newx, newy);
8638     TestIfBadThingTouchesFriend(newx, newy);
8639
8640     if (!IS_CUSTOM_ELEMENT(element))
8641       TestIfBadThingTouchesOtherBadThing(newx, newy);
8642   }
8643   else if (element == EL_PENGUIN)
8644     TestIfFriendTouchesBadThing(newx, newy);
8645
8646   if (DONT_GET_HIT_BY(element))
8647   {
8648     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8649   }
8650
8651   // give the player one last chance (one more frame) to move away
8652   if (CAN_FALL(element) && direction == MV_DOWN &&
8653       (last_line || (!IS_FREE(x, newy + 1) &&
8654                      (!IS_PLAYER(x, newy + 1) ||
8655                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8656     Impact(x, newy);
8657
8658   if (pushed_by_player && !game.use_change_when_pushing_bug)
8659   {
8660     int push_side = MV_DIR_OPPOSITE(direction);
8661     struct PlayerInfo *player = PLAYERINFO(x, y);
8662
8663     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8664                                player->index_bit, push_side);
8665     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8666                                         player->index_bit, push_side);
8667   }
8668
8669   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8670     MovDelay[newx][newy] = 1;
8671
8672   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8673
8674   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8675   TestIfElementHitsCustomElement(newx, newy, direction);
8676   TestIfPlayerTouchesCustomElement(newx, newy);
8677   TestIfElementTouchesCustomElement(newx, newy);
8678
8679   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8680       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8681     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8682                              MV_DIR_OPPOSITE(direction));
8683 }
8684
8685 int AmoebeNachbarNr(int ax, int ay)
8686 {
8687   int i;
8688   int element = Feld[ax][ay];
8689   int group_nr = 0;
8690   static int xy[4][2] =
8691   {
8692     { 0, -1 },
8693     { -1, 0 },
8694     { +1, 0 },
8695     { 0, +1 }
8696   };
8697
8698   for (i = 0; i < NUM_DIRECTIONS; i++)
8699   {
8700     int x = ax + xy[i][0];
8701     int y = ay + xy[i][1];
8702
8703     if (!IN_LEV_FIELD(x, y))
8704       continue;
8705
8706     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8707       group_nr = AmoebaNr[x][y];
8708   }
8709
8710   return group_nr;
8711 }
8712
8713 static void AmoebenVereinigen(int ax, int ay)
8714 {
8715   int i, x, y, xx, yy;
8716   int new_group_nr = AmoebaNr[ax][ay];
8717   static int xy[4][2] =
8718   {
8719     { 0, -1 },
8720     { -1, 0 },
8721     { +1, 0 },
8722     { 0, +1 }
8723   };
8724
8725   if (new_group_nr == 0)
8726     return;
8727
8728   for (i = 0; i < NUM_DIRECTIONS; i++)
8729   {
8730     x = ax + xy[i][0];
8731     y = ay + xy[i][1];
8732
8733     if (!IN_LEV_FIELD(x, y))
8734       continue;
8735
8736     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8737          Feld[x][y] == EL_BD_AMOEBA ||
8738          Feld[x][y] == EL_AMOEBA_DEAD) &&
8739         AmoebaNr[x][y] != new_group_nr)
8740     {
8741       int old_group_nr = AmoebaNr[x][y];
8742
8743       if (old_group_nr == 0)
8744         return;
8745
8746       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8747       AmoebaCnt[old_group_nr] = 0;
8748       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8749       AmoebaCnt2[old_group_nr] = 0;
8750
8751       SCAN_PLAYFIELD(xx, yy)
8752       {
8753         if (AmoebaNr[xx][yy] == old_group_nr)
8754           AmoebaNr[xx][yy] = new_group_nr;
8755       }
8756     }
8757   }
8758 }
8759
8760 void AmoebeUmwandeln(int ax, int ay)
8761 {
8762   int i, x, y;
8763
8764   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8765   {
8766     int group_nr = AmoebaNr[ax][ay];
8767
8768 #ifdef DEBUG
8769     if (group_nr == 0)
8770     {
8771       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8772       printf("AmoebeUmwandeln(): This should never happen!\n");
8773       return;
8774     }
8775 #endif
8776
8777     SCAN_PLAYFIELD(x, y)
8778     {
8779       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8780       {
8781         AmoebaNr[x][y] = 0;
8782         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8783       }
8784     }
8785
8786     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8787                             SND_AMOEBA_TURNING_TO_GEM :
8788                             SND_AMOEBA_TURNING_TO_ROCK));
8789     Bang(ax, ay);
8790   }
8791   else
8792   {
8793     static int xy[4][2] =
8794     {
8795       { 0, -1 },
8796       { -1, 0 },
8797       { +1, 0 },
8798       { 0, +1 }
8799     };
8800
8801     for (i = 0; i < NUM_DIRECTIONS; i++)
8802     {
8803       x = ax + xy[i][0];
8804       y = ay + xy[i][1];
8805
8806       if (!IN_LEV_FIELD(x, y))
8807         continue;
8808
8809       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8810       {
8811         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8812                               SND_AMOEBA_TURNING_TO_GEM :
8813                               SND_AMOEBA_TURNING_TO_ROCK));
8814         Bang(x, y);
8815       }
8816     }
8817   }
8818 }
8819
8820 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8821 {
8822   int x, y;
8823   int group_nr = AmoebaNr[ax][ay];
8824   boolean done = FALSE;
8825
8826 #ifdef DEBUG
8827   if (group_nr == 0)
8828   {
8829     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8830     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8831     return;
8832   }
8833 #endif
8834
8835   SCAN_PLAYFIELD(x, y)
8836   {
8837     if (AmoebaNr[x][y] == group_nr &&
8838         (Feld[x][y] == EL_AMOEBA_DEAD ||
8839          Feld[x][y] == EL_BD_AMOEBA ||
8840          Feld[x][y] == EL_AMOEBA_GROWING))
8841     {
8842       AmoebaNr[x][y] = 0;
8843       Feld[x][y] = new_element;
8844       InitField(x, y, FALSE);
8845       TEST_DrawLevelField(x, y);
8846       done = TRUE;
8847     }
8848   }
8849
8850   if (done)
8851     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8852                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8853                             SND_BD_AMOEBA_TURNING_TO_GEM));
8854 }
8855
8856 static void AmoebeWaechst(int x, int y)
8857 {
8858   static unsigned int sound_delay = 0;
8859   static unsigned int sound_delay_value = 0;
8860
8861   if (!MovDelay[x][y])          // start new growing cycle
8862   {
8863     MovDelay[x][y] = 7;
8864
8865     if (DelayReached(&sound_delay, sound_delay_value))
8866     {
8867       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8868       sound_delay_value = 30;
8869     }
8870   }
8871
8872   if (MovDelay[x][y])           // wait some time before growing bigger
8873   {
8874     MovDelay[x][y]--;
8875     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8876     {
8877       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8878                                            6 - MovDelay[x][y]);
8879
8880       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8881     }
8882
8883     if (!MovDelay[x][y])
8884     {
8885       Feld[x][y] = Store[x][y];
8886       Store[x][y] = 0;
8887       TEST_DrawLevelField(x, y);
8888     }
8889   }
8890 }
8891
8892 static void AmoebaDisappearing(int x, int y)
8893 {
8894   static unsigned int sound_delay = 0;
8895   static unsigned int sound_delay_value = 0;
8896
8897   if (!MovDelay[x][y])          // start new shrinking cycle
8898   {
8899     MovDelay[x][y] = 7;
8900
8901     if (DelayReached(&sound_delay, sound_delay_value))
8902       sound_delay_value = 30;
8903   }
8904
8905   if (MovDelay[x][y])           // wait some time before shrinking
8906   {
8907     MovDelay[x][y]--;
8908     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8909     {
8910       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8911                                            6 - MovDelay[x][y]);
8912
8913       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8914     }
8915
8916     if (!MovDelay[x][y])
8917     {
8918       Feld[x][y] = EL_EMPTY;
8919       TEST_DrawLevelField(x, y);
8920
8921       // don't let mole enter this field in this cycle;
8922       // (give priority to objects falling to this field from above)
8923       Stop[x][y] = TRUE;
8924     }
8925   }
8926 }
8927
8928 static void AmoebeAbleger(int ax, int ay)
8929 {
8930   int i;
8931   int element = Feld[ax][ay];
8932   int graphic = el2img(element);
8933   int newax = ax, neway = ay;
8934   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8935   static int xy[4][2] =
8936   {
8937     { 0, -1 },
8938     { -1, 0 },
8939     { +1, 0 },
8940     { 0, +1 }
8941   };
8942
8943   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8944   {
8945     Feld[ax][ay] = EL_AMOEBA_DEAD;
8946     TEST_DrawLevelField(ax, ay);
8947     return;
8948   }
8949
8950   if (IS_ANIMATED(graphic))
8951     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8952
8953   if (!MovDelay[ax][ay])        // start making new amoeba field
8954     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8955
8956   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8957   {
8958     MovDelay[ax][ay]--;
8959     if (MovDelay[ax][ay])
8960       return;
8961   }
8962
8963   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8964   {
8965     int start = RND(4);
8966     int x = ax + xy[start][0];
8967     int y = ay + xy[start][1];
8968
8969     if (!IN_LEV_FIELD(x, y))
8970       return;
8971
8972     if (IS_FREE(x, y) ||
8973         CAN_GROW_INTO(Feld[x][y]) ||
8974         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8975         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8976     {
8977       newax = x;
8978       neway = y;
8979     }
8980
8981     if (newax == ax && neway == ay)
8982       return;
8983   }
8984   else                          // normal or "filled" (BD style) amoeba
8985   {
8986     int start = RND(4);
8987     boolean waiting_for_player = FALSE;
8988
8989     for (i = 0; i < NUM_DIRECTIONS; i++)
8990     {
8991       int j = (start + i) % 4;
8992       int x = ax + xy[j][0];
8993       int y = ay + xy[j][1];
8994
8995       if (!IN_LEV_FIELD(x, y))
8996         continue;
8997
8998       if (IS_FREE(x, y) ||
8999           CAN_GROW_INTO(Feld[x][y]) ||
9000           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9001           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9002       {
9003         newax = x;
9004         neway = y;
9005         break;
9006       }
9007       else if (IS_PLAYER(x, y))
9008         waiting_for_player = TRUE;
9009     }
9010
9011     if (newax == ax && neway == ay)             // amoeba cannot grow
9012     {
9013       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9014       {
9015         Feld[ax][ay] = EL_AMOEBA_DEAD;
9016         TEST_DrawLevelField(ax, ay);
9017         AmoebaCnt[AmoebaNr[ax][ay]]--;
9018
9019         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9020         {
9021           if (element == EL_AMOEBA_FULL)
9022             AmoebeUmwandeln(ax, ay);
9023           else if (element == EL_BD_AMOEBA)
9024             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9025         }
9026       }
9027       return;
9028     }
9029     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9030     {
9031       // amoeba gets larger by growing in some direction
9032
9033       int new_group_nr = AmoebaNr[ax][ay];
9034
9035 #ifdef DEBUG
9036   if (new_group_nr == 0)
9037   {
9038     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9039     printf("AmoebeAbleger(): This should never happen!\n");
9040     return;
9041   }
9042 #endif
9043
9044       AmoebaNr[newax][neway] = new_group_nr;
9045       AmoebaCnt[new_group_nr]++;
9046       AmoebaCnt2[new_group_nr]++;
9047
9048       // if amoeba touches other amoeba(s) after growing, unify them
9049       AmoebenVereinigen(newax, neway);
9050
9051       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9052       {
9053         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9054         return;
9055       }
9056     }
9057   }
9058
9059   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9060       (neway == lev_fieldy - 1 && newax != ax))
9061   {
9062     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9063     Store[newax][neway] = element;
9064   }
9065   else if (neway == ay || element == EL_EMC_DRIPPER)
9066   {
9067     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9068
9069     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9070   }
9071   else
9072   {
9073     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9074     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9075     Store[ax][ay] = EL_AMOEBA_DROP;
9076     ContinueMoving(ax, ay);
9077     return;
9078   }
9079
9080   TEST_DrawLevelField(newax, neway);
9081 }
9082
9083 static void Life(int ax, int ay)
9084 {
9085   int x1, y1, x2, y2;
9086   int life_time = 40;
9087   int element = Feld[ax][ay];
9088   int graphic = el2img(element);
9089   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9090                          level.biomaze);
9091   boolean changed = FALSE;
9092
9093   if (IS_ANIMATED(graphic))
9094     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9095
9096   if (Stop[ax][ay])
9097     return;
9098
9099   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9100     MovDelay[ax][ay] = life_time;
9101
9102   if (MovDelay[ax][ay])         // wait some time before next cycle
9103   {
9104     MovDelay[ax][ay]--;
9105     if (MovDelay[ax][ay])
9106       return;
9107   }
9108
9109   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9110   {
9111     int xx = ax+x1, yy = ay+y1;
9112     int old_element = Feld[xx][yy];
9113     int num_neighbours = 0;
9114
9115     if (!IN_LEV_FIELD(xx, yy))
9116       continue;
9117
9118     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9119     {
9120       int x = xx+x2, y = yy+y2;
9121
9122       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9123         continue;
9124
9125       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9126       boolean is_neighbour = FALSE;
9127
9128       if (level.use_life_bugs)
9129         is_neighbour =
9130           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9131            (IS_FREE(x, y)                             &&  Stop[x][y]));
9132       else
9133         is_neighbour =
9134           (Last[x][y] == element || is_player_cell);
9135
9136       if (is_neighbour)
9137         num_neighbours++;
9138     }
9139
9140     boolean is_free = FALSE;
9141
9142     if (level.use_life_bugs)
9143       is_free = (IS_FREE(xx, yy));
9144     else
9145       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9146
9147     if (xx == ax && yy == ay)           // field in the middle
9148     {
9149       if (num_neighbours < life_parameter[0] ||
9150           num_neighbours > life_parameter[1])
9151       {
9152         Feld[xx][yy] = EL_EMPTY;
9153         if (Feld[xx][yy] != old_element)
9154           TEST_DrawLevelField(xx, yy);
9155         Stop[xx][yy] = TRUE;
9156         changed = TRUE;
9157       }
9158     }
9159     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9160     {                                   // free border field
9161       if (num_neighbours >= life_parameter[2] &&
9162           num_neighbours <= life_parameter[3])
9163       {
9164         Feld[xx][yy] = element;
9165         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9166         if (Feld[xx][yy] != old_element)
9167           TEST_DrawLevelField(xx, yy);
9168         Stop[xx][yy] = TRUE;
9169         changed = TRUE;
9170       }
9171     }
9172   }
9173
9174   if (changed)
9175     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9176                    SND_GAME_OF_LIFE_GROWING);
9177 }
9178
9179 static void InitRobotWheel(int x, int y)
9180 {
9181   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9182 }
9183
9184 static void RunRobotWheel(int x, int y)
9185 {
9186   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9187 }
9188
9189 static void StopRobotWheel(int x, int y)
9190 {
9191   if (game.robot_wheel_x == x &&
9192       game.robot_wheel_y == y)
9193   {
9194     game.robot_wheel_x = -1;
9195     game.robot_wheel_y = -1;
9196     game.robot_wheel_active = FALSE;
9197   }
9198 }
9199
9200 static void InitTimegateWheel(int x, int y)
9201 {
9202   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9203 }
9204
9205 static void RunTimegateWheel(int x, int y)
9206 {
9207   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9208 }
9209
9210 static void InitMagicBallDelay(int x, int y)
9211 {
9212   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9213 }
9214
9215 static void ActivateMagicBall(int bx, int by)
9216 {
9217   int x, y;
9218
9219   if (level.ball_random)
9220   {
9221     int pos_border = RND(8);    // select one of the eight border elements
9222     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9223     int xx = pos_content % 3;
9224     int yy = pos_content / 3;
9225
9226     x = bx - 1 + xx;
9227     y = by - 1 + yy;
9228
9229     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9230       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9231   }
9232   else
9233   {
9234     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9235     {
9236       int xx = x - bx + 1;
9237       int yy = y - by + 1;
9238
9239       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9240         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9241     }
9242   }
9243
9244   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9245 }
9246
9247 static void CheckExit(int x, int y)
9248 {
9249   if (game.gems_still_needed > 0 ||
9250       game.sokoban_fields_still_needed > 0 ||
9251       game.sokoban_objects_still_needed > 0 ||
9252       game.lights_still_needed > 0)
9253   {
9254     int element = Feld[x][y];
9255     int graphic = el2img(element);
9256
9257     if (IS_ANIMATED(graphic))
9258       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9259
9260     return;
9261   }
9262
9263   // do not re-open exit door closed after last player
9264   if (game.all_players_gone)
9265     return;
9266
9267   Feld[x][y] = EL_EXIT_OPENING;
9268
9269   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9270 }
9271
9272 static void CheckExitEM(int x, int y)
9273 {
9274   if (game.gems_still_needed > 0 ||
9275       game.sokoban_fields_still_needed > 0 ||
9276       game.sokoban_objects_still_needed > 0 ||
9277       game.lights_still_needed > 0)
9278   {
9279     int element = Feld[x][y];
9280     int graphic = el2img(element);
9281
9282     if (IS_ANIMATED(graphic))
9283       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9284
9285     return;
9286   }
9287
9288   // do not re-open exit door closed after last player
9289   if (game.all_players_gone)
9290     return;
9291
9292   Feld[x][y] = EL_EM_EXIT_OPENING;
9293
9294   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9295 }
9296
9297 static void CheckExitSteel(int x, int y)
9298 {
9299   if (game.gems_still_needed > 0 ||
9300       game.sokoban_fields_still_needed > 0 ||
9301       game.sokoban_objects_still_needed > 0 ||
9302       game.lights_still_needed > 0)
9303   {
9304     int element = Feld[x][y];
9305     int graphic = el2img(element);
9306
9307     if (IS_ANIMATED(graphic))
9308       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9309
9310     return;
9311   }
9312
9313   // do not re-open exit door closed after last player
9314   if (game.all_players_gone)
9315     return;
9316
9317   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9318
9319   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9320 }
9321
9322 static void CheckExitSteelEM(int x, int y)
9323 {
9324   if (game.gems_still_needed > 0 ||
9325       game.sokoban_fields_still_needed > 0 ||
9326       game.sokoban_objects_still_needed > 0 ||
9327       game.lights_still_needed > 0)
9328   {
9329     int element = Feld[x][y];
9330     int graphic = el2img(element);
9331
9332     if (IS_ANIMATED(graphic))
9333       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9334
9335     return;
9336   }
9337
9338   // do not re-open exit door closed after last player
9339   if (game.all_players_gone)
9340     return;
9341
9342   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9343
9344   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9345 }
9346
9347 static void CheckExitSP(int x, int y)
9348 {
9349   if (game.gems_still_needed > 0)
9350   {
9351     int element = Feld[x][y];
9352     int graphic = el2img(element);
9353
9354     if (IS_ANIMATED(graphic))
9355       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9356
9357     return;
9358   }
9359
9360   // do not re-open exit door closed after last player
9361   if (game.all_players_gone)
9362     return;
9363
9364   Feld[x][y] = EL_SP_EXIT_OPENING;
9365
9366   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9367 }
9368
9369 static void CloseAllOpenTimegates(void)
9370 {
9371   int x, y;
9372
9373   SCAN_PLAYFIELD(x, y)
9374   {
9375     int element = Feld[x][y];
9376
9377     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9378     {
9379       Feld[x][y] = EL_TIMEGATE_CLOSING;
9380
9381       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9382     }
9383   }
9384 }
9385
9386 static void DrawTwinkleOnField(int x, int y)
9387 {
9388   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9389     return;
9390
9391   if (Feld[x][y] == EL_BD_DIAMOND)
9392     return;
9393
9394   if (MovDelay[x][y] == 0)      // next animation frame
9395     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9396
9397   if (MovDelay[x][y] != 0)      // wait some time before next frame
9398   {
9399     MovDelay[x][y]--;
9400
9401     DrawLevelElementAnimation(x, y, Feld[x][y]);
9402
9403     if (MovDelay[x][y] != 0)
9404     {
9405       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9406                                            10 - MovDelay[x][y]);
9407
9408       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9409     }
9410   }
9411 }
9412
9413 static void MauerWaechst(int x, int y)
9414 {
9415   int delay = 6;
9416
9417   if (!MovDelay[x][y])          // next animation frame
9418     MovDelay[x][y] = 3 * delay;
9419
9420   if (MovDelay[x][y])           // wait some time before next frame
9421   {
9422     MovDelay[x][y]--;
9423
9424     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9425     {
9426       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9427       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9428
9429       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9430     }
9431
9432     if (!MovDelay[x][y])
9433     {
9434       if (MovDir[x][y] == MV_LEFT)
9435       {
9436         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9437           TEST_DrawLevelField(x - 1, y);
9438       }
9439       else if (MovDir[x][y] == MV_RIGHT)
9440       {
9441         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9442           TEST_DrawLevelField(x + 1, y);
9443       }
9444       else if (MovDir[x][y] == MV_UP)
9445       {
9446         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9447           TEST_DrawLevelField(x, y - 1);
9448       }
9449       else
9450       {
9451         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9452           TEST_DrawLevelField(x, y + 1);
9453       }
9454
9455       Feld[x][y] = Store[x][y];
9456       Store[x][y] = 0;
9457       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9458       TEST_DrawLevelField(x, y);
9459     }
9460   }
9461 }
9462
9463 static void MauerAbleger(int ax, int ay)
9464 {
9465   int element = Feld[ax][ay];
9466   int graphic = el2img(element);
9467   boolean oben_frei = FALSE, unten_frei = FALSE;
9468   boolean links_frei = FALSE, rechts_frei = FALSE;
9469   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9470   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9471   boolean new_wall = FALSE;
9472
9473   if (IS_ANIMATED(graphic))
9474     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9475
9476   if (!MovDelay[ax][ay])        // start building new wall
9477     MovDelay[ax][ay] = 6;
9478
9479   if (MovDelay[ax][ay])         // wait some time before building new wall
9480   {
9481     MovDelay[ax][ay]--;
9482     if (MovDelay[ax][ay])
9483       return;
9484   }
9485
9486   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9487     oben_frei = TRUE;
9488   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9489     unten_frei = TRUE;
9490   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9491     links_frei = TRUE;
9492   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9493     rechts_frei = TRUE;
9494
9495   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9496       element == EL_EXPANDABLE_WALL_ANY)
9497   {
9498     if (oben_frei)
9499     {
9500       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9501       Store[ax][ay-1] = element;
9502       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9503       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9504         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9505                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9506       new_wall = TRUE;
9507     }
9508     if (unten_frei)
9509     {
9510       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9511       Store[ax][ay+1] = element;
9512       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9513       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9514         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9515                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9516       new_wall = TRUE;
9517     }
9518   }
9519
9520   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9521       element == EL_EXPANDABLE_WALL_ANY ||
9522       element == EL_EXPANDABLE_WALL ||
9523       element == EL_BD_EXPANDABLE_WALL)
9524   {
9525     if (links_frei)
9526     {
9527       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9528       Store[ax-1][ay] = element;
9529       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9530       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9531         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9532                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9533       new_wall = TRUE;
9534     }
9535
9536     if (rechts_frei)
9537     {
9538       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9539       Store[ax+1][ay] = element;
9540       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9541       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9542         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9543                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9544       new_wall = TRUE;
9545     }
9546   }
9547
9548   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9549     TEST_DrawLevelField(ax, ay);
9550
9551   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9552     oben_massiv = TRUE;
9553   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9554     unten_massiv = TRUE;
9555   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9556     links_massiv = TRUE;
9557   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9558     rechts_massiv = TRUE;
9559
9560   if (((oben_massiv && unten_massiv) ||
9561        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9562        element == EL_EXPANDABLE_WALL) &&
9563       ((links_massiv && rechts_massiv) ||
9564        element == EL_EXPANDABLE_WALL_VERTICAL))
9565     Feld[ax][ay] = EL_WALL;
9566
9567   if (new_wall)
9568     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9569 }
9570
9571 static void MauerAblegerStahl(int ax, int ay)
9572 {
9573   int element = Feld[ax][ay];
9574   int graphic = el2img(element);
9575   boolean oben_frei = FALSE, unten_frei = FALSE;
9576   boolean links_frei = FALSE, rechts_frei = FALSE;
9577   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9578   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9579   boolean new_wall = FALSE;
9580
9581   if (IS_ANIMATED(graphic))
9582     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9583
9584   if (!MovDelay[ax][ay])        // start building new wall
9585     MovDelay[ax][ay] = 6;
9586
9587   if (MovDelay[ax][ay])         // wait some time before building new wall
9588   {
9589     MovDelay[ax][ay]--;
9590     if (MovDelay[ax][ay])
9591       return;
9592   }
9593
9594   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9595     oben_frei = TRUE;
9596   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9597     unten_frei = TRUE;
9598   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9599     links_frei = TRUE;
9600   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9601     rechts_frei = TRUE;
9602
9603   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9604       element == EL_EXPANDABLE_STEELWALL_ANY)
9605   {
9606     if (oben_frei)
9607     {
9608       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9609       Store[ax][ay-1] = element;
9610       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9611       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9612         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9613                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9614       new_wall = TRUE;
9615     }
9616     if (unten_frei)
9617     {
9618       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9619       Store[ax][ay+1] = element;
9620       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9621       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9622         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9623                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9624       new_wall = TRUE;
9625     }
9626   }
9627
9628   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9629       element == EL_EXPANDABLE_STEELWALL_ANY)
9630   {
9631     if (links_frei)
9632     {
9633       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9634       Store[ax-1][ay] = element;
9635       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9636       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9637         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9638                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9639       new_wall = TRUE;
9640     }
9641
9642     if (rechts_frei)
9643     {
9644       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9645       Store[ax+1][ay] = element;
9646       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9647       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9648         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9649                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9650       new_wall = TRUE;
9651     }
9652   }
9653
9654   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9655     oben_massiv = TRUE;
9656   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9657     unten_massiv = TRUE;
9658   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9659     links_massiv = TRUE;
9660   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9661     rechts_massiv = TRUE;
9662
9663   if (((oben_massiv && unten_massiv) ||
9664        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9665       ((links_massiv && rechts_massiv) ||
9666        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9667     Feld[ax][ay] = EL_STEELWALL;
9668
9669   if (new_wall)
9670     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9671 }
9672
9673 static void CheckForDragon(int x, int y)
9674 {
9675   int i, j;
9676   boolean dragon_found = FALSE;
9677   static int xy[4][2] =
9678   {
9679     { 0, -1 },
9680     { -1, 0 },
9681     { +1, 0 },
9682     { 0, +1 }
9683   };
9684
9685   for (i = 0; i < NUM_DIRECTIONS; i++)
9686   {
9687     for (j = 0; j < 4; j++)
9688     {
9689       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9690
9691       if (IN_LEV_FIELD(xx, yy) &&
9692           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9693       {
9694         if (Feld[xx][yy] == EL_DRAGON)
9695           dragon_found = TRUE;
9696       }
9697       else
9698         break;
9699     }
9700   }
9701
9702   if (!dragon_found)
9703   {
9704     for (i = 0; i < NUM_DIRECTIONS; i++)
9705     {
9706       for (j = 0; j < 3; j++)
9707       {
9708         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9709   
9710         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9711         {
9712           Feld[xx][yy] = EL_EMPTY;
9713           TEST_DrawLevelField(xx, yy);
9714         }
9715         else
9716           break;
9717       }
9718     }
9719   }
9720 }
9721
9722 static void InitBuggyBase(int x, int y)
9723 {
9724   int element = Feld[x][y];
9725   int activating_delay = FRAMES_PER_SECOND / 4;
9726
9727   ChangeDelay[x][y] =
9728     (element == EL_SP_BUGGY_BASE ?
9729      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9730      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9731      activating_delay :
9732      element == EL_SP_BUGGY_BASE_ACTIVE ?
9733      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9734 }
9735
9736 static void WarnBuggyBase(int x, int y)
9737 {
9738   int i;
9739   static int xy[4][2] =
9740   {
9741     { 0, -1 },
9742     { -1, 0 },
9743     { +1, 0 },
9744     { 0, +1 }
9745   };
9746
9747   for (i = 0; i < NUM_DIRECTIONS; i++)
9748   {
9749     int xx = x + xy[i][0];
9750     int yy = y + xy[i][1];
9751
9752     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9753     {
9754       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9755
9756       break;
9757     }
9758   }
9759 }
9760
9761 static void InitTrap(int x, int y)
9762 {
9763   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9764 }
9765
9766 static void ActivateTrap(int x, int y)
9767 {
9768   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9769 }
9770
9771 static void ChangeActiveTrap(int x, int y)
9772 {
9773   int graphic = IMG_TRAP_ACTIVE;
9774
9775   // if new animation frame was drawn, correct crumbled sand border
9776   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9777     TEST_DrawLevelFieldCrumbled(x, y);
9778 }
9779
9780 static int getSpecialActionElement(int element, int number, int base_element)
9781 {
9782   return (element != EL_EMPTY ? element :
9783           number != -1 ? base_element + number - 1 :
9784           EL_EMPTY);
9785 }
9786
9787 static int getModifiedActionNumber(int value_old, int operator, int operand,
9788                                    int value_min, int value_max)
9789 {
9790   int value_new = (operator == CA_MODE_SET      ? operand :
9791                    operator == CA_MODE_ADD      ? value_old + operand :
9792                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9793                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9794                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9795                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9796                    value_old);
9797
9798   return (value_new < value_min ? value_min :
9799           value_new > value_max ? value_max :
9800           value_new);
9801 }
9802
9803 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9804 {
9805   struct ElementInfo *ei = &element_info[element];
9806   struct ElementChangeInfo *change = &ei->change_page[page];
9807   int target_element = change->target_element;
9808   int action_type = change->action_type;
9809   int action_mode = change->action_mode;
9810   int action_arg = change->action_arg;
9811   int action_element = change->action_element;
9812   int i;
9813
9814   if (!change->has_action)
9815     return;
9816
9817   // ---------- determine action paramater values -----------------------------
9818
9819   int level_time_value =
9820     (level.time > 0 ? TimeLeft :
9821      TimePlayed);
9822
9823   int action_arg_element_raw =
9824     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9825      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9826      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9827      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9828      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9829      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9830      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9831      EL_EMPTY);
9832   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9833
9834   int action_arg_direction =
9835     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9836      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9837      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9838      change->actual_trigger_side :
9839      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9840      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9841      MV_NONE);
9842
9843   int action_arg_number_min =
9844     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9845      CA_ARG_MIN);
9846
9847   int action_arg_number_max =
9848     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9849      action_type == CA_SET_LEVEL_GEMS ? 999 :
9850      action_type == CA_SET_LEVEL_TIME ? 9999 :
9851      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9852      action_type == CA_SET_CE_VALUE ? 9999 :
9853      action_type == CA_SET_CE_SCORE ? 9999 :
9854      CA_ARG_MAX);
9855
9856   int action_arg_number_reset =
9857     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9858      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9859      action_type == CA_SET_LEVEL_TIME ? level.time :
9860      action_type == CA_SET_LEVEL_SCORE ? 0 :
9861      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9862      action_type == CA_SET_CE_SCORE ? 0 :
9863      0);
9864
9865   int action_arg_number =
9866     (action_arg <= CA_ARG_MAX ? action_arg :
9867      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9868      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9869      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9870      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9871      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9872      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9873      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9874      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9875      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9876      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9877      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9878      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9879      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9880      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9881      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9882      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9883      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9884      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9885      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9886      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9887      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9888      -1);
9889
9890   int action_arg_number_old =
9891     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9892      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9893      action_type == CA_SET_LEVEL_SCORE ? game.score :
9894      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9895      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9896      0);
9897
9898   int action_arg_number_new =
9899     getModifiedActionNumber(action_arg_number_old,
9900                             action_mode, action_arg_number,
9901                             action_arg_number_min, action_arg_number_max);
9902
9903   int trigger_player_bits =
9904     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9905      change->actual_trigger_player_bits : change->trigger_player);
9906
9907   int action_arg_player_bits =
9908     (action_arg >= CA_ARG_PLAYER_1 &&
9909      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9910      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9911      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9912      PLAYER_BITS_ANY);
9913
9914   // ---------- execute action  -----------------------------------------------
9915
9916   switch (action_type)
9917   {
9918     case CA_NO_ACTION:
9919     {
9920       return;
9921     }
9922
9923     // ---------- level actions  ----------------------------------------------
9924
9925     case CA_RESTART_LEVEL:
9926     {
9927       game.restart_level = TRUE;
9928
9929       break;
9930     }
9931
9932     case CA_SHOW_ENVELOPE:
9933     {
9934       int element = getSpecialActionElement(action_arg_element,
9935                                             action_arg_number, EL_ENVELOPE_1);
9936
9937       if (IS_ENVELOPE(element))
9938         local_player->show_envelope = element;
9939
9940       break;
9941     }
9942
9943     case CA_SET_LEVEL_TIME:
9944     {
9945       if (level.time > 0)       // only modify limited time value
9946       {
9947         TimeLeft = action_arg_number_new;
9948
9949         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9950
9951         DisplayGameControlValues();
9952
9953         if (!TimeLeft && setup.time_limit)
9954           for (i = 0; i < MAX_PLAYERS; i++)
9955             KillPlayer(&stored_player[i]);
9956       }
9957
9958       break;
9959     }
9960
9961     case CA_SET_LEVEL_SCORE:
9962     {
9963       game.score = action_arg_number_new;
9964
9965       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9966
9967       DisplayGameControlValues();
9968
9969       break;
9970     }
9971
9972     case CA_SET_LEVEL_GEMS:
9973     {
9974       game.gems_still_needed = action_arg_number_new;
9975
9976       game.snapshot.collected_item = TRUE;
9977
9978       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9979
9980       DisplayGameControlValues();
9981
9982       break;
9983     }
9984
9985     case CA_SET_LEVEL_WIND:
9986     {
9987       game.wind_direction = action_arg_direction;
9988
9989       break;
9990     }
9991
9992     case CA_SET_LEVEL_RANDOM_SEED:
9993     {
9994       // ensure that setting a new random seed while playing is predictable
9995       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9996
9997       break;
9998     }
9999
10000     // ---------- player actions  ---------------------------------------------
10001
10002     case CA_MOVE_PLAYER:
10003     case CA_MOVE_PLAYER_NEW:
10004     {
10005       // automatically move to the next field in specified direction
10006       for (i = 0; i < MAX_PLAYERS; i++)
10007         if (trigger_player_bits & (1 << i))
10008           if (action_type == CA_MOVE_PLAYER ||
10009               stored_player[i].MovPos == 0)
10010             stored_player[i].programmed_action = action_arg_direction;
10011
10012       break;
10013     }
10014
10015     case CA_EXIT_PLAYER:
10016     {
10017       for (i = 0; i < MAX_PLAYERS; i++)
10018         if (action_arg_player_bits & (1 << i))
10019           ExitPlayer(&stored_player[i]);
10020
10021       if (game.players_still_needed == 0)
10022         LevelSolved();
10023
10024       break;
10025     }
10026
10027     case CA_KILL_PLAYER:
10028     {
10029       for (i = 0; i < MAX_PLAYERS; i++)
10030         if (action_arg_player_bits & (1 << i))
10031           KillPlayer(&stored_player[i]);
10032
10033       break;
10034     }
10035
10036     case CA_SET_PLAYER_KEYS:
10037     {
10038       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10039       int element = getSpecialActionElement(action_arg_element,
10040                                             action_arg_number, EL_KEY_1);
10041
10042       if (IS_KEY(element))
10043       {
10044         for (i = 0; i < MAX_PLAYERS; i++)
10045         {
10046           if (trigger_player_bits & (1 << i))
10047           {
10048             stored_player[i].key[KEY_NR(element)] = key_state;
10049
10050             DrawGameDoorValues();
10051           }
10052         }
10053       }
10054
10055       break;
10056     }
10057
10058     case CA_SET_PLAYER_SPEED:
10059     {
10060       for (i = 0; i < MAX_PLAYERS; i++)
10061       {
10062         if (trigger_player_bits & (1 << i))
10063         {
10064           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10065
10066           if (action_arg == CA_ARG_SPEED_FASTER &&
10067               stored_player[i].cannot_move)
10068           {
10069             action_arg_number = STEPSIZE_VERY_SLOW;
10070           }
10071           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10072                    action_arg == CA_ARG_SPEED_FASTER)
10073           {
10074             action_arg_number = 2;
10075             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10076                            CA_MODE_MULTIPLY);
10077           }
10078           else if (action_arg == CA_ARG_NUMBER_RESET)
10079           {
10080             action_arg_number = level.initial_player_stepsize[i];
10081           }
10082
10083           move_stepsize =
10084             getModifiedActionNumber(move_stepsize,
10085                                     action_mode,
10086                                     action_arg_number,
10087                                     action_arg_number_min,
10088                                     action_arg_number_max);
10089
10090           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10091         }
10092       }
10093
10094       break;
10095     }
10096
10097     case CA_SET_PLAYER_SHIELD:
10098     {
10099       for (i = 0; i < MAX_PLAYERS; i++)
10100       {
10101         if (trigger_player_bits & (1 << i))
10102         {
10103           if (action_arg == CA_ARG_SHIELD_OFF)
10104           {
10105             stored_player[i].shield_normal_time_left = 0;
10106             stored_player[i].shield_deadly_time_left = 0;
10107           }
10108           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10109           {
10110             stored_player[i].shield_normal_time_left = 999999;
10111           }
10112           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10113           {
10114             stored_player[i].shield_normal_time_left = 999999;
10115             stored_player[i].shield_deadly_time_left = 999999;
10116           }
10117         }
10118       }
10119
10120       break;
10121     }
10122
10123     case CA_SET_PLAYER_GRAVITY:
10124     {
10125       for (i = 0; i < MAX_PLAYERS; i++)
10126       {
10127         if (trigger_player_bits & (1 << i))
10128         {
10129           stored_player[i].gravity =
10130             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10131              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10132              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10133              stored_player[i].gravity);
10134         }
10135       }
10136
10137       break;
10138     }
10139
10140     case CA_SET_PLAYER_ARTWORK:
10141     {
10142       for (i = 0; i < MAX_PLAYERS; i++)
10143       {
10144         if (trigger_player_bits & (1 << i))
10145         {
10146           int artwork_element = action_arg_element;
10147
10148           if (action_arg == CA_ARG_ELEMENT_RESET)
10149             artwork_element =
10150               (level.use_artwork_element[i] ? level.artwork_element[i] :
10151                stored_player[i].element_nr);
10152
10153           if (stored_player[i].artwork_element != artwork_element)
10154             stored_player[i].Frame = 0;
10155
10156           stored_player[i].artwork_element = artwork_element;
10157
10158           SetPlayerWaiting(&stored_player[i], FALSE);
10159
10160           // set number of special actions for bored and sleeping animation
10161           stored_player[i].num_special_action_bored =
10162             get_num_special_action(artwork_element,
10163                                    ACTION_BORING_1, ACTION_BORING_LAST);
10164           stored_player[i].num_special_action_sleeping =
10165             get_num_special_action(artwork_element,
10166                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10167         }
10168       }
10169
10170       break;
10171     }
10172
10173     case CA_SET_PLAYER_INVENTORY:
10174     {
10175       for (i = 0; i < MAX_PLAYERS; i++)
10176       {
10177         struct PlayerInfo *player = &stored_player[i];
10178         int j, k;
10179
10180         if (trigger_player_bits & (1 << i))
10181         {
10182           int inventory_element = action_arg_element;
10183
10184           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10185               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10186               action_arg == CA_ARG_ELEMENT_ACTION)
10187           {
10188             int element = inventory_element;
10189             int collect_count = element_info[element].collect_count_initial;
10190
10191             if (!IS_CUSTOM_ELEMENT(element))
10192               collect_count = 1;
10193
10194             if (collect_count == 0)
10195               player->inventory_infinite_element = element;
10196             else
10197               for (k = 0; k < collect_count; k++)
10198                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10199                   player->inventory_element[player->inventory_size++] =
10200                     element;
10201           }
10202           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10203                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10204                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10205           {
10206             if (player->inventory_infinite_element != EL_UNDEFINED &&
10207                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10208                                      action_arg_element_raw))
10209               player->inventory_infinite_element = EL_UNDEFINED;
10210
10211             for (k = 0, j = 0; j < player->inventory_size; j++)
10212             {
10213               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10214                                         action_arg_element_raw))
10215                 player->inventory_element[k++] = player->inventory_element[j];
10216             }
10217
10218             player->inventory_size = k;
10219           }
10220           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10221           {
10222             if (player->inventory_size > 0)
10223             {
10224               for (j = 0; j < player->inventory_size - 1; j++)
10225                 player->inventory_element[j] = player->inventory_element[j + 1];
10226
10227               player->inventory_size--;
10228             }
10229           }
10230           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10231           {
10232             if (player->inventory_size > 0)
10233               player->inventory_size--;
10234           }
10235           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10236           {
10237             player->inventory_infinite_element = EL_UNDEFINED;
10238             player->inventory_size = 0;
10239           }
10240           else if (action_arg == CA_ARG_INVENTORY_RESET)
10241           {
10242             player->inventory_infinite_element = EL_UNDEFINED;
10243             player->inventory_size = 0;
10244
10245             if (level.use_initial_inventory[i])
10246             {
10247               for (j = 0; j < level.initial_inventory_size[i]; j++)
10248               {
10249                 int element = level.initial_inventory_content[i][j];
10250                 int collect_count = element_info[element].collect_count_initial;
10251
10252                 if (!IS_CUSTOM_ELEMENT(element))
10253                   collect_count = 1;
10254
10255                 if (collect_count == 0)
10256                   player->inventory_infinite_element = element;
10257                 else
10258                   for (k = 0; k < collect_count; k++)
10259                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10260                       player->inventory_element[player->inventory_size++] =
10261                         element;
10262               }
10263             }
10264           }
10265         }
10266       }
10267
10268       break;
10269     }
10270
10271     // ---------- CE actions  -------------------------------------------------
10272
10273     case CA_SET_CE_VALUE:
10274     {
10275       int last_ce_value = CustomValue[x][y];
10276
10277       CustomValue[x][y] = action_arg_number_new;
10278
10279       if (CustomValue[x][y] != last_ce_value)
10280       {
10281         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10282         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10283
10284         if (CustomValue[x][y] == 0)
10285         {
10286           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10287           ChangeCount[x][y] = 0;        // allow at least one more change
10288
10289           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10290           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10291         }
10292       }
10293
10294       break;
10295     }
10296
10297     case CA_SET_CE_SCORE:
10298     {
10299       int last_ce_score = ei->collect_score;
10300
10301       ei->collect_score = action_arg_number_new;
10302
10303       if (ei->collect_score != last_ce_score)
10304       {
10305         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10306         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10307
10308         if (ei->collect_score == 0)
10309         {
10310           int xx, yy;
10311
10312           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10313           ChangeCount[x][y] = 0;        // allow at least one more change
10314
10315           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10316           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10317
10318           /*
10319             This is a very special case that seems to be a mixture between
10320             CheckElementChange() and CheckTriggeredElementChange(): while
10321             the first one only affects single elements that are triggered
10322             directly, the second one affects multiple elements in the playfield
10323             that are triggered indirectly by another element. This is a third
10324             case: Changing the CE score always affects multiple identical CEs,
10325             so every affected CE must be checked, not only the single CE for
10326             which the CE score was changed in the first place (as every instance
10327             of that CE shares the same CE score, and therefore also can change)!
10328           */
10329           SCAN_PLAYFIELD(xx, yy)
10330           {
10331             if (Feld[xx][yy] == element)
10332               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10333                                  CE_SCORE_GETS_ZERO);
10334           }
10335         }
10336       }
10337
10338       break;
10339     }
10340
10341     case CA_SET_CE_ARTWORK:
10342     {
10343       int artwork_element = action_arg_element;
10344       boolean reset_frame = FALSE;
10345       int xx, yy;
10346
10347       if (action_arg == CA_ARG_ELEMENT_RESET)
10348         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10349                            element);
10350
10351       if (ei->gfx_element != artwork_element)
10352         reset_frame = TRUE;
10353
10354       ei->gfx_element = artwork_element;
10355
10356       SCAN_PLAYFIELD(xx, yy)
10357       {
10358         if (Feld[xx][yy] == element)
10359         {
10360           if (reset_frame)
10361           {
10362             ResetGfxAnimation(xx, yy);
10363             ResetRandomAnimationValue(xx, yy);
10364           }
10365
10366           TEST_DrawLevelField(xx, yy);
10367         }
10368       }
10369
10370       break;
10371     }
10372
10373     // ---------- engine actions  ---------------------------------------------
10374
10375     case CA_SET_ENGINE_SCAN_MODE:
10376     {
10377       InitPlayfieldScanMode(action_arg);
10378
10379       break;
10380     }
10381
10382     default:
10383       break;
10384   }
10385 }
10386
10387 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10388 {
10389   int old_element = Feld[x][y];
10390   int new_element = GetElementFromGroupElement(element);
10391   int previous_move_direction = MovDir[x][y];
10392   int last_ce_value = CustomValue[x][y];
10393   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10394   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10395   boolean add_player_onto_element = (new_element_is_player &&
10396                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10397                                      IS_WALKABLE(old_element));
10398
10399   if (!add_player_onto_element)
10400   {
10401     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10402       RemoveMovingField(x, y);
10403     else
10404       RemoveField(x, y);
10405
10406     Feld[x][y] = new_element;
10407
10408     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10409       MovDir[x][y] = previous_move_direction;
10410
10411     if (element_info[new_element].use_last_ce_value)
10412       CustomValue[x][y] = last_ce_value;
10413
10414     InitField_WithBug1(x, y, FALSE);
10415
10416     new_element = Feld[x][y];   // element may have changed
10417
10418     ResetGfxAnimation(x, y);
10419     ResetRandomAnimationValue(x, y);
10420
10421     TEST_DrawLevelField(x, y);
10422
10423     if (GFX_CRUMBLED(new_element))
10424       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10425   }
10426
10427   // check if element under the player changes from accessible to unaccessible
10428   // (needed for special case of dropping element which then changes)
10429   // (must be checked after creating new element for walkable group elements)
10430   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10431       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10432   {
10433     Bang(x, y);
10434
10435     return;
10436   }
10437
10438   // "ChangeCount" not set yet to allow "entered by player" change one time
10439   if (new_element_is_player)
10440     RelocatePlayer(x, y, new_element);
10441
10442   if (is_change)
10443     ChangeCount[x][y]++;        // count number of changes in the same frame
10444
10445   TestIfBadThingTouchesPlayer(x, y);
10446   TestIfPlayerTouchesCustomElement(x, y);
10447   TestIfElementTouchesCustomElement(x, y);
10448 }
10449
10450 static void CreateField(int x, int y, int element)
10451 {
10452   CreateFieldExt(x, y, element, FALSE);
10453 }
10454
10455 static void CreateElementFromChange(int x, int y, int element)
10456 {
10457   element = GET_VALID_RUNTIME_ELEMENT(element);
10458
10459   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10460   {
10461     int old_element = Feld[x][y];
10462
10463     // prevent changed element from moving in same engine frame
10464     // unless both old and new element can either fall or move
10465     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10466         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10467       Stop[x][y] = TRUE;
10468   }
10469
10470   CreateFieldExt(x, y, element, TRUE);
10471 }
10472
10473 static boolean ChangeElement(int x, int y, int element, int page)
10474 {
10475   struct ElementInfo *ei = &element_info[element];
10476   struct ElementChangeInfo *change = &ei->change_page[page];
10477   int ce_value = CustomValue[x][y];
10478   int ce_score = ei->collect_score;
10479   int target_element;
10480   int old_element = Feld[x][y];
10481
10482   // always use default change event to prevent running into a loop
10483   if (ChangeEvent[x][y] == -1)
10484     ChangeEvent[x][y] = CE_DELAY;
10485
10486   if (ChangeEvent[x][y] == CE_DELAY)
10487   {
10488     // reset actual trigger element, trigger player and action element
10489     change->actual_trigger_element = EL_EMPTY;
10490     change->actual_trigger_player = EL_EMPTY;
10491     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10492     change->actual_trigger_side = CH_SIDE_NONE;
10493     change->actual_trigger_ce_value = 0;
10494     change->actual_trigger_ce_score = 0;
10495   }
10496
10497   // do not change elements more than a specified maximum number of changes
10498   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10499     return FALSE;
10500
10501   ChangeCount[x][y]++;          // count number of changes in the same frame
10502
10503   if (change->explode)
10504   {
10505     Bang(x, y);
10506
10507     return TRUE;
10508   }
10509
10510   if (change->use_target_content)
10511   {
10512     boolean complete_replace = TRUE;
10513     boolean can_replace[3][3];
10514     int xx, yy;
10515
10516     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10517     {
10518       boolean is_empty;
10519       boolean is_walkable;
10520       boolean is_diggable;
10521       boolean is_collectible;
10522       boolean is_removable;
10523       boolean is_destructible;
10524       int ex = x + xx - 1;
10525       int ey = y + yy - 1;
10526       int content_element = change->target_content.e[xx][yy];
10527       int e;
10528
10529       can_replace[xx][yy] = TRUE;
10530
10531       if (ex == x && ey == y)   // do not check changing element itself
10532         continue;
10533
10534       if (content_element == EL_EMPTY_SPACE)
10535       {
10536         can_replace[xx][yy] = FALSE;    // do not replace border with space
10537
10538         continue;
10539       }
10540
10541       if (!IN_LEV_FIELD(ex, ey))
10542       {
10543         can_replace[xx][yy] = FALSE;
10544         complete_replace = FALSE;
10545
10546         continue;
10547       }
10548
10549       e = Feld[ex][ey];
10550
10551       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10552         e = MovingOrBlocked2Element(ex, ey);
10553
10554       is_empty = (IS_FREE(ex, ey) ||
10555                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10556
10557       is_walkable     = (is_empty || IS_WALKABLE(e));
10558       is_diggable     = (is_empty || IS_DIGGABLE(e));
10559       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10560       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10561       is_removable    = (is_diggable || is_collectible);
10562
10563       can_replace[xx][yy] =
10564         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10565           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10566           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10567           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10568           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10569           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10570          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10571
10572       if (!can_replace[xx][yy])
10573         complete_replace = FALSE;
10574     }
10575
10576     if (!change->only_if_complete || complete_replace)
10577     {
10578       boolean something_has_changed = FALSE;
10579
10580       if (change->only_if_complete && change->use_random_replace &&
10581           RND(100) < change->random_percentage)
10582         return FALSE;
10583
10584       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10585       {
10586         int ex = x + xx - 1;
10587         int ey = y + yy - 1;
10588         int content_element;
10589
10590         if (can_replace[xx][yy] && (!change->use_random_replace ||
10591                                     RND(100) < change->random_percentage))
10592         {
10593           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10594             RemoveMovingField(ex, ey);
10595
10596           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10597
10598           content_element = change->target_content.e[xx][yy];
10599           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10600                                               ce_value, ce_score);
10601
10602           CreateElementFromChange(ex, ey, target_element);
10603
10604           something_has_changed = TRUE;
10605
10606           // for symmetry reasons, freeze newly created border elements
10607           if (ex != x || ey != y)
10608             Stop[ex][ey] = TRUE;        // no more moving in this frame
10609         }
10610       }
10611
10612       if (something_has_changed)
10613       {
10614         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10615         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10616       }
10617     }
10618   }
10619   else
10620   {
10621     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10622                                         ce_value, ce_score);
10623
10624     if (element == EL_DIAGONAL_GROWING ||
10625         element == EL_DIAGONAL_SHRINKING)
10626     {
10627       target_element = Store[x][y];
10628
10629       Store[x][y] = EL_EMPTY;
10630     }
10631
10632     CreateElementFromChange(x, y, target_element);
10633
10634     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10635     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10636   }
10637
10638   // this uses direct change before indirect change
10639   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10640
10641   return TRUE;
10642 }
10643
10644 static void HandleElementChange(int x, int y, int page)
10645 {
10646   int element = MovingOrBlocked2Element(x, y);
10647   struct ElementInfo *ei = &element_info[element];
10648   struct ElementChangeInfo *change = &ei->change_page[page];
10649   boolean handle_action_before_change = FALSE;
10650
10651 #ifdef DEBUG
10652   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10653       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10654   {
10655     printf("\n\n");
10656     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10657            x, y, element, element_info[element].token_name);
10658     printf("HandleElementChange(): This should never happen!\n");
10659     printf("\n\n");
10660   }
10661 #endif
10662
10663   // this can happen with classic bombs on walkable, changing elements
10664   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10665   {
10666     return;
10667   }
10668
10669   if (ChangeDelay[x][y] == 0)           // initialize element change
10670   {
10671     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10672
10673     if (change->can_change)
10674     {
10675       // !!! not clear why graphic animation should be reset at all here !!!
10676       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10677       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10678
10679       /*
10680         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10681
10682         When using an animation frame delay of 1 (this only happens with
10683         "sp_zonk.moving.left/right" in the classic graphics), the default
10684         (non-moving) animation shows wrong animation frames (while the
10685         moving animation, like "sp_zonk.moving.left/right", is correct,
10686         so this graphical bug never shows up with the classic graphics).
10687         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10688         be drawn instead of the correct frames 0,1,2,3. This is caused by
10689         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10690         an element change: First when the change delay ("ChangeDelay[][]")
10691         counter has reached zero after decrementing, then a second time in
10692         the next frame (after "GfxFrame[][]" was already incremented) when
10693         "ChangeDelay[][]" is reset to the initial delay value again.
10694
10695         This causes frame 0 to be drawn twice, while the last frame won't
10696         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10697
10698         As some animations may already be cleverly designed around this bug
10699         (at least the "Snake Bite" snake tail animation does this), it cannot
10700         simply be fixed here without breaking such existing animations.
10701         Unfortunately, it cannot easily be detected if a graphics set was
10702         designed "before" or "after" the bug was fixed. As a workaround,
10703         a new graphics set option "game.graphics_engine_version" was added
10704         to be able to specify the game's major release version for which the
10705         graphics set was designed, which can then be used to decide if the
10706         bugfix should be used (version 4 and above) or not (version 3 or
10707         below, or if no version was specified at all, as with old sets).
10708
10709         (The wrong/fixed animation frames can be tested with the test level set
10710         "test_gfxframe" and level "000", which contains a specially prepared
10711         custom element at level position (x/y) == (11/9) which uses the zonk
10712         animation mentioned above. Using "game.graphics_engine_version: 4"
10713         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10714         This can also be seen from the debug output for this test element.)
10715       */
10716
10717       // when a custom element is about to change (for example by change delay),
10718       // do not reset graphic animation when the custom element is moving
10719       if (game.graphics_engine_version < 4 &&
10720           !IS_MOVING(x, y))
10721       {
10722         ResetGfxAnimation(x, y);
10723         ResetRandomAnimationValue(x, y);
10724       }
10725
10726       if (change->pre_change_function)
10727         change->pre_change_function(x, y);
10728     }
10729   }
10730
10731   ChangeDelay[x][y]--;
10732
10733   if (ChangeDelay[x][y] != 0)           // continue element change
10734   {
10735     if (change->can_change)
10736     {
10737       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10738
10739       if (IS_ANIMATED(graphic))
10740         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10741
10742       if (change->change_function)
10743         change->change_function(x, y);
10744     }
10745   }
10746   else                                  // finish element change
10747   {
10748     if (ChangePage[x][y] != -1)         // remember page from delayed change
10749     {
10750       page = ChangePage[x][y];
10751       ChangePage[x][y] = -1;
10752
10753       change = &ei->change_page[page];
10754     }
10755
10756     if (IS_MOVING(x, y))                // never change a running system ;-)
10757     {
10758       ChangeDelay[x][y] = 1;            // try change after next move step
10759       ChangePage[x][y] = page;          // remember page to use for change
10760
10761       return;
10762     }
10763
10764     // special case: set new level random seed before changing element
10765     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10766       handle_action_before_change = TRUE;
10767
10768     if (change->has_action && handle_action_before_change)
10769       ExecuteCustomElementAction(x, y, element, page);
10770
10771     if (change->can_change)
10772     {
10773       if (ChangeElement(x, y, element, page))
10774       {
10775         if (change->post_change_function)
10776           change->post_change_function(x, y);
10777       }
10778     }
10779
10780     if (change->has_action && !handle_action_before_change)
10781       ExecuteCustomElementAction(x, y, element, page);
10782   }
10783 }
10784
10785 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10786                                               int trigger_element,
10787                                               int trigger_event,
10788                                               int trigger_player,
10789                                               int trigger_side,
10790                                               int trigger_page)
10791 {
10792   boolean change_done_any = FALSE;
10793   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10794   int i;
10795
10796   if (!(trigger_events[trigger_element][trigger_event]))
10797     return FALSE;
10798
10799   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10800
10801   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10802   {
10803     int element = EL_CUSTOM_START + i;
10804     boolean change_done = FALSE;
10805     int p;
10806
10807     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10808         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10809       continue;
10810
10811     for (p = 0; p < element_info[element].num_change_pages; p++)
10812     {
10813       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10814
10815       if (change->can_change_or_has_action &&
10816           change->has_event[trigger_event] &&
10817           change->trigger_side & trigger_side &&
10818           change->trigger_player & trigger_player &&
10819           change->trigger_page & trigger_page_bits &&
10820           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10821       {
10822         change->actual_trigger_element = trigger_element;
10823         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10824         change->actual_trigger_player_bits = trigger_player;
10825         change->actual_trigger_side = trigger_side;
10826         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10827         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10828
10829         if ((change->can_change && !change_done) || change->has_action)
10830         {
10831           int x, y;
10832
10833           SCAN_PLAYFIELD(x, y)
10834           {
10835             if (Feld[x][y] == element)
10836             {
10837               if (change->can_change && !change_done)
10838               {
10839                 // if element already changed in this frame, not only prevent
10840                 // another element change (checked in ChangeElement()), but
10841                 // also prevent additional element actions for this element
10842
10843                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10844                     !level.use_action_after_change_bug)
10845                   continue;
10846
10847                 ChangeDelay[x][y] = 1;
10848                 ChangeEvent[x][y] = trigger_event;
10849
10850                 HandleElementChange(x, y, p);
10851               }
10852               else if (change->has_action)
10853               {
10854                 // if element already changed in this frame, not only prevent
10855                 // another element change (checked in ChangeElement()), but
10856                 // also prevent additional element actions for this element
10857
10858                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10859                     !level.use_action_after_change_bug)
10860                   continue;
10861
10862                 ExecuteCustomElementAction(x, y, element, p);
10863                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10864               }
10865             }
10866           }
10867
10868           if (change->can_change)
10869           {
10870             change_done = TRUE;
10871             change_done_any = TRUE;
10872           }
10873         }
10874       }
10875     }
10876   }
10877
10878   RECURSION_LOOP_DETECTION_END();
10879
10880   return change_done_any;
10881 }
10882
10883 static boolean CheckElementChangeExt(int x, int y,
10884                                      int element,
10885                                      int trigger_element,
10886                                      int trigger_event,
10887                                      int trigger_player,
10888                                      int trigger_side)
10889 {
10890   boolean change_done = FALSE;
10891   int p;
10892
10893   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10894       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10895     return FALSE;
10896
10897   if (Feld[x][y] == EL_BLOCKED)
10898   {
10899     Blocked2Moving(x, y, &x, &y);
10900     element = Feld[x][y];
10901   }
10902
10903   // check if element has already changed or is about to change after moving
10904   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10905        Feld[x][y] != element) ||
10906
10907       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10908        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10909         ChangePage[x][y] != -1)))
10910     return FALSE;
10911
10912   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10913
10914   for (p = 0; p < element_info[element].num_change_pages; p++)
10915   {
10916     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10917
10918     /* check trigger element for all events where the element that is checked
10919        for changing interacts with a directly adjacent element -- this is
10920        different to element changes that affect other elements to change on the
10921        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10922     boolean check_trigger_element =
10923       (trigger_event == CE_TOUCHING_X ||
10924        trigger_event == CE_HITTING_X ||
10925        trigger_event == CE_HIT_BY_X ||
10926        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10927
10928     if (change->can_change_or_has_action &&
10929         change->has_event[trigger_event] &&
10930         change->trigger_side & trigger_side &&
10931         change->trigger_player & trigger_player &&
10932         (!check_trigger_element ||
10933          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10934     {
10935       change->actual_trigger_element = trigger_element;
10936       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10937       change->actual_trigger_player_bits = trigger_player;
10938       change->actual_trigger_side = trigger_side;
10939       change->actual_trigger_ce_value = CustomValue[x][y];
10940       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10941
10942       // special case: trigger element not at (x,y) position for some events
10943       if (check_trigger_element)
10944       {
10945         static struct
10946         {
10947           int dx, dy;
10948         } move_xy[] =
10949           {
10950             {  0,  0 },
10951             { -1,  0 },
10952             { +1,  0 },
10953             {  0,  0 },
10954             {  0, -1 },
10955             {  0,  0 }, { 0, 0 }, { 0, 0 },
10956             {  0, +1 }
10957           };
10958
10959         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10960         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10961
10962         change->actual_trigger_ce_value = CustomValue[xx][yy];
10963         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10964       }
10965
10966       if (change->can_change && !change_done)
10967       {
10968         ChangeDelay[x][y] = 1;
10969         ChangeEvent[x][y] = trigger_event;
10970
10971         HandleElementChange(x, y, p);
10972
10973         change_done = TRUE;
10974       }
10975       else if (change->has_action)
10976       {
10977         ExecuteCustomElementAction(x, y, element, p);
10978         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10979       }
10980     }
10981   }
10982
10983   RECURSION_LOOP_DETECTION_END();
10984
10985   return change_done;
10986 }
10987
10988 static void PlayPlayerSound(struct PlayerInfo *player)
10989 {
10990   int jx = player->jx, jy = player->jy;
10991   int sound_element = player->artwork_element;
10992   int last_action = player->last_action_waiting;
10993   int action = player->action_waiting;
10994
10995   if (player->is_waiting)
10996   {
10997     if (action != last_action)
10998       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10999     else
11000       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11001   }
11002   else
11003   {
11004     if (action != last_action)
11005       StopSound(element_info[sound_element].sound[last_action]);
11006
11007     if (last_action == ACTION_SLEEPING)
11008       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11009   }
11010 }
11011
11012 static void PlayAllPlayersSound(void)
11013 {
11014   int i;
11015
11016   for (i = 0; i < MAX_PLAYERS; i++)
11017     if (stored_player[i].active)
11018       PlayPlayerSound(&stored_player[i]);
11019 }
11020
11021 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11022 {
11023   boolean last_waiting = player->is_waiting;
11024   int move_dir = player->MovDir;
11025
11026   player->dir_waiting = move_dir;
11027   player->last_action_waiting = player->action_waiting;
11028
11029   if (is_waiting)
11030   {
11031     if (!last_waiting)          // not waiting -> waiting
11032     {
11033       player->is_waiting = TRUE;
11034
11035       player->frame_counter_bored =
11036         FrameCounter +
11037         game.player_boring_delay_fixed +
11038         GetSimpleRandom(game.player_boring_delay_random);
11039       player->frame_counter_sleeping =
11040         FrameCounter +
11041         game.player_sleeping_delay_fixed +
11042         GetSimpleRandom(game.player_sleeping_delay_random);
11043
11044       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11045     }
11046
11047     if (game.player_sleeping_delay_fixed +
11048         game.player_sleeping_delay_random > 0 &&
11049         player->anim_delay_counter == 0 &&
11050         player->post_delay_counter == 0 &&
11051         FrameCounter >= player->frame_counter_sleeping)
11052       player->is_sleeping = TRUE;
11053     else if (game.player_boring_delay_fixed +
11054              game.player_boring_delay_random > 0 &&
11055              FrameCounter >= player->frame_counter_bored)
11056       player->is_bored = TRUE;
11057
11058     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11059                               player->is_bored ? ACTION_BORING :
11060                               ACTION_WAITING);
11061
11062     if (player->is_sleeping && player->use_murphy)
11063     {
11064       // special case for sleeping Murphy when leaning against non-free tile
11065
11066       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11067           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11068            !IS_MOVING(player->jx - 1, player->jy)))
11069         move_dir = MV_LEFT;
11070       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11071                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11072                 !IS_MOVING(player->jx + 1, player->jy)))
11073         move_dir = MV_RIGHT;
11074       else
11075         player->is_sleeping = FALSE;
11076
11077       player->dir_waiting = move_dir;
11078     }
11079
11080     if (player->is_sleeping)
11081     {
11082       if (player->num_special_action_sleeping > 0)
11083       {
11084         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11085         {
11086           int last_special_action = player->special_action_sleeping;
11087           int num_special_action = player->num_special_action_sleeping;
11088           int special_action =
11089             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11090              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11091              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11092              last_special_action + 1 : ACTION_SLEEPING);
11093           int special_graphic =
11094             el_act_dir2img(player->artwork_element, special_action, move_dir);
11095
11096           player->anim_delay_counter =
11097             graphic_info[special_graphic].anim_delay_fixed +
11098             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11099           player->post_delay_counter =
11100             graphic_info[special_graphic].post_delay_fixed +
11101             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11102
11103           player->special_action_sleeping = special_action;
11104         }
11105
11106         if (player->anim_delay_counter > 0)
11107         {
11108           player->action_waiting = player->special_action_sleeping;
11109           player->anim_delay_counter--;
11110         }
11111         else if (player->post_delay_counter > 0)
11112         {
11113           player->post_delay_counter--;
11114         }
11115       }
11116     }
11117     else if (player->is_bored)
11118     {
11119       if (player->num_special_action_bored > 0)
11120       {
11121         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11122         {
11123           int special_action =
11124             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11125           int special_graphic =
11126             el_act_dir2img(player->artwork_element, special_action, move_dir);
11127
11128           player->anim_delay_counter =
11129             graphic_info[special_graphic].anim_delay_fixed +
11130             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11131           player->post_delay_counter =
11132             graphic_info[special_graphic].post_delay_fixed +
11133             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11134
11135           player->special_action_bored = special_action;
11136         }
11137
11138         if (player->anim_delay_counter > 0)
11139         {
11140           player->action_waiting = player->special_action_bored;
11141           player->anim_delay_counter--;
11142         }
11143         else if (player->post_delay_counter > 0)
11144         {
11145           player->post_delay_counter--;
11146         }
11147       }
11148     }
11149   }
11150   else if (last_waiting)        // waiting -> not waiting
11151   {
11152     player->is_waiting = FALSE;
11153     player->is_bored = FALSE;
11154     player->is_sleeping = FALSE;
11155
11156     player->frame_counter_bored = -1;
11157     player->frame_counter_sleeping = -1;
11158
11159     player->anim_delay_counter = 0;
11160     player->post_delay_counter = 0;
11161
11162     player->dir_waiting = player->MovDir;
11163     player->action_waiting = ACTION_DEFAULT;
11164
11165     player->special_action_bored = ACTION_DEFAULT;
11166     player->special_action_sleeping = ACTION_DEFAULT;
11167   }
11168 }
11169
11170 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11171 {
11172   if ((!player->is_moving  && player->was_moving) ||
11173       (player->MovPos == 0 && player->was_moving) ||
11174       (player->is_snapping && !player->was_snapping) ||
11175       (player->is_dropping && !player->was_dropping))
11176   {
11177     if (!CheckSaveEngineSnapshotToList())
11178       return;
11179
11180     player->was_moving = FALSE;
11181     player->was_snapping = TRUE;
11182     player->was_dropping = TRUE;
11183   }
11184   else
11185   {
11186     if (player->is_moving)
11187       player->was_moving = TRUE;
11188
11189     if (!player->is_snapping)
11190       player->was_snapping = FALSE;
11191
11192     if (!player->is_dropping)
11193       player->was_dropping = FALSE;
11194   }
11195 }
11196
11197 static void CheckSingleStepMode(struct PlayerInfo *player)
11198 {
11199   if (tape.single_step && tape.recording && !tape.pausing)
11200   {
11201     /* as it is called "single step mode", just return to pause mode when the
11202        player stopped moving after one tile (or never starts moving at all) */
11203     if (!player->is_moving &&
11204         !player->is_pushing &&
11205         !player->is_dropping_pressed)
11206       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11207   }
11208
11209   CheckSaveEngineSnapshot(player);
11210 }
11211
11212 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11213 {
11214   int left      = player_action & JOY_LEFT;
11215   int right     = player_action & JOY_RIGHT;
11216   int up        = player_action & JOY_UP;
11217   int down      = player_action & JOY_DOWN;
11218   int button1   = player_action & JOY_BUTTON_1;
11219   int button2   = player_action & JOY_BUTTON_2;
11220   int dx        = (left ? -1 : right ? 1 : 0);
11221   int dy        = (up   ? -1 : down  ? 1 : 0);
11222
11223   if (!player->active || tape.pausing)
11224     return 0;
11225
11226   if (player_action)
11227   {
11228     if (button1)
11229       SnapField(player, dx, dy);
11230     else
11231     {
11232       if (button2)
11233         DropElement(player);
11234
11235       MovePlayer(player, dx, dy);
11236     }
11237
11238     CheckSingleStepMode(player);
11239
11240     SetPlayerWaiting(player, FALSE);
11241
11242     return player_action;
11243   }
11244   else
11245   {
11246     // no actions for this player (no input at player's configured device)
11247
11248     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11249     SnapField(player, 0, 0);
11250     CheckGravityMovementWhenNotMoving(player);
11251
11252     if (player->MovPos == 0)
11253       SetPlayerWaiting(player, TRUE);
11254
11255     if (player->MovPos == 0)    // needed for tape.playing
11256       player->is_moving = FALSE;
11257
11258     player->is_dropping = FALSE;
11259     player->is_dropping_pressed = FALSE;
11260     player->drop_pressed_delay = 0;
11261
11262     CheckSingleStepMode(player);
11263
11264     return 0;
11265   }
11266 }
11267
11268 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11269                                          byte *tape_action)
11270 {
11271   if (!tape.use_mouse_actions)
11272     return;
11273
11274   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11275   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11276   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11277 }
11278
11279 static void SetTapeActionFromMouseAction(byte *tape_action,
11280                                          struct MouseActionInfo *mouse_action)
11281 {
11282   if (!tape.use_mouse_actions)
11283     return;
11284
11285   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11286   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11287   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11288 }
11289
11290 static void CheckLevelSolved(void)
11291 {
11292   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11293   {
11294     if (game_em.level_solved &&
11295         !game_em.game_over)                             // game won
11296     {
11297       LevelSolved();
11298
11299       game_em.game_over = TRUE;
11300
11301       game.all_players_gone = TRUE;
11302     }
11303
11304     if (game_em.game_over)                              // game lost
11305       game.all_players_gone = TRUE;
11306   }
11307   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11308   {
11309     if (game_sp.level_solved &&
11310         !game_sp.game_over)                             // game won
11311     {
11312       LevelSolved();
11313
11314       game_sp.game_over = TRUE;
11315
11316       game.all_players_gone = TRUE;
11317     }
11318
11319     if (game_sp.game_over)                              // game lost
11320       game.all_players_gone = TRUE;
11321   }
11322   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11323   {
11324     if (game_mm.level_solved &&
11325         !game_mm.game_over)                             // game won
11326     {
11327       LevelSolved();
11328
11329       game_mm.game_over = TRUE;
11330
11331       game.all_players_gone = TRUE;
11332     }
11333
11334     if (game_mm.game_over)                              // game lost
11335       game.all_players_gone = TRUE;
11336   }
11337 }
11338
11339 static void CheckLevelTime(void)
11340 {
11341   int i;
11342
11343   if (TimeFrames >= FRAMES_PER_SECOND)
11344   {
11345     TimeFrames = 0;
11346     TapeTime++;
11347
11348     for (i = 0; i < MAX_PLAYERS; i++)
11349     {
11350       struct PlayerInfo *player = &stored_player[i];
11351
11352       if (SHIELD_ON(player))
11353       {
11354         player->shield_normal_time_left--;
11355
11356         if (player->shield_deadly_time_left > 0)
11357           player->shield_deadly_time_left--;
11358       }
11359     }
11360
11361     if (!game.LevelSolved && !level.use_step_counter)
11362     {
11363       TimePlayed++;
11364
11365       if (TimeLeft > 0)
11366       {
11367         TimeLeft--;
11368
11369         if (TimeLeft <= 10 && setup.time_limit)
11370           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11371
11372         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11373            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11374
11375         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11376
11377         if (!TimeLeft && setup.time_limit)
11378         {
11379           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11380             game_em.lev->killed_out_of_time = TRUE;
11381           else
11382             for (i = 0; i < MAX_PLAYERS; i++)
11383               KillPlayer(&stored_player[i]);
11384         }
11385       }
11386       else if (game.no_time_limit && !game.all_players_gone)
11387       {
11388         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11389       }
11390
11391       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11392     }
11393
11394     if (tape.recording || tape.playing)
11395       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11396   }
11397
11398   if (tape.recording || tape.playing)
11399     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11400
11401   UpdateAndDisplayGameControlValues();
11402 }
11403
11404 void AdvanceFrameAndPlayerCounters(int player_nr)
11405 {
11406   int i;
11407
11408   // advance frame counters (global frame counter and time frame counter)
11409   FrameCounter++;
11410   TimeFrames++;
11411
11412   // advance player counters (counters for move delay, move animation etc.)
11413   for (i = 0; i < MAX_PLAYERS; i++)
11414   {
11415     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11416     int move_delay_value = stored_player[i].move_delay_value;
11417     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11418
11419     if (!advance_player_counters)       // not all players may be affected
11420       continue;
11421
11422     if (move_frames == 0)       // less than one move per game frame
11423     {
11424       int stepsize = TILEX / move_delay_value;
11425       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11426       int count = (stored_player[i].is_moving ?
11427                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11428
11429       if (count % delay == 0)
11430         move_frames = 1;
11431     }
11432
11433     stored_player[i].Frame += move_frames;
11434
11435     if (stored_player[i].MovPos != 0)
11436       stored_player[i].StepFrame += move_frames;
11437
11438     if (stored_player[i].move_delay > 0)
11439       stored_player[i].move_delay--;
11440
11441     // due to bugs in previous versions, counter must count up, not down
11442     if (stored_player[i].push_delay != -1)
11443       stored_player[i].push_delay++;
11444
11445     if (stored_player[i].drop_delay > 0)
11446       stored_player[i].drop_delay--;
11447
11448     if (stored_player[i].is_dropping_pressed)
11449       stored_player[i].drop_pressed_delay++;
11450   }
11451 }
11452
11453 void StartGameActions(boolean init_network_game, boolean record_tape,
11454                       int random_seed)
11455 {
11456   unsigned int new_random_seed = InitRND(random_seed);
11457
11458   if (record_tape)
11459     TapeStartRecording(new_random_seed);
11460
11461   if (init_network_game)
11462   {
11463     SendToServer_LevelFile();
11464     SendToServer_StartPlaying();
11465
11466     return;
11467   }
11468
11469   InitGame();
11470 }
11471
11472 static void GameActionsExt(void)
11473 {
11474 #if 0
11475   static unsigned int game_frame_delay = 0;
11476 #endif
11477   unsigned int game_frame_delay_value;
11478   byte *recorded_player_action;
11479   byte summarized_player_action = 0;
11480   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11481   int i;
11482
11483   // detect endless loops, caused by custom element programming
11484   if (recursion_loop_detected && recursion_loop_depth == 0)
11485   {
11486     char *message = getStringCat3("Internal Error! Element ",
11487                                   EL_NAME(recursion_loop_element),
11488                                   " caused endless loop! Quit the game?");
11489
11490     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11491           EL_NAME(recursion_loop_element));
11492
11493     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11494
11495     recursion_loop_detected = FALSE;    // if game should be continued
11496
11497     free(message);
11498
11499     return;
11500   }
11501
11502   if (game.restart_level)
11503     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11504
11505   CheckLevelSolved();
11506
11507   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11508     GameWon();
11509
11510   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11511     TapeStop();
11512
11513   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11514     return;
11515
11516   game_frame_delay_value =
11517     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11518
11519   if (tape.playing && tape.warp_forward && !tape.pausing)
11520     game_frame_delay_value = 0;
11521
11522   SetVideoFrameDelay(game_frame_delay_value);
11523
11524   // (de)activate virtual buttons depending on current game status
11525   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11526   {
11527     if (game.all_players_gone)  // if no players there to be controlled anymore
11528       SetOverlayActive(FALSE);
11529     else if (!tape.playing)     // if game continues after tape stopped playing
11530       SetOverlayActive(TRUE);
11531   }
11532
11533 #if 0
11534 #if 0
11535   // ---------- main game synchronization point ----------
11536
11537   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11538
11539   printf("::: skip == %d\n", skip);
11540
11541 #else
11542   // ---------- main game synchronization point ----------
11543
11544   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11545 #endif
11546 #endif
11547
11548   if (network_playing && !network_player_action_received)
11549   {
11550     // try to get network player actions in time
11551
11552     // last chance to get network player actions without main loop delay
11553     HandleNetworking();
11554
11555     // game was quit by network peer
11556     if (game_status != GAME_MODE_PLAYING)
11557       return;
11558
11559     // check if network player actions still missing and game still running
11560     if (!network_player_action_received && !checkGameEnded())
11561       return;           // failed to get network player actions in time
11562
11563     // do not yet reset "network_player_action_received" (for tape.pausing)
11564   }
11565
11566   if (tape.pausing)
11567     return;
11568
11569   // at this point we know that we really continue executing the game
11570
11571   network_player_action_received = FALSE;
11572
11573   // when playing tape, read previously recorded player input from tape data
11574   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11575
11576   local_player->effective_mouse_action = local_player->mouse_action;
11577
11578   if (recorded_player_action != NULL)
11579     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11580                                  recorded_player_action);
11581
11582   // TapePlayAction() may return NULL when toggling to "pause before death"
11583   if (tape.pausing)
11584     return;
11585
11586   if (tape.set_centered_player)
11587   {
11588     game.centered_player_nr_next = tape.centered_player_nr_next;
11589     game.set_centered_player = TRUE;
11590   }
11591
11592   for (i = 0; i < MAX_PLAYERS; i++)
11593   {
11594     summarized_player_action |= stored_player[i].action;
11595
11596     if (!network_playing && (game.team_mode || tape.playing))
11597       stored_player[i].effective_action = stored_player[i].action;
11598   }
11599
11600   if (network_playing && !checkGameEnded())
11601     SendToServer_MovePlayer(summarized_player_action);
11602
11603   // summarize all actions at local players mapped input device position
11604   // (this allows using different input devices in single player mode)
11605   if (!network.enabled && !game.team_mode)
11606     stored_player[map_player_action[local_player->index_nr]].effective_action =
11607       summarized_player_action;
11608
11609   // summarize all actions at centered player in local team mode
11610   if (tape.recording &&
11611       setup.team_mode && !network.enabled &&
11612       setup.input_on_focus &&
11613       game.centered_player_nr != -1)
11614   {
11615     for (i = 0; i < MAX_PLAYERS; i++)
11616       stored_player[map_player_action[i]].effective_action =
11617         (i == game.centered_player_nr ? summarized_player_action : 0);
11618   }
11619
11620   if (recorded_player_action != NULL)
11621     for (i = 0; i < MAX_PLAYERS; i++)
11622       stored_player[i].effective_action = recorded_player_action[i];
11623
11624   for (i = 0; i < MAX_PLAYERS; i++)
11625   {
11626     tape_action[i] = stored_player[i].effective_action;
11627
11628     /* (this may happen in the RND game engine if a player was not present on
11629        the playfield on level start, but appeared later from a custom element */
11630     if (setup.team_mode &&
11631         tape.recording &&
11632         tape_action[i] &&
11633         !tape.player_participates[i])
11634       tape.player_participates[i] = TRUE;
11635   }
11636
11637   SetTapeActionFromMouseAction(tape_action,
11638                                &local_player->effective_mouse_action);
11639
11640   // only record actions from input devices, but not programmed actions
11641   if (tape.recording)
11642     TapeRecordAction(tape_action);
11643
11644   // remember if game was played (especially after tape stopped playing)
11645   if (!tape.playing && summarized_player_action)
11646     game.GamePlayed = TRUE;
11647
11648 #if USE_NEW_PLAYER_ASSIGNMENTS
11649   // !!! also map player actions in single player mode !!!
11650   // if (game.team_mode)
11651   if (1)
11652   {
11653     byte mapped_action[MAX_PLAYERS];
11654
11655 #if DEBUG_PLAYER_ACTIONS
11656     printf(":::");
11657     for (i = 0; i < MAX_PLAYERS; i++)
11658       printf(" %d, ", stored_player[i].effective_action);
11659 #endif
11660
11661     for (i = 0; i < MAX_PLAYERS; i++)
11662       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11663
11664     for (i = 0; i < MAX_PLAYERS; i++)
11665       stored_player[i].effective_action = mapped_action[i];
11666
11667 #if DEBUG_PLAYER_ACTIONS
11668     printf(" =>");
11669     for (i = 0; i < MAX_PLAYERS; i++)
11670       printf(" %d, ", stored_player[i].effective_action);
11671     printf("\n");
11672 #endif
11673   }
11674 #if DEBUG_PLAYER_ACTIONS
11675   else
11676   {
11677     printf(":::");
11678     for (i = 0; i < MAX_PLAYERS; i++)
11679       printf(" %d, ", stored_player[i].effective_action);
11680     printf("\n");
11681   }
11682 #endif
11683 #endif
11684
11685   for (i = 0; i < MAX_PLAYERS; i++)
11686   {
11687     // allow engine snapshot in case of changed movement attempt
11688     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11689         (stored_player[i].effective_action & KEY_MOTION))
11690       game.snapshot.changed_action = TRUE;
11691
11692     // allow engine snapshot in case of snapping/dropping attempt
11693     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11694         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11695       game.snapshot.changed_action = TRUE;
11696
11697     game.snapshot.last_action[i] = stored_player[i].effective_action;
11698   }
11699
11700   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11701   {
11702     GameActions_EM_Main();
11703   }
11704   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11705   {
11706     GameActions_SP_Main();
11707   }
11708   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11709   {
11710     GameActions_MM_Main();
11711   }
11712   else
11713   {
11714     GameActions_RND_Main();
11715   }
11716
11717   BlitScreenToBitmap(backbuffer);
11718
11719   CheckLevelSolved();
11720   CheckLevelTime();
11721
11722   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11723
11724   if (global.show_frames_per_second)
11725   {
11726     static unsigned int fps_counter = 0;
11727     static int fps_frames = 0;
11728     unsigned int fps_delay_ms = Counter() - fps_counter;
11729
11730     fps_frames++;
11731
11732     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11733     {
11734       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11735
11736       fps_frames = 0;
11737       fps_counter = Counter();
11738
11739       // always draw FPS to screen after FPS value was updated
11740       redraw_mask |= REDRAW_FPS;
11741     }
11742
11743     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11744     if (GetDrawDeactivationMask() == REDRAW_NONE)
11745       redraw_mask |= REDRAW_FPS;
11746   }
11747 }
11748
11749 static void GameActions_CheckSaveEngineSnapshot(void)
11750 {
11751   if (!game.snapshot.save_snapshot)
11752     return;
11753
11754   // clear flag for saving snapshot _before_ saving snapshot
11755   game.snapshot.save_snapshot = FALSE;
11756
11757   SaveEngineSnapshotToList();
11758 }
11759
11760 void GameActions(void)
11761 {
11762   GameActionsExt();
11763
11764   GameActions_CheckSaveEngineSnapshot();
11765 }
11766
11767 void GameActions_EM_Main(void)
11768 {
11769   byte effective_action[MAX_PLAYERS];
11770   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11771   int i;
11772
11773   for (i = 0; i < MAX_PLAYERS; i++)
11774     effective_action[i] = stored_player[i].effective_action;
11775
11776   GameActions_EM(effective_action, warp_mode);
11777 }
11778
11779 void GameActions_SP_Main(void)
11780 {
11781   byte effective_action[MAX_PLAYERS];
11782   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11783   int i;
11784
11785   for (i = 0; i < MAX_PLAYERS; i++)
11786     effective_action[i] = stored_player[i].effective_action;
11787
11788   GameActions_SP(effective_action, warp_mode);
11789
11790   for (i = 0; i < MAX_PLAYERS; i++)
11791   {
11792     if (stored_player[i].force_dropping)
11793       stored_player[i].action |= KEY_BUTTON_DROP;
11794
11795     stored_player[i].force_dropping = FALSE;
11796   }
11797 }
11798
11799 void GameActions_MM_Main(void)
11800 {
11801   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11802
11803   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11804 }
11805
11806 void GameActions_RND_Main(void)
11807 {
11808   GameActions_RND();
11809 }
11810
11811 void GameActions_RND(void)
11812 {
11813   static struct MouseActionInfo mouse_action_last = { 0 };
11814   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11815   int magic_wall_x = 0, magic_wall_y = 0;
11816   int i, x, y, element, graphic, last_gfx_frame;
11817
11818   InitPlayfieldScanModeVars();
11819
11820   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11821   {
11822     SCAN_PLAYFIELD(x, y)
11823     {
11824       ChangeCount[x][y] = 0;
11825       ChangeEvent[x][y] = -1;
11826     }
11827   }
11828
11829   if (game.set_centered_player)
11830   {
11831     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11832
11833     // switching to "all players" only possible if all players fit to screen
11834     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11835     {
11836       game.centered_player_nr_next = game.centered_player_nr;
11837       game.set_centered_player = FALSE;
11838     }
11839
11840     // do not switch focus to non-existing (or non-active) player
11841     if (game.centered_player_nr_next >= 0 &&
11842         !stored_player[game.centered_player_nr_next].active)
11843     {
11844       game.centered_player_nr_next = game.centered_player_nr;
11845       game.set_centered_player = FALSE;
11846     }
11847   }
11848
11849   if (game.set_centered_player &&
11850       ScreenMovPos == 0)        // screen currently aligned at tile position
11851   {
11852     int sx, sy;
11853
11854     if (game.centered_player_nr_next == -1)
11855     {
11856       setScreenCenteredToAllPlayers(&sx, &sy);
11857     }
11858     else
11859     {
11860       sx = stored_player[game.centered_player_nr_next].jx;
11861       sy = stored_player[game.centered_player_nr_next].jy;
11862     }
11863
11864     game.centered_player_nr = game.centered_player_nr_next;
11865     game.set_centered_player = FALSE;
11866
11867     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11868     DrawGameDoorValues();
11869   }
11870
11871   for (i = 0; i < MAX_PLAYERS; i++)
11872   {
11873     int actual_player_action = stored_player[i].effective_action;
11874
11875 #if 1
11876     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11877        - rnd_equinox_tetrachloride 048
11878        - rnd_equinox_tetrachloride_ii 096
11879        - rnd_emanuel_schmieg 002
11880        - doctor_sloan_ww 001, 020
11881     */
11882     if (stored_player[i].MovPos == 0)
11883       CheckGravityMovement(&stored_player[i]);
11884 #endif
11885
11886     // overwrite programmed action with tape action
11887     if (stored_player[i].programmed_action)
11888       actual_player_action = stored_player[i].programmed_action;
11889
11890     PlayerActions(&stored_player[i], actual_player_action);
11891
11892     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11893   }
11894
11895   ScrollScreen(NULL, SCROLL_GO_ON);
11896
11897   /* for backwards compatibility, the following code emulates a fixed bug that
11898      occured when pushing elements (causing elements that just made their last
11899      pushing step to already (if possible) make their first falling step in the
11900      same game frame, which is bad); this code is also needed to use the famous
11901      "spring push bug" which is used in older levels and might be wanted to be
11902      used also in newer levels, but in this case the buggy pushing code is only
11903      affecting the "spring" element and no other elements */
11904
11905   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11906   {
11907     for (i = 0; i < MAX_PLAYERS; i++)
11908     {
11909       struct PlayerInfo *player = &stored_player[i];
11910       int x = player->jx;
11911       int y = player->jy;
11912
11913       if (player->active && player->is_pushing && player->is_moving &&
11914           IS_MOVING(x, y) &&
11915           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11916            Feld[x][y] == EL_SPRING))
11917       {
11918         ContinueMoving(x, y);
11919
11920         // continue moving after pushing (this is actually a bug)
11921         if (!IS_MOVING(x, y))
11922           Stop[x][y] = FALSE;
11923       }
11924     }
11925   }
11926
11927   SCAN_PLAYFIELD(x, y)
11928   {
11929     Last[x][y] = Feld[x][y];
11930
11931     ChangeCount[x][y] = 0;
11932     ChangeEvent[x][y] = -1;
11933
11934     // this must be handled before main playfield loop
11935     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11936     {
11937       MovDelay[x][y]--;
11938       if (MovDelay[x][y] <= 0)
11939         RemoveField(x, y);
11940     }
11941
11942     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11943     {
11944       MovDelay[x][y]--;
11945       if (MovDelay[x][y] <= 0)
11946       {
11947         RemoveField(x, y);
11948         TEST_DrawLevelField(x, y);
11949
11950         TestIfElementTouchesCustomElement(x, y);        // for empty space
11951       }
11952     }
11953
11954 #if DEBUG
11955     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11956     {
11957       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11958       printf("GameActions(): This should never happen!\n");
11959
11960       ChangePage[x][y] = -1;
11961     }
11962 #endif
11963
11964     Stop[x][y] = FALSE;
11965     if (WasJustMoving[x][y] > 0)
11966       WasJustMoving[x][y]--;
11967     if (WasJustFalling[x][y] > 0)
11968       WasJustFalling[x][y]--;
11969     if (CheckCollision[x][y] > 0)
11970       CheckCollision[x][y]--;
11971     if (CheckImpact[x][y] > 0)
11972       CheckImpact[x][y]--;
11973
11974     GfxFrame[x][y]++;
11975
11976     /* reset finished pushing action (not done in ContinueMoving() to allow
11977        continuous pushing animation for elements with zero push delay) */
11978     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11979     {
11980       ResetGfxAnimation(x, y);
11981       TEST_DrawLevelField(x, y);
11982     }
11983
11984 #if DEBUG
11985     if (IS_BLOCKED(x, y))
11986     {
11987       int oldx, oldy;
11988
11989       Blocked2Moving(x, y, &oldx, &oldy);
11990       if (!IS_MOVING(oldx, oldy))
11991       {
11992         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11993         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11994         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11995         printf("GameActions(): This should never happen!\n");
11996       }
11997     }
11998 #endif
11999   }
12000
12001   if (mouse_action.button)
12002   {
12003     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12004
12005     x = mouse_action.lx;
12006     y = mouse_action.ly;
12007     element = Feld[x][y];
12008
12009     if (new_button)
12010     {
12011       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12012       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12013     }
12014
12015     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12016     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12017   }
12018
12019   SCAN_PLAYFIELD(x, y)
12020   {
12021     element = Feld[x][y];
12022     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12023     last_gfx_frame = GfxFrame[x][y];
12024
12025     ResetGfxFrame(x, y);
12026
12027     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12028       DrawLevelGraphicAnimation(x, y, graphic);
12029
12030     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12031         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12032       ResetRandomAnimationValue(x, y);
12033
12034     SetRandomAnimationValue(x, y);
12035
12036     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12037
12038     if (IS_INACTIVE(element))
12039     {
12040       if (IS_ANIMATED(graphic))
12041         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12042
12043       continue;
12044     }
12045
12046     // this may take place after moving, so 'element' may have changed
12047     if (IS_CHANGING(x, y) &&
12048         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12049     {
12050       int page = element_info[element].event_page_nr[CE_DELAY];
12051
12052       HandleElementChange(x, y, page);
12053
12054       element = Feld[x][y];
12055       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12056     }
12057
12058     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12059     {
12060       StartMoving(x, y);
12061
12062       element = Feld[x][y];
12063       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12064
12065       if (IS_ANIMATED(graphic) &&
12066           !IS_MOVING(x, y) &&
12067           !Stop[x][y])
12068         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12069
12070       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12071         TEST_DrawTwinkleOnField(x, y);
12072     }
12073     else if (element == EL_ACID)
12074     {
12075       if (!Stop[x][y])
12076         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12077     }
12078     else if ((element == EL_EXIT_OPEN ||
12079               element == EL_EM_EXIT_OPEN ||
12080               element == EL_SP_EXIT_OPEN ||
12081               element == EL_STEEL_EXIT_OPEN ||
12082               element == EL_EM_STEEL_EXIT_OPEN ||
12083               element == EL_SP_TERMINAL ||
12084               element == EL_SP_TERMINAL_ACTIVE ||
12085               element == EL_EXTRA_TIME ||
12086               element == EL_SHIELD_NORMAL ||
12087               element == EL_SHIELD_DEADLY) &&
12088              IS_ANIMATED(graphic))
12089       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12090     else if (IS_MOVING(x, y))
12091       ContinueMoving(x, y);
12092     else if (IS_ACTIVE_BOMB(element))
12093       CheckDynamite(x, y);
12094     else if (element == EL_AMOEBA_GROWING)
12095       AmoebeWaechst(x, y);
12096     else if (element == EL_AMOEBA_SHRINKING)
12097       AmoebaDisappearing(x, y);
12098
12099 #if !USE_NEW_AMOEBA_CODE
12100     else if (IS_AMOEBALIVE(element))
12101       AmoebeAbleger(x, y);
12102 #endif
12103
12104     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12105       Life(x, y);
12106     else if (element == EL_EXIT_CLOSED)
12107       CheckExit(x, y);
12108     else if (element == EL_EM_EXIT_CLOSED)
12109       CheckExitEM(x, y);
12110     else if (element == EL_STEEL_EXIT_CLOSED)
12111       CheckExitSteel(x, y);
12112     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12113       CheckExitSteelEM(x, y);
12114     else if (element == EL_SP_EXIT_CLOSED)
12115       CheckExitSP(x, y);
12116     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12117              element == EL_EXPANDABLE_STEELWALL_GROWING)
12118       MauerWaechst(x, y);
12119     else if (element == EL_EXPANDABLE_WALL ||
12120              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12121              element == EL_EXPANDABLE_WALL_VERTICAL ||
12122              element == EL_EXPANDABLE_WALL_ANY ||
12123              element == EL_BD_EXPANDABLE_WALL)
12124       MauerAbleger(x, y);
12125     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12126              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12127              element == EL_EXPANDABLE_STEELWALL_ANY)
12128       MauerAblegerStahl(x, y);
12129     else if (element == EL_FLAMES)
12130       CheckForDragon(x, y);
12131     else if (element == EL_EXPLOSION)
12132       ; // drawing of correct explosion animation is handled separately
12133     else if (element == EL_ELEMENT_SNAPPING ||
12134              element == EL_DIAGONAL_SHRINKING ||
12135              element == EL_DIAGONAL_GROWING)
12136     {
12137       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12138
12139       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12140     }
12141     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12142       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12143
12144     if (IS_BELT_ACTIVE(element))
12145       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12146
12147     if (game.magic_wall_active)
12148     {
12149       int jx = local_player->jx, jy = local_player->jy;
12150
12151       // play the element sound at the position nearest to the player
12152       if ((element == EL_MAGIC_WALL_FULL ||
12153            element == EL_MAGIC_WALL_ACTIVE ||
12154            element == EL_MAGIC_WALL_EMPTYING ||
12155            element == EL_BD_MAGIC_WALL_FULL ||
12156            element == EL_BD_MAGIC_WALL_ACTIVE ||
12157            element == EL_BD_MAGIC_WALL_EMPTYING ||
12158            element == EL_DC_MAGIC_WALL_FULL ||
12159            element == EL_DC_MAGIC_WALL_ACTIVE ||
12160            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12161           ABS(x - jx) + ABS(y - jy) <
12162           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12163       {
12164         magic_wall_x = x;
12165         magic_wall_y = y;
12166       }
12167     }
12168   }
12169
12170 #if USE_NEW_AMOEBA_CODE
12171   // new experimental amoeba growth stuff
12172   if (!(FrameCounter % 8))
12173   {
12174     static unsigned int random = 1684108901;
12175
12176     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12177     {
12178       x = RND(lev_fieldx);
12179       y = RND(lev_fieldy);
12180       element = Feld[x][y];
12181
12182       if (!IS_PLAYER(x,y) &&
12183           (element == EL_EMPTY ||
12184            CAN_GROW_INTO(element) ||
12185            element == EL_QUICKSAND_EMPTY ||
12186            element == EL_QUICKSAND_FAST_EMPTY ||
12187            element == EL_ACID_SPLASH_LEFT ||
12188            element == EL_ACID_SPLASH_RIGHT))
12189       {
12190         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12191             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12192             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12193             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12194           Feld[x][y] = EL_AMOEBA_DROP;
12195       }
12196
12197       random = random * 129 + 1;
12198     }
12199   }
12200 #endif
12201
12202   game.explosions_delayed = FALSE;
12203
12204   SCAN_PLAYFIELD(x, y)
12205   {
12206     element = Feld[x][y];
12207
12208     if (ExplodeField[x][y])
12209       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12210     else if (element == EL_EXPLOSION)
12211       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12212
12213     ExplodeField[x][y] = EX_TYPE_NONE;
12214   }
12215
12216   game.explosions_delayed = TRUE;
12217
12218   if (game.magic_wall_active)
12219   {
12220     if (!(game.magic_wall_time_left % 4))
12221     {
12222       int element = Feld[magic_wall_x][magic_wall_y];
12223
12224       if (element == EL_BD_MAGIC_WALL_FULL ||
12225           element == EL_BD_MAGIC_WALL_ACTIVE ||
12226           element == EL_BD_MAGIC_WALL_EMPTYING)
12227         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12228       else if (element == EL_DC_MAGIC_WALL_FULL ||
12229                element == EL_DC_MAGIC_WALL_ACTIVE ||
12230                element == EL_DC_MAGIC_WALL_EMPTYING)
12231         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12232       else
12233         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12234     }
12235
12236     if (game.magic_wall_time_left > 0)
12237     {
12238       game.magic_wall_time_left--;
12239
12240       if (!game.magic_wall_time_left)
12241       {
12242         SCAN_PLAYFIELD(x, y)
12243         {
12244           element = Feld[x][y];
12245
12246           if (element == EL_MAGIC_WALL_ACTIVE ||
12247               element == EL_MAGIC_WALL_FULL)
12248           {
12249             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12250             TEST_DrawLevelField(x, y);
12251           }
12252           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12253                    element == EL_BD_MAGIC_WALL_FULL)
12254           {
12255             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12256             TEST_DrawLevelField(x, y);
12257           }
12258           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12259                    element == EL_DC_MAGIC_WALL_FULL)
12260           {
12261             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12262             TEST_DrawLevelField(x, y);
12263           }
12264         }
12265
12266         game.magic_wall_active = FALSE;
12267       }
12268     }
12269   }
12270
12271   if (game.light_time_left > 0)
12272   {
12273     game.light_time_left--;
12274
12275     if (game.light_time_left == 0)
12276       RedrawAllLightSwitchesAndInvisibleElements();
12277   }
12278
12279   if (game.timegate_time_left > 0)
12280   {
12281     game.timegate_time_left--;
12282
12283     if (game.timegate_time_left == 0)
12284       CloseAllOpenTimegates();
12285   }
12286
12287   if (game.lenses_time_left > 0)
12288   {
12289     game.lenses_time_left--;
12290
12291     if (game.lenses_time_left == 0)
12292       RedrawAllInvisibleElementsForLenses();
12293   }
12294
12295   if (game.magnify_time_left > 0)
12296   {
12297     game.magnify_time_left--;
12298
12299     if (game.magnify_time_left == 0)
12300       RedrawAllInvisibleElementsForMagnifier();
12301   }
12302
12303   for (i = 0; i < MAX_PLAYERS; i++)
12304   {
12305     struct PlayerInfo *player = &stored_player[i];
12306
12307     if (SHIELD_ON(player))
12308     {
12309       if (player->shield_deadly_time_left)
12310         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12311       else if (player->shield_normal_time_left)
12312         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12313     }
12314   }
12315
12316 #if USE_DELAYED_GFX_REDRAW
12317   SCAN_PLAYFIELD(x, y)
12318   {
12319     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12320     {
12321       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12322          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12323
12324       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12325         DrawLevelField(x, y);
12326
12327       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12328         DrawLevelFieldCrumbled(x, y);
12329
12330       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12331         DrawLevelFieldCrumbledNeighbours(x, y);
12332
12333       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12334         DrawTwinkleOnField(x, y);
12335     }
12336
12337     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12338   }
12339 #endif
12340
12341   DrawAllPlayers();
12342   PlayAllPlayersSound();
12343
12344   for (i = 0; i < MAX_PLAYERS; i++)
12345   {
12346     struct PlayerInfo *player = &stored_player[i];
12347
12348     if (player->show_envelope != 0 && (!player->active ||
12349                                        player->MovPos == 0))
12350     {
12351       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12352
12353       player->show_envelope = 0;
12354     }
12355   }
12356
12357   // use random number generator in every frame to make it less predictable
12358   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12359     RND(1);
12360
12361   mouse_action_last = mouse_action;
12362 }
12363
12364 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12365 {
12366   int min_x = x, min_y = y, max_x = x, max_y = y;
12367   int i;
12368
12369   for (i = 0; i < MAX_PLAYERS; i++)
12370   {
12371     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12372
12373     if (!stored_player[i].active || &stored_player[i] == player)
12374       continue;
12375
12376     min_x = MIN(min_x, jx);
12377     min_y = MIN(min_y, jy);
12378     max_x = MAX(max_x, jx);
12379     max_y = MAX(max_y, jy);
12380   }
12381
12382   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12383 }
12384
12385 static boolean AllPlayersInVisibleScreen(void)
12386 {
12387   int i;
12388
12389   for (i = 0; i < MAX_PLAYERS; i++)
12390   {
12391     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12392
12393     if (!stored_player[i].active)
12394       continue;
12395
12396     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12397       return FALSE;
12398   }
12399
12400   return TRUE;
12401 }
12402
12403 void ScrollLevel(int dx, int dy)
12404 {
12405   int scroll_offset = 2 * TILEX_VAR;
12406   int x, y;
12407
12408   BlitBitmap(drawto_field, drawto_field,
12409              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12410              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12411              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12412              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12413              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12414              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12415
12416   if (dx != 0)
12417   {
12418     x = (dx == 1 ? BX1 : BX2);
12419     for (y = BY1; y <= BY2; y++)
12420       DrawScreenField(x, y);
12421   }
12422
12423   if (dy != 0)
12424   {
12425     y = (dy == 1 ? BY1 : BY2);
12426     for (x = BX1; x <= BX2; x++)
12427       DrawScreenField(x, y);
12428   }
12429
12430   redraw_mask |= REDRAW_FIELD;
12431 }
12432
12433 static boolean canFallDown(struct PlayerInfo *player)
12434 {
12435   int jx = player->jx, jy = player->jy;
12436
12437   return (IN_LEV_FIELD(jx, jy + 1) &&
12438           (IS_FREE(jx, jy + 1) ||
12439            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12440           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12441           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12442 }
12443
12444 static boolean canPassField(int x, int y, int move_dir)
12445 {
12446   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12447   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12448   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12449   int nextx = x + dx;
12450   int nexty = y + dy;
12451   int element = Feld[x][y];
12452
12453   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12454           !CAN_MOVE(element) &&
12455           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12456           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12457           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12458 }
12459
12460 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12461 {
12462   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12463   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12464   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12465   int newx = x + dx;
12466   int newy = y + dy;
12467
12468   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12469           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12470           (IS_DIGGABLE(Feld[newx][newy]) ||
12471            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12472            canPassField(newx, newy, move_dir)));
12473 }
12474
12475 static void CheckGravityMovement(struct PlayerInfo *player)
12476 {
12477   if (player->gravity && !player->programmed_action)
12478   {
12479     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12480     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12481     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12482     int jx = player->jx, jy = player->jy;
12483     boolean player_is_moving_to_valid_field =
12484       (!player_is_snapping &&
12485        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12486         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12487     boolean player_can_fall_down = canFallDown(player);
12488
12489     if (player_can_fall_down &&
12490         !player_is_moving_to_valid_field)
12491       player->programmed_action = MV_DOWN;
12492   }
12493 }
12494
12495 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12496 {
12497   return CheckGravityMovement(player);
12498
12499   if (player->gravity && !player->programmed_action)
12500   {
12501     int jx = player->jx, jy = player->jy;
12502     boolean field_under_player_is_free =
12503       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12504     boolean player_is_standing_on_valid_field =
12505       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12506        (IS_WALKABLE(Feld[jx][jy]) &&
12507         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12508
12509     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12510       player->programmed_action = MV_DOWN;
12511   }
12512 }
12513
12514 /*
12515   MovePlayerOneStep()
12516   -----------------------------------------------------------------------------
12517   dx, dy:               direction (non-diagonal) to try to move the player to
12518   real_dx, real_dy:     direction as read from input device (can be diagonal)
12519 */
12520
12521 boolean MovePlayerOneStep(struct PlayerInfo *player,
12522                           int dx, int dy, int real_dx, int real_dy)
12523 {
12524   int jx = player->jx, jy = player->jy;
12525   int new_jx = jx + dx, new_jy = jy + dy;
12526   int can_move;
12527   boolean player_can_move = !player->cannot_move;
12528
12529   if (!player->active || (!dx && !dy))
12530     return MP_NO_ACTION;
12531
12532   player->MovDir = (dx < 0 ? MV_LEFT :
12533                     dx > 0 ? MV_RIGHT :
12534                     dy < 0 ? MV_UP :
12535                     dy > 0 ? MV_DOWN :  MV_NONE);
12536
12537   if (!IN_LEV_FIELD(new_jx, new_jy))
12538     return MP_NO_ACTION;
12539
12540   if (!player_can_move)
12541   {
12542     if (player->MovPos == 0)
12543     {
12544       player->is_moving = FALSE;
12545       player->is_digging = FALSE;
12546       player->is_collecting = FALSE;
12547       player->is_snapping = FALSE;
12548       player->is_pushing = FALSE;
12549     }
12550   }
12551
12552   if (!network.enabled && game.centered_player_nr == -1 &&
12553       !AllPlayersInSight(player, new_jx, new_jy))
12554     return MP_NO_ACTION;
12555
12556   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12557   if (can_move != MP_MOVING)
12558     return can_move;
12559
12560   // check if DigField() has caused relocation of the player
12561   if (player->jx != jx || player->jy != jy)
12562     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12563
12564   StorePlayer[jx][jy] = 0;
12565   player->last_jx = jx;
12566   player->last_jy = jy;
12567   player->jx = new_jx;
12568   player->jy = new_jy;
12569   StorePlayer[new_jx][new_jy] = player->element_nr;
12570
12571   if (player->move_delay_value_next != -1)
12572   {
12573     player->move_delay_value = player->move_delay_value_next;
12574     player->move_delay_value_next = -1;
12575   }
12576
12577   player->MovPos =
12578     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12579
12580   player->step_counter++;
12581
12582   PlayerVisit[jx][jy] = FrameCounter;
12583
12584   player->is_moving = TRUE;
12585
12586 #if 1
12587   // should better be called in MovePlayer(), but this breaks some tapes
12588   ScrollPlayer(player, SCROLL_INIT);
12589 #endif
12590
12591   return MP_MOVING;
12592 }
12593
12594 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12595 {
12596   int jx = player->jx, jy = player->jy;
12597   int old_jx = jx, old_jy = jy;
12598   int moved = MP_NO_ACTION;
12599
12600   if (!player->active)
12601     return FALSE;
12602
12603   if (!dx && !dy)
12604   {
12605     if (player->MovPos == 0)
12606     {
12607       player->is_moving = FALSE;
12608       player->is_digging = FALSE;
12609       player->is_collecting = FALSE;
12610       player->is_snapping = FALSE;
12611       player->is_pushing = FALSE;
12612     }
12613
12614     return FALSE;
12615   }
12616
12617   if (player->move_delay > 0)
12618     return FALSE;
12619
12620   player->move_delay = -1;              // set to "uninitialized" value
12621
12622   // store if player is automatically moved to next field
12623   player->is_auto_moving = (player->programmed_action != MV_NONE);
12624
12625   // remove the last programmed player action
12626   player->programmed_action = 0;
12627
12628   if (player->MovPos)
12629   {
12630     // should only happen if pre-1.2 tape recordings are played
12631     // this is only for backward compatibility
12632
12633     int original_move_delay_value = player->move_delay_value;
12634
12635 #if DEBUG
12636     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12637            tape.counter);
12638 #endif
12639
12640     // scroll remaining steps with finest movement resolution
12641     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12642
12643     while (player->MovPos)
12644     {
12645       ScrollPlayer(player, SCROLL_GO_ON);
12646       ScrollScreen(NULL, SCROLL_GO_ON);
12647
12648       AdvanceFrameAndPlayerCounters(player->index_nr);
12649
12650       DrawAllPlayers();
12651       BackToFront_WithFrameDelay(0);
12652     }
12653
12654     player->move_delay_value = original_move_delay_value;
12655   }
12656
12657   player->is_active = FALSE;
12658
12659   if (player->last_move_dir & MV_HORIZONTAL)
12660   {
12661     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12662       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12663   }
12664   else
12665   {
12666     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12667       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12668   }
12669
12670   if (!moved && !player->is_active)
12671   {
12672     player->is_moving = FALSE;
12673     player->is_digging = FALSE;
12674     player->is_collecting = FALSE;
12675     player->is_snapping = FALSE;
12676     player->is_pushing = FALSE;
12677   }
12678
12679   jx = player->jx;
12680   jy = player->jy;
12681
12682   if (moved & MP_MOVING && !ScreenMovPos &&
12683       (player->index_nr == game.centered_player_nr ||
12684        game.centered_player_nr == -1))
12685   {
12686     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12687
12688     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12689     {
12690       // actual player has left the screen -- scroll in that direction
12691       if (jx != old_jx)         // player has moved horizontally
12692         scroll_x += (jx - old_jx);
12693       else                      // player has moved vertically
12694         scroll_y += (jy - old_jy);
12695     }
12696     else
12697     {
12698       int offset_raw = game.scroll_delay_value;
12699
12700       if (jx != old_jx)         // player has moved horizontally
12701       {
12702         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12703         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12704         int new_scroll_x = jx - MIDPOSX + offset_x;
12705
12706         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12707             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12708           scroll_x = new_scroll_x;
12709
12710         // don't scroll over playfield boundaries
12711         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12712
12713         // don't scroll more than one field at a time
12714         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12715
12716         // don't scroll against the player's moving direction
12717         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12718             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12719           scroll_x = old_scroll_x;
12720       }
12721       else                      // player has moved vertically
12722       {
12723         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12724         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12725         int new_scroll_y = jy - MIDPOSY + offset_y;
12726
12727         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12728             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12729           scroll_y = new_scroll_y;
12730
12731         // don't scroll over playfield boundaries
12732         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12733
12734         // don't scroll more than one field at a time
12735         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12736
12737         // don't scroll against the player's moving direction
12738         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12739             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12740           scroll_y = old_scroll_y;
12741       }
12742     }
12743
12744     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12745     {
12746       if (!network.enabled && game.centered_player_nr == -1 &&
12747           !AllPlayersInVisibleScreen())
12748       {
12749         scroll_x = old_scroll_x;
12750         scroll_y = old_scroll_y;
12751       }
12752       else
12753       {
12754         ScrollScreen(player, SCROLL_INIT);
12755         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12756       }
12757     }
12758   }
12759
12760   player->StepFrame = 0;
12761
12762   if (moved & MP_MOVING)
12763   {
12764     if (old_jx != jx && old_jy == jy)
12765       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12766     else if (old_jx == jx && old_jy != jy)
12767       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12768
12769     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12770
12771     player->last_move_dir = player->MovDir;
12772     player->is_moving = TRUE;
12773     player->is_snapping = FALSE;
12774     player->is_switching = FALSE;
12775     player->is_dropping = FALSE;
12776     player->is_dropping_pressed = FALSE;
12777     player->drop_pressed_delay = 0;
12778
12779 #if 0
12780     // should better be called here than above, but this breaks some tapes
12781     ScrollPlayer(player, SCROLL_INIT);
12782 #endif
12783   }
12784   else
12785   {
12786     CheckGravityMovementWhenNotMoving(player);
12787
12788     player->is_moving = FALSE;
12789
12790     /* at this point, the player is allowed to move, but cannot move right now
12791        (e.g. because of something blocking the way) -- ensure that the player
12792        is also allowed to move in the next frame (in old versions before 3.1.1,
12793        the player was forced to wait again for eight frames before next try) */
12794
12795     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12796       player->move_delay = 0;   // allow direct movement in the next frame
12797   }
12798
12799   if (player->move_delay == -1)         // not yet initialized by DigField()
12800     player->move_delay = player->move_delay_value;
12801
12802   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12803   {
12804     TestIfPlayerTouchesBadThing(jx, jy);
12805     TestIfPlayerTouchesCustomElement(jx, jy);
12806   }
12807
12808   if (!player->active)
12809     RemovePlayer(player);
12810
12811   return moved;
12812 }
12813
12814 void ScrollPlayer(struct PlayerInfo *player, int mode)
12815 {
12816   int jx = player->jx, jy = player->jy;
12817   int last_jx = player->last_jx, last_jy = player->last_jy;
12818   int move_stepsize = TILEX / player->move_delay_value;
12819
12820   if (!player->active)
12821     return;
12822
12823   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12824     return;
12825
12826   if (mode == SCROLL_INIT)
12827   {
12828     player->actual_frame_counter = FrameCounter;
12829     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12830
12831     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12832         Feld[last_jx][last_jy] == EL_EMPTY)
12833     {
12834       int last_field_block_delay = 0;   // start with no blocking at all
12835       int block_delay_adjustment = player->block_delay_adjustment;
12836
12837       // if player blocks last field, add delay for exactly one move
12838       if (player->block_last_field)
12839       {
12840         last_field_block_delay += player->move_delay_value;
12841
12842         // when blocking enabled, prevent moving up despite gravity
12843         if (player->gravity && player->MovDir == MV_UP)
12844           block_delay_adjustment = -1;
12845       }
12846
12847       // add block delay adjustment (also possible when not blocking)
12848       last_field_block_delay += block_delay_adjustment;
12849
12850       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12851       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12852     }
12853
12854     if (player->MovPos != 0)    // player has not yet reached destination
12855       return;
12856   }
12857   else if (!FrameReached(&player->actual_frame_counter, 1))
12858     return;
12859
12860   if (player->MovPos != 0)
12861   {
12862     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12863     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12864
12865     // before DrawPlayer() to draw correct player graphic for this case
12866     if (player->MovPos == 0)
12867       CheckGravityMovement(player);
12868   }
12869
12870   if (player->MovPos == 0)      // player reached destination field
12871   {
12872     if (player->move_delay_reset_counter > 0)
12873     {
12874       player->move_delay_reset_counter--;
12875
12876       if (player->move_delay_reset_counter == 0)
12877       {
12878         // continue with normal speed after quickly moving through gate
12879         HALVE_PLAYER_SPEED(player);
12880
12881         // be able to make the next move without delay
12882         player->move_delay = 0;
12883       }
12884     }
12885
12886     player->last_jx = jx;
12887     player->last_jy = jy;
12888
12889     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12890         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12891         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12892         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12893         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12894         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12895         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12896         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12897     {
12898       ExitPlayer(player);
12899
12900       if (game.players_still_needed == 0 &&
12901           (game.friends_still_needed == 0 ||
12902            IS_SP_ELEMENT(Feld[jx][jy])))
12903         LevelSolved();
12904     }
12905
12906     // this breaks one level: "machine", level 000
12907     {
12908       int move_direction = player->MovDir;
12909       int enter_side = MV_DIR_OPPOSITE(move_direction);
12910       int leave_side = move_direction;
12911       int old_jx = last_jx;
12912       int old_jy = last_jy;
12913       int old_element = Feld[old_jx][old_jy];
12914       int new_element = Feld[jx][jy];
12915
12916       if (IS_CUSTOM_ELEMENT(old_element))
12917         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12918                                    CE_LEFT_BY_PLAYER,
12919                                    player->index_bit, leave_side);
12920
12921       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12922                                           CE_PLAYER_LEAVES_X,
12923                                           player->index_bit, leave_side);
12924
12925       if (IS_CUSTOM_ELEMENT(new_element))
12926         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12927                                    player->index_bit, enter_side);
12928
12929       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12930                                           CE_PLAYER_ENTERS_X,
12931                                           player->index_bit, enter_side);
12932
12933       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12934                                         CE_MOVE_OF_X, move_direction);
12935     }
12936
12937     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12938     {
12939       TestIfPlayerTouchesBadThing(jx, jy);
12940       TestIfPlayerTouchesCustomElement(jx, jy);
12941
12942       /* needed because pushed element has not yet reached its destination,
12943          so it would trigger a change event at its previous field location */
12944       if (!player->is_pushing)
12945         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12946
12947       if (!player->active)
12948         RemovePlayer(player);
12949     }
12950
12951     if (!game.LevelSolved && level.use_step_counter)
12952     {
12953       int i;
12954
12955       TimePlayed++;
12956
12957       if (TimeLeft > 0)
12958       {
12959         TimeLeft--;
12960
12961         if (TimeLeft <= 10 && setup.time_limit)
12962           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12963
12964         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12965
12966         DisplayGameControlValues();
12967
12968         if (!TimeLeft && setup.time_limit)
12969           for (i = 0; i < MAX_PLAYERS; i++)
12970             KillPlayer(&stored_player[i]);
12971       }
12972       else if (game.no_time_limit && !game.all_players_gone)
12973       {
12974         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12975
12976         DisplayGameControlValues();
12977       }
12978     }
12979
12980     if (tape.single_step && tape.recording && !tape.pausing &&
12981         !player->programmed_action)
12982       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12983
12984     if (!player->programmed_action)
12985       CheckSaveEngineSnapshot(player);
12986   }
12987 }
12988
12989 void ScrollScreen(struct PlayerInfo *player, int mode)
12990 {
12991   static unsigned int screen_frame_counter = 0;
12992
12993   if (mode == SCROLL_INIT)
12994   {
12995     // set scrolling step size according to actual player's moving speed
12996     ScrollStepSize = TILEX / player->move_delay_value;
12997
12998     screen_frame_counter = FrameCounter;
12999     ScreenMovDir = player->MovDir;
13000     ScreenMovPos = player->MovPos;
13001     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13002     return;
13003   }
13004   else if (!FrameReached(&screen_frame_counter, 1))
13005     return;
13006
13007   if (ScreenMovPos)
13008   {
13009     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13010     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13011     redraw_mask |= REDRAW_FIELD;
13012   }
13013   else
13014     ScreenMovDir = MV_NONE;
13015 }
13016
13017 void TestIfPlayerTouchesCustomElement(int x, int y)
13018 {
13019   static int xy[4][2] =
13020   {
13021     { 0, -1 },
13022     { -1, 0 },
13023     { +1, 0 },
13024     { 0, +1 }
13025   };
13026   static int trigger_sides[4][2] =
13027   {
13028     // center side       border side
13029     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13030     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13031     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13032     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13033   };
13034   static int touch_dir[4] =
13035   {
13036     MV_LEFT | MV_RIGHT,
13037     MV_UP   | MV_DOWN,
13038     MV_UP   | MV_DOWN,
13039     MV_LEFT | MV_RIGHT
13040   };
13041   int center_element = Feld[x][y];      // should always be non-moving!
13042   int i;
13043
13044   for (i = 0; i < NUM_DIRECTIONS; i++)
13045   {
13046     int xx = x + xy[i][0];
13047     int yy = y + xy[i][1];
13048     int center_side = trigger_sides[i][0];
13049     int border_side = trigger_sides[i][1];
13050     int border_element;
13051
13052     if (!IN_LEV_FIELD(xx, yy))
13053       continue;
13054
13055     if (IS_PLAYER(x, y))                // player found at center element
13056     {
13057       struct PlayerInfo *player = PLAYERINFO(x, y);
13058
13059       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13060         border_element = Feld[xx][yy];          // may be moving!
13061       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13062         border_element = Feld[xx][yy];
13063       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13064         border_element = MovingOrBlocked2Element(xx, yy);
13065       else
13066         continue;               // center and border element do not touch
13067
13068       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13069                                  player->index_bit, border_side);
13070       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13071                                           CE_PLAYER_TOUCHES_X,
13072                                           player->index_bit, border_side);
13073
13074       {
13075         /* use player element that is initially defined in the level playfield,
13076            not the player element that corresponds to the runtime player number
13077            (example: a level that contains EL_PLAYER_3 as the only player would
13078            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13079         int player_element = PLAYERINFO(x, y)->initial_element;
13080
13081         CheckElementChangeBySide(xx, yy, border_element, player_element,
13082                                  CE_TOUCHING_X, border_side);
13083       }
13084     }
13085     else if (IS_PLAYER(xx, yy))         // player found at border element
13086     {
13087       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13088
13089       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13090       {
13091         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13092           continue;             // center and border element do not touch
13093       }
13094
13095       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13096                                  player->index_bit, center_side);
13097       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13098                                           CE_PLAYER_TOUCHES_X,
13099                                           player->index_bit, center_side);
13100
13101       {
13102         /* use player element that is initially defined in the level playfield,
13103            not the player element that corresponds to the runtime player number
13104            (example: a level that contains EL_PLAYER_3 as the only player would
13105            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13106         int player_element = PLAYERINFO(xx, yy)->initial_element;
13107
13108         CheckElementChangeBySide(x, y, center_element, player_element,
13109                                  CE_TOUCHING_X, center_side);
13110       }
13111
13112       break;
13113     }
13114   }
13115 }
13116
13117 void TestIfElementTouchesCustomElement(int x, int y)
13118 {
13119   static int xy[4][2] =
13120   {
13121     { 0, -1 },
13122     { -1, 0 },
13123     { +1, 0 },
13124     { 0, +1 }
13125   };
13126   static int trigger_sides[4][2] =
13127   {
13128     // center side      border side
13129     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13130     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13131     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13132     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13133   };
13134   static int touch_dir[4] =
13135   {
13136     MV_LEFT | MV_RIGHT,
13137     MV_UP   | MV_DOWN,
13138     MV_UP   | MV_DOWN,
13139     MV_LEFT | MV_RIGHT
13140   };
13141   boolean change_center_element = FALSE;
13142   int center_element = Feld[x][y];      // should always be non-moving!
13143   int border_element_old[NUM_DIRECTIONS];
13144   int i;
13145
13146   for (i = 0; i < NUM_DIRECTIONS; i++)
13147   {
13148     int xx = x + xy[i][0];
13149     int yy = y + xy[i][1];
13150     int border_element;
13151
13152     border_element_old[i] = -1;
13153
13154     if (!IN_LEV_FIELD(xx, yy))
13155       continue;
13156
13157     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13158       border_element = Feld[xx][yy];    // may be moving!
13159     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13160       border_element = Feld[xx][yy];
13161     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13162       border_element = MovingOrBlocked2Element(xx, yy);
13163     else
13164       continue;                 // center and border element do not touch
13165
13166     border_element_old[i] = border_element;
13167   }
13168
13169   for (i = 0; i < NUM_DIRECTIONS; i++)
13170   {
13171     int xx = x + xy[i][0];
13172     int yy = y + xy[i][1];
13173     int center_side = trigger_sides[i][0];
13174     int border_element = border_element_old[i];
13175
13176     if (border_element == -1)
13177       continue;
13178
13179     // check for change of border element
13180     CheckElementChangeBySide(xx, yy, border_element, center_element,
13181                              CE_TOUCHING_X, center_side);
13182
13183     // (center element cannot be player, so we dont have to check this here)
13184   }
13185
13186   for (i = 0; i < NUM_DIRECTIONS; i++)
13187   {
13188     int xx = x + xy[i][0];
13189     int yy = y + xy[i][1];
13190     int border_side = trigger_sides[i][1];
13191     int border_element = border_element_old[i];
13192
13193     if (border_element == -1)
13194       continue;
13195
13196     // check for change of center element (but change it only once)
13197     if (!change_center_element)
13198       change_center_element =
13199         CheckElementChangeBySide(x, y, center_element, border_element,
13200                                  CE_TOUCHING_X, border_side);
13201
13202     if (IS_PLAYER(xx, yy))
13203     {
13204       /* use player element that is initially defined in the level playfield,
13205          not the player element that corresponds to the runtime player number
13206          (example: a level that contains EL_PLAYER_3 as the only player would
13207          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13208       int player_element = PLAYERINFO(xx, yy)->initial_element;
13209
13210       CheckElementChangeBySide(x, y, center_element, player_element,
13211                                CE_TOUCHING_X, border_side);
13212     }
13213   }
13214 }
13215
13216 void TestIfElementHitsCustomElement(int x, int y, int direction)
13217 {
13218   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13219   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13220   int hitx = x + dx, hity = y + dy;
13221   int hitting_element = Feld[x][y];
13222   int touched_element;
13223
13224   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13225     return;
13226
13227   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13228                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13229
13230   if (IN_LEV_FIELD(hitx, hity))
13231   {
13232     int opposite_direction = MV_DIR_OPPOSITE(direction);
13233     int hitting_side = direction;
13234     int touched_side = opposite_direction;
13235     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13236                           MovDir[hitx][hity] != direction ||
13237                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13238
13239     object_hit = TRUE;
13240
13241     if (object_hit)
13242     {
13243       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13244                                CE_HITTING_X, touched_side);
13245
13246       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13247                                CE_HIT_BY_X, hitting_side);
13248
13249       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13250                                CE_HIT_BY_SOMETHING, opposite_direction);
13251
13252       if (IS_PLAYER(hitx, hity))
13253       {
13254         /* use player element that is initially defined in the level playfield,
13255            not the player element that corresponds to the runtime player number
13256            (example: a level that contains EL_PLAYER_3 as the only player would
13257            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13258         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13259
13260         CheckElementChangeBySide(x, y, hitting_element, player_element,
13261                                  CE_HITTING_X, touched_side);
13262       }
13263     }
13264   }
13265
13266   // "hitting something" is also true when hitting the playfield border
13267   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13268                            CE_HITTING_SOMETHING, direction);
13269 }
13270
13271 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13272 {
13273   int i, kill_x = -1, kill_y = -1;
13274
13275   int bad_element = -1;
13276   static int test_xy[4][2] =
13277   {
13278     { 0, -1 },
13279     { -1, 0 },
13280     { +1, 0 },
13281     { 0, +1 }
13282   };
13283   static int test_dir[4] =
13284   {
13285     MV_UP,
13286     MV_LEFT,
13287     MV_RIGHT,
13288     MV_DOWN
13289   };
13290
13291   for (i = 0; i < NUM_DIRECTIONS; i++)
13292   {
13293     int test_x, test_y, test_move_dir, test_element;
13294
13295     test_x = good_x + test_xy[i][0];
13296     test_y = good_y + test_xy[i][1];
13297
13298     if (!IN_LEV_FIELD(test_x, test_y))
13299       continue;
13300
13301     test_move_dir =
13302       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13303
13304     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13305
13306     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13307        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13308     */
13309     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13310         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13311     {
13312       kill_x = test_x;
13313       kill_y = test_y;
13314       bad_element = test_element;
13315
13316       break;
13317     }
13318   }
13319
13320   if (kill_x != -1 || kill_y != -1)
13321   {
13322     if (IS_PLAYER(good_x, good_y))
13323     {
13324       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13325
13326       if (player->shield_deadly_time_left > 0 &&
13327           !IS_INDESTRUCTIBLE(bad_element))
13328         Bang(kill_x, kill_y);
13329       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13330         KillPlayer(player);
13331     }
13332     else
13333       Bang(good_x, good_y);
13334   }
13335 }
13336
13337 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13338 {
13339   int i, kill_x = -1, kill_y = -1;
13340   int bad_element = Feld[bad_x][bad_y];
13341   static int test_xy[4][2] =
13342   {
13343     { 0, -1 },
13344     { -1, 0 },
13345     { +1, 0 },
13346     { 0, +1 }
13347   };
13348   static int touch_dir[4] =
13349   {
13350     MV_LEFT | MV_RIGHT,
13351     MV_UP   | MV_DOWN,
13352     MV_UP   | MV_DOWN,
13353     MV_LEFT | MV_RIGHT
13354   };
13355   static int test_dir[4] =
13356   {
13357     MV_UP,
13358     MV_LEFT,
13359     MV_RIGHT,
13360     MV_DOWN
13361   };
13362
13363   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13364     return;
13365
13366   for (i = 0; i < NUM_DIRECTIONS; i++)
13367   {
13368     int test_x, test_y, test_move_dir, test_element;
13369
13370     test_x = bad_x + test_xy[i][0];
13371     test_y = bad_y + test_xy[i][1];
13372
13373     if (!IN_LEV_FIELD(test_x, test_y))
13374       continue;
13375
13376     test_move_dir =
13377       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13378
13379     test_element = Feld[test_x][test_y];
13380
13381     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13382        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13383     */
13384     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13385         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13386     {
13387       // good thing is player or penguin that does not move away
13388       if (IS_PLAYER(test_x, test_y))
13389       {
13390         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13391
13392         if (bad_element == EL_ROBOT && player->is_moving)
13393           continue;     // robot does not kill player if he is moving
13394
13395         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13396         {
13397           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13398             continue;           // center and border element do not touch
13399         }
13400
13401         kill_x = test_x;
13402         kill_y = test_y;
13403
13404         break;
13405       }
13406       else if (test_element == EL_PENGUIN)
13407       {
13408         kill_x = test_x;
13409         kill_y = test_y;
13410
13411         break;
13412       }
13413     }
13414   }
13415
13416   if (kill_x != -1 || kill_y != -1)
13417   {
13418     if (IS_PLAYER(kill_x, kill_y))
13419     {
13420       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13421
13422       if (player->shield_deadly_time_left > 0 &&
13423           !IS_INDESTRUCTIBLE(bad_element))
13424         Bang(bad_x, bad_y);
13425       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13426         KillPlayer(player);
13427     }
13428     else
13429       Bang(kill_x, kill_y);
13430   }
13431 }
13432
13433 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13434 {
13435   int bad_element = Feld[bad_x][bad_y];
13436   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13437   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13438   int test_x = bad_x + dx, test_y = bad_y + dy;
13439   int test_move_dir, test_element;
13440   int kill_x = -1, kill_y = -1;
13441
13442   if (!IN_LEV_FIELD(test_x, test_y))
13443     return;
13444
13445   test_move_dir =
13446     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13447
13448   test_element = Feld[test_x][test_y];
13449
13450   if (test_move_dir != bad_move_dir)
13451   {
13452     // good thing can be player or penguin that does not move away
13453     if (IS_PLAYER(test_x, test_y))
13454     {
13455       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13456
13457       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13458          player as being hit when he is moving towards the bad thing, because
13459          the "get hit by" condition would be lost after the player stops) */
13460       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13461         return;         // player moves away from bad thing
13462
13463       kill_x = test_x;
13464       kill_y = test_y;
13465     }
13466     else if (test_element == EL_PENGUIN)
13467     {
13468       kill_x = test_x;
13469       kill_y = test_y;
13470     }
13471   }
13472
13473   if (kill_x != -1 || kill_y != -1)
13474   {
13475     if (IS_PLAYER(kill_x, kill_y))
13476     {
13477       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13478
13479       if (player->shield_deadly_time_left > 0 &&
13480           !IS_INDESTRUCTIBLE(bad_element))
13481         Bang(bad_x, bad_y);
13482       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13483         KillPlayer(player);
13484     }
13485     else
13486       Bang(kill_x, kill_y);
13487   }
13488 }
13489
13490 void TestIfPlayerTouchesBadThing(int x, int y)
13491 {
13492   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13493 }
13494
13495 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13496 {
13497   TestIfGoodThingHitsBadThing(x, y, move_dir);
13498 }
13499
13500 void TestIfBadThingTouchesPlayer(int x, int y)
13501 {
13502   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13503 }
13504
13505 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13506 {
13507   TestIfBadThingHitsGoodThing(x, y, move_dir);
13508 }
13509
13510 void TestIfFriendTouchesBadThing(int x, int y)
13511 {
13512   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13513 }
13514
13515 void TestIfBadThingTouchesFriend(int x, int y)
13516 {
13517   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13518 }
13519
13520 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13521 {
13522   int i, kill_x = bad_x, kill_y = bad_y;
13523   static int xy[4][2] =
13524   {
13525     { 0, -1 },
13526     { -1, 0 },
13527     { +1, 0 },
13528     { 0, +1 }
13529   };
13530
13531   for (i = 0; i < NUM_DIRECTIONS; i++)
13532   {
13533     int x, y, element;
13534
13535     x = bad_x + xy[i][0];
13536     y = bad_y + xy[i][1];
13537     if (!IN_LEV_FIELD(x, y))
13538       continue;
13539
13540     element = Feld[x][y];
13541     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13542         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13543     {
13544       kill_x = x;
13545       kill_y = y;
13546       break;
13547     }
13548   }
13549
13550   if (kill_x != bad_x || kill_y != bad_y)
13551     Bang(bad_x, bad_y);
13552 }
13553
13554 void KillPlayer(struct PlayerInfo *player)
13555 {
13556   int jx = player->jx, jy = player->jy;
13557
13558   if (!player->active)
13559     return;
13560
13561 #if 0
13562   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13563          player->killed, player->active, player->reanimated);
13564 #endif
13565
13566   /* the following code was introduced to prevent an infinite loop when calling
13567      -> Bang()
13568      -> CheckTriggeredElementChangeExt()
13569      -> ExecuteCustomElementAction()
13570      -> KillPlayer()
13571      -> (infinitely repeating the above sequence of function calls)
13572      which occurs when killing the player while having a CE with the setting
13573      "kill player X when explosion of <player X>"; the solution using a new
13574      field "player->killed" was chosen for backwards compatibility, although
13575      clever use of the fields "player->active" etc. would probably also work */
13576 #if 1
13577   if (player->killed)
13578     return;
13579 #endif
13580
13581   player->killed = TRUE;
13582
13583   // remove accessible field at the player's position
13584   Feld[jx][jy] = EL_EMPTY;
13585
13586   // deactivate shield (else Bang()/Explode() would not work right)
13587   player->shield_normal_time_left = 0;
13588   player->shield_deadly_time_left = 0;
13589
13590 #if 0
13591   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13592          player->killed, player->active, player->reanimated);
13593 #endif
13594
13595   Bang(jx, jy);
13596
13597 #if 0
13598   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13599          player->killed, player->active, player->reanimated);
13600 #endif
13601
13602   if (player->reanimated)       // killed player may have been reanimated
13603     player->killed = player->reanimated = FALSE;
13604   else
13605     BuryPlayer(player);
13606 }
13607
13608 static void KillPlayerUnlessEnemyProtected(int x, int y)
13609 {
13610   if (!PLAYER_ENEMY_PROTECTED(x, y))
13611     KillPlayer(PLAYERINFO(x, y));
13612 }
13613
13614 static void KillPlayerUnlessExplosionProtected(int x, int y)
13615 {
13616   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13617     KillPlayer(PLAYERINFO(x, y));
13618 }
13619
13620 void BuryPlayer(struct PlayerInfo *player)
13621 {
13622   int jx = player->jx, jy = player->jy;
13623
13624   if (!player->active)
13625     return;
13626
13627   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13628   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13629
13630   RemovePlayer(player);
13631
13632   player->buried = TRUE;
13633
13634   if (game.all_players_gone)
13635     game.GameOver = TRUE;
13636 }
13637
13638 void RemovePlayer(struct PlayerInfo *player)
13639 {
13640   int jx = player->jx, jy = player->jy;
13641   int i, found = FALSE;
13642
13643   player->present = FALSE;
13644   player->active = FALSE;
13645
13646   // required for some CE actions (even if the player is not active anymore)
13647   player->MovPos = 0;
13648
13649   if (!ExplodeField[jx][jy])
13650     StorePlayer[jx][jy] = 0;
13651
13652   if (player->is_moving)
13653     TEST_DrawLevelField(player->last_jx, player->last_jy);
13654
13655   for (i = 0; i < MAX_PLAYERS; i++)
13656     if (stored_player[i].active)
13657       found = TRUE;
13658
13659   if (!found)
13660   {
13661     game.all_players_gone = TRUE;
13662     game.GameOver = TRUE;
13663   }
13664
13665   game.exit_x = game.robot_wheel_x = jx;
13666   game.exit_y = game.robot_wheel_y = jy;
13667 }
13668
13669 void ExitPlayer(struct PlayerInfo *player)
13670 {
13671   DrawPlayer(player);   // needed here only to cleanup last field
13672   RemovePlayer(player);
13673
13674   if (game.players_still_needed > 0)
13675     game.players_still_needed--;
13676 }
13677
13678 static void setFieldForSnapping(int x, int y, int element, int direction)
13679 {
13680   struct ElementInfo *ei = &element_info[element];
13681   int direction_bit = MV_DIR_TO_BIT(direction);
13682   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13683   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13684                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13685
13686   Feld[x][y] = EL_ELEMENT_SNAPPING;
13687   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13688
13689   ResetGfxAnimation(x, y);
13690
13691   GfxElement[x][y] = element;
13692   GfxAction[x][y] = action;
13693   GfxDir[x][y] = direction;
13694   GfxFrame[x][y] = -1;
13695 }
13696
13697 /*
13698   =============================================================================
13699   checkDiagonalPushing()
13700   -----------------------------------------------------------------------------
13701   check if diagonal input device direction results in pushing of object
13702   (by checking if the alternative direction is walkable, diggable, ...)
13703   =============================================================================
13704 */
13705
13706 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13707                                     int x, int y, int real_dx, int real_dy)
13708 {
13709   int jx, jy, dx, dy, xx, yy;
13710
13711   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13712     return TRUE;
13713
13714   // diagonal direction: check alternative direction
13715   jx = player->jx;
13716   jy = player->jy;
13717   dx = x - jx;
13718   dy = y - jy;
13719   xx = jx + (dx == 0 ? real_dx : 0);
13720   yy = jy + (dy == 0 ? real_dy : 0);
13721
13722   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13723 }
13724
13725 /*
13726   =============================================================================
13727   DigField()
13728   -----------------------------------------------------------------------------
13729   x, y:                 field next to player (non-diagonal) to try to dig to
13730   real_dx, real_dy:     direction as read from input device (can be diagonal)
13731   =============================================================================
13732 */
13733
13734 static int DigField(struct PlayerInfo *player,
13735                     int oldx, int oldy, int x, int y,
13736                     int real_dx, int real_dy, int mode)
13737 {
13738   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13739   boolean player_was_pushing = player->is_pushing;
13740   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13741   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13742   int jx = oldx, jy = oldy;
13743   int dx = x - jx, dy = y - jy;
13744   int nextx = x + dx, nexty = y + dy;
13745   int move_direction = (dx == -1 ? MV_LEFT  :
13746                         dx == +1 ? MV_RIGHT :
13747                         dy == -1 ? MV_UP    :
13748                         dy == +1 ? MV_DOWN  : MV_NONE);
13749   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13750   int dig_side = MV_DIR_OPPOSITE(move_direction);
13751   int old_element = Feld[jx][jy];
13752   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13753   int collect_count;
13754
13755   if (is_player)                // function can also be called by EL_PENGUIN
13756   {
13757     if (player->MovPos == 0)
13758     {
13759       player->is_digging = FALSE;
13760       player->is_collecting = FALSE;
13761     }
13762
13763     if (player->MovPos == 0)    // last pushing move finished
13764       player->is_pushing = FALSE;
13765
13766     if (mode == DF_NO_PUSH)     // player just stopped pushing
13767     {
13768       player->is_switching = FALSE;
13769       player->push_delay = -1;
13770
13771       return MP_NO_ACTION;
13772     }
13773   }
13774
13775   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13776     old_element = Back[jx][jy];
13777
13778   // in case of element dropped at player position, check background
13779   else if (Back[jx][jy] != EL_EMPTY &&
13780            game.engine_version >= VERSION_IDENT(2,2,0,0))
13781     old_element = Back[jx][jy];
13782
13783   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13784     return MP_NO_ACTION;        // field has no opening in this direction
13785
13786   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13787     return MP_NO_ACTION;        // field has no opening in this direction
13788
13789   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13790   {
13791     SplashAcid(x, y);
13792
13793     Feld[jx][jy] = player->artwork_element;
13794     InitMovingField(jx, jy, MV_DOWN);
13795     Store[jx][jy] = EL_ACID;
13796     ContinueMoving(jx, jy);
13797     BuryPlayer(player);
13798
13799     return MP_DONT_RUN_INTO;
13800   }
13801
13802   if (player_can_move && DONT_RUN_INTO(element))
13803   {
13804     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13805
13806     return MP_DONT_RUN_INTO;
13807   }
13808
13809   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13810     return MP_NO_ACTION;
13811
13812   collect_count = element_info[element].collect_count_initial;
13813
13814   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13815     return MP_NO_ACTION;
13816
13817   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13818     player_can_move = player_can_move_or_snap;
13819
13820   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13821       game.engine_version >= VERSION_IDENT(2,2,0,0))
13822   {
13823     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13824                                player->index_bit, dig_side);
13825     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13826                                         player->index_bit, dig_side);
13827
13828     if (element == EL_DC_LANDMINE)
13829       Bang(x, y);
13830
13831     if (Feld[x][y] != element)          // field changed by snapping
13832       return MP_ACTION;
13833
13834     return MP_NO_ACTION;
13835   }
13836
13837   if (player->gravity && is_player && !player->is_auto_moving &&
13838       canFallDown(player) && move_direction != MV_DOWN &&
13839       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13840     return MP_NO_ACTION;        // player cannot walk here due to gravity
13841
13842   if (player_can_move &&
13843       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13844   {
13845     int sound_element = SND_ELEMENT(element);
13846     int sound_action = ACTION_WALKING;
13847
13848     if (IS_RND_GATE(element))
13849     {
13850       if (!player->key[RND_GATE_NR(element)])
13851         return MP_NO_ACTION;
13852     }
13853     else if (IS_RND_GATE_GRAY(element))
13854     {
13855       if (!player->key[RND_GATE_GRAY_NR(element)])
13856         return MP_NO_ACTION;
13857     }
13858     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13859     {
13860       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13861         return MP_NO_ACTION;
13862     }
13863     else if (element == EL_EXIT_OPEN ||
13864              element == EL_EM_EXIT_OPEN ||
13865              element == EL_EM_EXIT_OPENING ||
13866              element == EL_STEEL_EXIT_OPEN ||
13867              element == EL_EM_STEEL_EXIT_OPEN ||
13868              element == EL_EM_STEEL_EXIT_OPENING ||
13869              element == EL_SP_EXIT_OPEN ||
13870              element == EL_SP_EXIT_OPENING)
13871     {
13872       sound_action = ACTION_PASSING;    // player is passing exit
13873     }
13874     else if (element == EL_EMPTY)
13875     {
13876       sound_action = ACTION_MOVING;             // nothing to walk on
13877     }
13878
13879     // play sound from background or player, whatever is available
13880     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13881       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13882     else
13883       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13884   }
13885   else if (player_can_move &&
13886            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13887   {
13888     if (!ACCESS_FROM(element, opposite_direction))
13889       return MP_NO_ACTION;      // field not accessible from this direction
13890
13891     if (CAN_MOVE(element))      // only fixed elements can be passed!
13892       return MP_NO_ACTION;
13893
13894     if (IS_EM_GATE(element))
13895     {
13896       if (!player->key[EM_GATE_NR(element)])
13897         return MP_NO_ACTION;
13898     }
13899     else if (IS_EM_GATE_GRAY(element))
13900     {
13901       if (!player->key[EM_GATE_GRAY_NR(element)])
13902         return MP_NO_ACTION;
13903     }
13904     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13905     {
13906       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13907         return MP_NO_ACTION;
13908     }
13909     else if (IS_EMC_GATE(element))
13910     {
13911       if (!player->key[EMC_GATE_NR(element)])
13912         return MP_NO_ACTION;
13913     }
13914     else if (IS_EMC_GATE_GRAY(element))
13915     {
13916       if (!player->key[EMC_GATE_GRAY_NR(element)])
13917         return MP_NO_ACTION;
13918     }
13919     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13920     {
13921       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13922         return MP_NO_ACTION;
13923     }
13924     else if (element == EL_DC_GATE_WHITE ||
13925              element == EL_DC_GATE_WHITE_GRAY ||
13926              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13927     {
13928       if (player->num_white_keys == 0)
13929         return MP_NO_ACTION;
13930
13931       player->num_white_keys--;
13932     }
13933     else if (IS_SP_PORT(element))
13934     {
13935       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13936           element == EL_SP_GRAVITY_PORT_RIGHT ||
13937           element == EL_SP_GRAVITY_PORT_UP ||
13938           element == EL_SP_GRAVITY_PORT_DOWN)
13939         player->gravity = !player->gravity;
13940       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13941                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13942                element == EL_SP_GRAVITY_ON_PORT_UP ||
13943                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13944         player->gravity = TRUE;
13945       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13946                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13947                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13948                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13949         player->gravity = FALSE;
13950     }
13951
13952     // automatically move to the next field with double speed
13953     player->programmed_action = move_direction;
13954
13955     if (player->move_delay_reset_counter == 0)
13956     {
13957       player->move_delay_reset_counter = 2;     // two double speed steps
13958
13959       DOUBLE_PLAYER_SPEED(player);
13960     }
13961
13962     PlayLevelSoundAction(x, y, ACTION_PASSING);
13963   }
13964   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13965   {
13966     RemoveField(x, y);
13967
13968     if (mode != DF_SNAP)
13969     {
13970       GfxElement[x][y] = GFX_ELEMENT(element);
13971       player->is_digging = TRUE;
13972     }
13973
13974     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13975
13976     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13977                                         player->index_bit, dig_side);
13978
13979     if (mode == DF_SNAP)
13980     {
13981       if (level.block_snap_field)
13982         setFieldForSnapping(x, y, element, move_direction);
13983       else
13984         TestIfElementTouchesCustomElement(x, y);        // for empty space
13985
13986       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13987                                           player->index_bit, dig_side);
13988     }
13989   }
13990   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13991   {
13992     RemoveField(x, y);
13993
13994     if (is_player && mode != DF_SNAP)
13995     {
13996       GfxElement[x][y] = element;
13997       player->is_collecting = TRUE;
13998     }
13999
14000     if (element == EL_SPEED_PILL)
14001     {
14002       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14003     }
14004     else if (element == EL_EXTRA_TIME && level.time > 0)
14005     {
14006       TimeLeft += level.extra_time;
14007
14008       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14009
14010       DisplayGameControlValues();
14011     }
14012     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14013     {
14014       player->shield_normal_time_left += level.shield_normal_time;
14015       if (element == EL_SHIELD_DEADLY)
14016         player->shield_deadly_time_left += level.shield_deadly_time;
14017     }
14018     else if (element == EL_DYNAMITE ||
14019              element == EL_EM_DYNAMITE ||
14020              element == EL_SP_DISK_RED)
14021     {
14022       if (player->inventory_size < MAX_INVENTORY_SIZE)
14023         player->inventory_element[player->inventory_size++] = element;
14024
14025       DrawGameDoorValues();
14026     }
14027     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14028     {
14029       player->dynabomb_count++;
14030       player->dynabombs_left++;
14031     }
14032     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14033     {
14034       player->dynabomb_size++;
14035     }
14036     else if (element == EL_DYNABOMB_INCREASE_POWER)
14037     {
14038       player->dynabomb_xl = TRUE;
14039     }
14040     else if (IS_KEY(element))
14041     {
14042       player->key[KEY_NR(element)] = TRUE;
14043
14044       DrawGameDoorValues();
14045     }
14046     else if (element == EL_DC_KEY_WHITE)
14047     {
14048       player->num_white_keys++;
14049
14050       // display white keys?
14051       // DrawGameDoorValues();
14052     }
14053     else if (IS_ENVELOPE(element))
14054     {
14055       player->show_envelope = element;
14056     }
14057     else if (element == EL_EMC_LENSES)
14058     {
14059       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14060
14061       RedrawAllInvisibleElementsForLenses();
14062     }
14063     else if (element == EL_EMC_MAGNIFIER)
14064     {
14065       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14066
14067       RedrawAllInvisibleElementsForMagnifier();
14068     }
14069     else if (IS_DROPPABLE(element) ||
14070              IS_THROWABLE(element))     // can be collected and dropped
14071     {
14072       int i;
14073
14074       if (collect_count == 0)
14075         player->inventory_infinite_element = element;
14076       else
14077         for (i = 0; i < collect_count; i++)
14078           if (player->inventory_size < MAX_INVENTORY_SIZE)
14079             player->inventory_element[player->inventory_size++] = element;
14080
14081       DrawGameDoorValues();
14082     }
14083     else if (collect_count > 0)
14084     {
14085       game.gems_still_needed -= collect_count;
14086       if (game.gems_still_needed < 0)
14087         game.gems_still_needed = 0;
14088
14089       game.snapshot.collected_item = TRUE;
14090
14091       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14092
14093       DisplayGameControlValues();
14094     }
14095
14096     RaiseScoreElement(element);
14097     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14098
14099     if (is_player)
14100       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14101                                           player->index_bit, dig_side);
14102
14103     if (mode == DF_SNAP)
14104     {
14105       if (level.block_snap_field)
14106         setFieldForSnapping(x, y, element, move_direction);
14107       else
14108         TestIfElementTouchesCustomElement(x, y);        // for empty space
14109
14110       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14111                                           player->index_bit, dig_side);
14112     }
14113   }
14114   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14115   {
14116     if (mode == DF_SNAP && element != EL_BD_ROCK)
14117       return MP_NO_ACTION;
14118
14119     if (CAN_FALL(element) && dy)
14120       return MP_NO_ACTION;
14121
14122     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14123         !(element == EL_SPRING && level.use_spring_bug))
14124       return MP_NO_ACTION;
14125
14126     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14127         ((move_direction & MV_VERTICAL &&
14128           ((element_info[element].move_pattern & MV_LEFT &&
14129             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14130            (element_info[element].move_pattern & MV_RIGHT &&
14131             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14132          (move_direction & MV_HORIZONTAL &&
14133           ((element_info[element].move_pattern & MV_UP &&
14134             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14135            (element_info[element].move_pattern & MV_DOWN &&
14136             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14137       return MP_NO_ACTION;
14138
14139     // do not push elements already moving away faster than player
14140     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14141         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14142       return MP_NO_ACTION;
14143
14144     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14145     {
14146       if (player->push_delay_value == -1 || !player_was_pushing)
14147         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14148     }
14149     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14150     {
14151       if (player->push_delay_value == -1)
14152         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14153     }
14154     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14155     {
14156       if (!player->is_pushing)
14157         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14158     }
14159
14160     player->is_pushing = TRUE;
14161     player->is_active = TRUE;
14162
14163     if (!(IN_LEV_FIELD(nextx, nexty) &&
14164           (IS_FREE(nextx, nexty) ||
14165            (IS_SB_ELEMENT(element) &&
14166             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14167            (IS_CUSTOM_ELEMENT(element) &&
14168             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14169       return MP_NO_ACTION;
14170
14171     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14172       return MP_NO_ACTION;
14173
14174     if (player->push_delay == -1)       // new pushing; restart delay
14175       player->push_delay = 0;
14176
14177     if (player->push_delay < player->push_delay_value &&
14178         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14179         element != EL_SPRING && element != EL_BALLOON)
14180     {
14181       // make sure that there is no move delay before next try to push
14182       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14183         player->move_delay = 0;
14184
14185       return MP_NO_ACTION;
14186     }
14187
14188     if (IS_CUSTOM_ELEMENT(element) &&
14189         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14190     {
14191       if (!DigFieldByCE(nextx, nexty, element))
14192         return MP_NO_ACTION;
14193     }
14194
14195     if (IS_SB_ELEMENT(element))
14196     {
14197       boolean sokoban_task_solved = FALSE;
14198
14199       if (element == EL_SOKOBAN_FIELD_FULL)
14200       {
14201         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14202
14203         IncrementSokobanFieldsNeeded();
14204         IncrementSokobanObjectsNeeded();
14205       }
14206
14207       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14208       {
14209         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14210
14211         DecrementSokobanFieldsNeeded();
14212         DecrementSokobanObjectsNeeded();
14213
14214         // sokoban object was pushed from empty field to sokoban field
14215         if (Back[x][y] == EL_EMPTY)
14216           sokoban_task_solved = TRUE;
14217       }
14218
14219       Feld[x][y] = EL_SOKOBAN_OBJECT;
14220
14221       if (Back[x][y] == Back[nextx][nexty])
14222         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14223       else if (Back[x][y] != 0)
14224         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14225                                     ACTION_EMPTYING);
14226       else
14227         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14228                                     ACTION_FILLING);
14229
14230       if (sokoban_task_solved &&
14231           game.sokoban_fields_still_needed == 0 &&
14232           game.sokoban_objects_still_needed == 0 &&
14233           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14234       {
14235         game.players_still_needed = 0;
14236
14237         LevelSolved();
14238
14239         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14240       }
14241     }
14242     else
14243       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14244
14245     InitMovingField(x, y, move_direction);
14246     GfxAction[x][y] = ACTION_PUSHING;
14247
14248     if (mode == DF_SNAP)
14249       ContinueMoving(x, y);
14250     else
14251       MovPos[x][y] = (dx != 0 ? dx : dy);
14252
14253     Pushed[x][y] = TRUE;
14254     Pushed[nextx][nexty] = TRUE;
14255
14256     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14257       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14258     else
14259       player->push_delay_value = -1;    // get new value later
14260
14261     // check for element change _after_ element has been pushed
14262     if (game.use_change_when_pushing_bug)
14263     {
14264       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14265                                  player->index_bit, dig_side);
14266       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14267                                           player->index_bit, dig_side);
14268     }
14269   }
14270   else if (IS_SWITCHABLE(element))
14271   {
14272     if (PLAYER_SWITCHING(player, x, y))
14273     {
14274       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14275                                           player->index_bit, dig_side);
14276
14277       return MP_ACTION;
14278     }
14279
14280     player->is_switching = TRUE;
14281     player->switch_x = x;
14282     player->switch_y = y;
14283
14284     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14285
14286     if (element == EL_ROBOT_WHEEL)
14287     {
14288       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14289
14290       game.robot_wheel_x = x;
14291       game.robot_wheel_y = y;
14292       game.robot_wheel_active = TRUE;
14293
14294       TEST_DrawLevelField(x, y);
14295     }
14296     else if (element == EL_SP_TERMINAL)
14297     {
14298       int xx, yy;
14299
14300       SCAN_PLAYFIELD(xx, yy)
14301       {
14302         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14303         {
14304           Bang(xx, yy);
14305         }
14306         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14307         {
14308           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14309
14310           ResetGfxAnimation(xx, yy);
14311           TEST_DrawLevelField(xx, yy);
14312         }
14313       }
14314     }
14315     else if (IS_BELT_SWITCH(element))
14316     {
14317       ToggleBeltSwitch(x, y);
14318     }
14319     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14320              element == EL_SWITCHGATE_SWITCH_DOWN ||
14321              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14322              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14323     {
14324       ToggleSwitchgateSwitch(x, y);
14325     }
14326     else if (element == EL_LIGHT_SWITCH ||
14327              element == EL_LIGHT_SWITCH_ACTIVE)
14328     {
14329       ToggleLightSwitch(x, y);
14330     }
14331     else if (element == EL_TIMEGATE_SWITCH ||
14332              element == EL_DC_TIMEGATE_SWITCH)
14333     {
14334       ActivateTimegateSwitch(x, y);
14335     }
14336     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14337              element == EL_BALLOON_SWITCH_RIGHT ||
14338              element == EL_BALLOON_SWITCH_UP    ||
14339              element == EL_BALLOON_SWITCH_DOWN  ||
14340              element == EL_BALLOON_SWITCH_NONE  ||
14341              element == EL_BALLOON_SWITCH_ANY)
14342     {
14343       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14344                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14345                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14346                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14347                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14348                              move_direction);
14349     }
14350     else if (element == EL_LAMP)
14351     {
14352       Feld[x][y] = EL_LAMP_ACTIVE;
14353       game.lights_still_needed--;
14354
14355       ResetGfxAnimation(x, y);
14356       TEST_DrawLevelField(x, y);
14357     }
14358     else if (element == EL_TIME_ORB_FULL)
14359     {
14360       Feld[x][y] = EL_TIME_ORB_EMPTY;
14361
14362       if (level.time > 0 || level.use_time_orb_bug)
14363       {
14364         TimeLeft += level.time_orb_time;
14365         game.no_time_limit = FALSE;
14366
14367         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14368
14369         DisplayGameControlValues();
14370       }
14371
14372       ResetGfxAnimation(x, y);
14373       TEST_DrawLevelField(x, y);
14374     }
14375     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14376              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14377     {
14378       int xx, yy;
14379
14380       game.ball_active = !game.ball_active;
14381
14382       SCAN_PLAYFIELD(xx, yy)
14383       {
14384         int e = Feld[xx][yy];
14385
14386         if (game.ball_active)
14387         {
14388           if (e == EL_EMC_MAGIC_BALL)
14389             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14390           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14391             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14392         }
14393         else
14394         {
14395           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14396             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14397           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14398             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14399         }
14400       }
14401     }
14402
14403     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14404                                         player->index_bit, dig_side);
14405
14406     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14407                                         player->index_bit, dig_side);
14408
14409     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14410                                         player->index_bit, dig_side);
14411
14412     return MP_ACTION;
14413   }
14414   else
14415   {
14416     if (!PLAYER_SWITCHING(player, x, y))
14417     {
14418       player->is_switching = TRUE;
14419       player->switch_x = x;
14420       player->switch_y = y;
14421
14422       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14423                                  player->index_bit, dig_side);
14424       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14425                                           player->index_bit, dig_side);
14426
14427       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14428                                  player->index_bit, dig_side);
14429       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14430                                           player->index_bit, dig_side);
14431     }
14432
14433     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14434                                player->index_bit, dig_side);
14435     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14436                                         player->index_bit, dig_side);
14437
14438     return MP_NO_ACTION;
14439   }
14440
14441   player->push_delay = -1;
14442
14443   if (is_player)                // function can also be called by EL_PENGUIN
14444   {
14445     if (Feld[x][y] != element)          // really digged/collected something
14446     {
14447       player->is_collecting = !player->is_digging;
14448       player->is_active = TRUE;
14449     }
14450   }
14451
14452   return MP_MOVING;
14453 }
14454
14455 static boolean DigFieldByCE(int x, int y, int digging_element)
14456 {
14457   int element = Feld[x][y];
14458
14459   if (!IS_FREE(x, y))
14460   {
14461     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14462                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14463                   ACTION_BREAKING);
14464
14465     // no element can dig solid indestructible elements
14466     if (IS_INDESTRUCTIBLE(element) &&
14467         !IS_DIGGABLE(element) &&
14468         !IS_COLLECTIBLE(element))
14469       return FALSE;
14470
14471     if (AmoebaNr[x][y] &&
14472         (element == EL_AMOEBA_FULL ||
14473          element == EL_BD_AMOEBA ||
14474          element == EL_AMOEBA_GROWING))
14475     {
14476       AmoebaCnt[AmoebaNr[x][y]]--;
14477       AmoebaCnt2[AmoebaNr[x][y]]--;
14478     }
14479
14480     if (IS_MOVING(x, y))
14481       RemoveMovingField(x, y);
14482     else
14483     {
14484       RemoveField(x, y);
14485       TEST_DrawLevelField(x, y);
14486     }
14487
14488     // if digged element was about to explode, prevent the explosion
14489     ExplodeField[x][y] = EX_TYPE_NONE;
14490
14491     PlayLevelSoundAction(x, y, action);
14492   }
14493
14494   Store[x][y] = EL_EMPTY;
14495
14496   // this makes it possible to leave the removed element again
14497   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14498     Store[x][y] = element;
14499
14500   return TRUE;
14501 }
14502
14503 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14504 {
14505   int jx = player->jx, jy = player->jy;
14506   int x = jx + dx, y = jy + dy;
14507   int snap_direction = (dx == -1 ? MV_LEFT  :
14508                         dx == +1 ? MV_RIGHT :
14509                         dy == -1 ? MV_UP    :
14510                         dy == +1 ? MV_DOWN  : MV_NONE);
14511   boolean can_continue_snapping = (level.continuous_snapping &&
14512                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14513
14514   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14515     return FALSE;
14516
14517   if (!player->active || !IN_LEV_FIELD(x, y))
14518     return FALSE;
14519
14520   if (dx && dy)
14521     return FALSE;
14522
14523   if (!dx && !dy)
14524   {
14525     if (player->MovPos == 0)
14526       player->is_pushing = FALSE;
14527
14528     player->is_snapping = FALSE;
14529
14530     if (player->MovPos == 0)
14531     {
14532       player->is_moving = FALSE;
14533       player->is_digging = FALSE;
14534       player->is_collecting = FALSE;
14535     }
14536
14537     return FALSE;
14538   }
14539
14540   // prevent snapping with already pressed snap key when not allowed
14541   if (player->is_snapping && !can_continue_snapping)
14542     return FALSE;
14543
14544   player->MovDir = snap_direction;
14545
14546   if (player->MovPos == 0)
14547   {
14548     player->is_moving = FALSE;
14549     player->is_digging = FALSE;
14550     player->is_collecting = FALSE;
14551   }
14552
14553   player->is_dropping = FALSE;
14554   player->is_dropping_pressed = FALSE;
14555   player->drop_pressed_delay = 0;
14556
14557   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14558     return FALSE;
14559
14560   player->is_snapping = TRUE;
14561   player->is_active = TRUE;
14562
14563   if (player->MovPos == 0)
14564   {
14565     player->is_moving = FALSE;
14566     player->is_digging = FALSE;
14567     player->is_collecting = FALSE;
14568   }
14569
14570   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14571     TEST_DrawLevelField(player->last_jx, player->last_jy);
14572
14573   TEST_DrawLevelField(x, y);
14574
14575   return TRUE;
14576 }
14577
14578 static boolean DropElement(struct PlayerInfo *player)
14579 {
14580   int old_element, new_element;
14581   int dropx = player->jx, dropy = player->jy;
14582   int drop_direction = player->MovDir;
14583   int drop_side = drop_direction;
14584   int drop_element = get_next_dropped_element(player);
14585
14586   /* do not drop an element on top of another element; when holding drop key
14587      pressed without moving, dropped element must move away before the next
14588      element can be dropped (this is especially important if the next element
14589      is dynamite, which can be placed on background for historical reasons) */
14590   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14591     return MP_ACTION;
14592
14593   if (IS_THROWABLE(drop_element))
14594   {
14595     dropx += GET_DX_FROM_DIR(drop_direction);
14596     dropy += GET_DY_FROM_DIR(drop_direction);
14597
14598     if (!IN_LEV_FIELD(dropx, dropy))
14599       return FALSE;
14600   }
14601
14602   old_element = Feld[dropx][dropy];     // old element at dropping position
14603   new_element = drop_element;           // default: no change when dropping
14604
14605   // check if player is active, not moving and ready to drop
14606   if (!player->active || player->MovPos || player->drop_delay > 0)
14607     return FALSE;
14608
14609   // check if player has anything that can be dropped
14610   if (new_element == EL_UNDEFINED)
14611     return FALSE;
14612
14613   // only set if player has anything that can be dropped
14614   player->is_dropping_pressed = TRUE;
14615
14616   // check if drop key was pressed long enough for EM style dynamite
14617   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14618     return FALSE;
14619
14620   // check if anything can be dropped at the current position
14621   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14622     return FALSE;
14623
14624   // collected custom elements can only be dropped on empty fields
14625   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14626     return FALSE;
14627
14628   if (old_element != EL_EMPTY)
14629     Back[dropx][dropy] = old_element;   // store old element on this field
14630
14631   ResetGfxAnimation(dropx, dropy);
14632   ResetRandomAnimationValue(dropx, dropy);
14633
14634   if (player->inventory_size > 0 ||
14635       player->inventory_infinite_element != EL_UNDEFINED)
14636   {
14637     if (player->inventory_size > 0)
14638     {
14639       player->inventory_size--;
14640
14641       DrawGameDoorValues();
14642
14643       if (new_element == EL_DYNAMITE)
14644         new_element = EL_DYNAMITE_ACTIVE;
14645       else if (new_element == EL_EM_DYNAMITE)
14646         new_element = EL_EM_DYNAMITE_ACTIVE;
14647       else if (new_element == EL_SP_DISK_RED)
14648         new_element = EL_SP_DISK_RED_ACTIVE;
14649     }
14650
14651     Feld[dropx][dropy] = new_element;
14652
14653     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14654       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14655                           el2img(Feld[dropx][dropy]), 0);
14656
14657     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14658
14659     // needed if previous element just changed to "empty" in the last frame
14660     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14661
14662     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14663                                player->index_bit, drop_side);
14664     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14665                                         CE_PLAYER_DROPS_X,
14666                                         player->index_bit, drop_side);
14667
14668     TestIfElementTouchesCustomElement(dropx, dropy);
14669   }
14670   else          // player is dropping a dyna bomb
14671   {
14672     player->dynabombs_left--;
14673
14674     Feld[dropx][dropy] = new_element;
14675
14676     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14677       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14678                           el2img(Feld[dropx][dropy]), 0);
14679
14680     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14681   }
14682
14683   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14684     InitField_WithBug1(dropx, dropy, FALSE);
14685
14686   new_element = Feld[dropx][dropy];     // element might have changed
14687
14688   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14689       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14690   {
14691     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14692       MovDir[dropx][dropy] = drop_direction;
14693
14694     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14695
14696     // do not cause impact style collision by dropping elements that can fall
14697     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14698   }
14699
14700   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14701   player->is_dropping = TRUE;
14702
14703   player->drop_pressed_delay = 0;
14704   player->is_dropping_pressed = FALSE;
14705
14706   player->drop_x = dropx;
14707   player->drop_y = dropy;
14708
14709   return TRUE;
14710 }
14711
14712 // ----------------------------------------------------------------------------
14713 // game sound playing functions
14714 // ----------------------------------------------------------------------------
14715
14716 static int *loop_sound_frame = NULL;
14717 static int *loop_sound_volume = NULL;
14718
14719 void InitPlayLevelSound(void)
14720 {
14721   int num_sounds = getSoundListSize();
14722
14723   checked_free(loop_sound_frame);
14724   checked_free(loop_sound_volume);
14725
14726   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14727   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14728 }
14729
14730 static void PlayLevelSound(int x, int y, int nr)
14731 {
14732   int sx = SCREENX(x), sy = SCREENY(y);
14733   int volume, stereo_position;
14734   int max_distance = 8;
14735   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14736
14737   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14738       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14739     return;
14740
14741   if (!IN_LEV_FIELD(x, y) ||
14742       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14743       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14744     return;
14745
14746   volume = SOUND_MAX_VOLUME;
14747
14748   if (!IN_SCR_FIELD(sx, sy))
14749   {
14750     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14751     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14752
14753     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14754   }
14755
14756   stereo_position = (SOUND_MAX_LEFT +
14757                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14758                      (SCR_FIELDX + 2 * max_distance));
14759
14760   if (IS_LOOP_SOUND(nr))
14761   {
14762     /* This assures that quieter loop sounds do not overwrite louder ones,
14763        while restarting sound volume comparison with each new game frame. */
14764
14765     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14766       return;
14767
14768     loop_sound_volume[nr] = volume;
14769     loop_sound_frame[nr] = FrameCounter;
14770   }
14771
14772   PlaySoundExt(nr, volume, stereo_position, type);
14773 }
14774
14775 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14776 {
14777   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14778                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14779                  y < LEVELY(BY1) ? LEVELY(BY1) :
14780                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14781                  sound_action);
14782 }
14783
14784 static void PlayLevelSoundAction(int x, int y, int action)
14785 {
14786   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14787 }
14788
14789 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14790 {
14791   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14792
14793   if (sound_effect != SND_UNDEFINED)
14794     PlayLevelSound(x, y, sound_effect);
14795 }
14796
14797 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14798                                               int action)
14799 {
14800   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14801
14802   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14803     PlayLevelSound(x, y, sound_effect);
14804 }
14805
14806 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14807 {
14808   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14809
14810   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14811     PlayLevelSound(x, y, sound_effect);
14812 }
14813
14814 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14815 {
14816   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14817
14818   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14819     StopSound(sound_effect);
14820 }
14821
14822 static int getLevelMusicNr(void)
14823 {
14824   if (levelset.music[level_nr] != MUS_UNDEFINED)
14825     return levelset.music[level_nr];            // from config file
14826   else
14827     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14828 }
14829
14830 static void FadeLevelSounds(void)
14831 {
14832   FadeSounds();
14833 }
14834
14835 static void FadeLevelMusic(void)
14836 {
14837   int music_nr = getLevelMusicNr();
14838   char *curr_music = getCurrentlyPlayingMusicFilename();
14839   char *next_music = getMusicInfoEntryFilename(music_nr);
14840
14841   if (!strEqual(curr_music, next_music))
14842     FadeMusic();
14843 }
14844
14845 void FadeLevelSoundsAndMusic(void)
14846 {
14847   FadeLevelSounds();
14848   FadeLevelMusic();
14849 }
14850
14851 static void PlayLevelMusic(void)
14852 {
14853   int music_nr = getLevelMusicNr();
14854   char *curr_music = getCurrentlyPlayingMusicFilename();
14855   char *next_music = getMusicInfoEntryFilename(music_nr);
14856
14857   if (!strEqual(curr_music, next_music))
14858     PlayMusicLoop(music_nr);
14859 }
14860
14861 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14862 {
14863   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14864   int offset = 0;
14865   int x = xx - offset;
14866   int y = yy - offset;
14867
14868   x = correctLevelPosX_EM(x);
14869   y = correctLevelPosY_EM(y);
14870
14871   switch (sample)
14872   {
14873     case SOUND_blank:
14874       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14875       break;
14876
14877     case SOUND_roll:
14878       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14879       break;
14880
14881     case SOUND_stone:
14882       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14883       break;
14884
14885     case SOUND_nut:
14886       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14887       break;
14888
14889     case SOUND_crack:
14890       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14891       break;
14892
14893     case SOUND_bug:
14894       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14895       break;
14896
14897     case SOUND_tank:
14898       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14899       break;
14900
14901     case SOUND_android_clone:
14902       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14903       break;
14904
14905     case SOUND_android_move:
14906       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14907       break;
14908
14909     case SOUND_spring:
14910       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14911       break;
14912
14913     case SOUND_slurp:
14914       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14915       break;
14916
14917     case SOUND_eater:
14918       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14919       break;
14920
14921     case SOUND_eater_eat:
14922       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14923       break;
14924
14925     case SOUND_alien:
14926       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14927       break;
14928
14929     case SOUND_collect:
14930       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14931       break;
14932
14933     case SOUND_diamond:
14934       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14935       break;
14936
14937     case SOUND_squash:
14938       // !!! CHECK THIS !!!
14939 #if 1
14940       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14941 #else
14942       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14943 #endif
14944       break;
14945
14946     case SOUND_wonderfall:
14947       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14948       break;
14949
14950     case SOUND_drip:
14951       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14952       break;
14953
14954     case SOUND_push:
14955       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14956       break;
14957
14958     case SOUND_dirt:
14959       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14960       break;
14961
14962     case SOUND_acid:
14963       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14964       break;
14965
14966     case SOUND_ball:
14967       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14968       break;
14969
14970     case SOUND_slide:
14971       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14972       break;
14973
14974     case SOUND_wonder:
14975       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14976       break;
14977
14978     case SOUND_door:
14979       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14980       break;
14981
14982     case SOUND_exit_open:
14983       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14984       break;
14985
14986     case SOUND_exit_leave:
14987       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14988       break;
14989
14990     case SOUND_dynamite:
14991       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14992       break;
14993
14994     case SOUND_tick:
14995       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14996       break;
14997
14998     case SOUND_press:
14999       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15000       break;
15001
15002     case SOUND_wheel:
15003       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15004       break;
15005
15006     case SOUND_boom:
15007       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15008       break;
15009
15010     case SOUND_die:
15011       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15012       break;
15013
15014     case SOUND_time:
15015       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15016       break;
15017
15018     default:
15019       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15020       break;
15021   }
15022 }
15023
15024 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15025 {
15026   int element = map_element_SP_to_RND(element_sp);
15027   int action = map_action_SP_to_RND(action_sp);
15028   int offset = (setup.sp_show_border_elements ? 0 : 1);
15029   int x = xx - offset;
15030   int y = yy - offset;
15031
15032   PlayLevelSoundElementAction(x, y, element, action);
15033 }
15034
15035 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15036 {
15037   int element = map_element_MM_to_RND(element_mm);
15038   int action = map_action_MM_to_RND(action_mm);
15039   int offset = 0;
15040   int x = xx - offset;
15041   int y = yy - offset;
15042
15043   if (!IS_MM_ELEMENT(element))
15044     element = EL_MM_DEFAULT;
15045
15046   PlayLevelSoundElementAction(x, y, element, action);
15047 }
15048
15049 void PlaySound_MM(int sound_mm)
15050 {
15051   int sound = map_sound_MM_to_RND(sound_mm);
15052
15053   if (sound == SND_UNDEFINED)
15054     return;
15055
15056   PlaySound(sound);
15057 }
15058
15059 void PlaySoundLoop_MM(int sound_mm)
15060 {
15061   int sound = map_sound_MM_to_RND(sound_mm);
15062
15063   if (sound == SND_UNDEFINED)
15064     return;
15065
15066   PlaySoundLoop(sound);
15067 }
15068
15069 void StopSound_MM(int sound_mm)
15070 {
15071   int sound = map_sound_MM_to_RND(sound_mm);
15072
15073   if (sound == SND_UNDEFINED)
15074     return;
15075
15076   StopSound(sound);
15077 }
15078
15079 void RaiseScore(int value)
15080 {
15081   game.score += value;
15082
15083   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15084
15085   DisplayGameControlValues();
15086 }
15087
15088 void RaiseScoreElement(int element)
15089 {
15090   switch (element)
15091   {
15092     case EL_EMERALD:
15093     case EL_BD_DIAMOND:
15094     case EL_EMERALD_YELLOW:
15095     case EL_EMERALD_RED:
15096     case EL_EMERALD_PURPLE:
15097     case EL_SP_INFOTRON:
15098       RaiseScore(level.score[SC_EMERALD]);
15099       break;
15100     case EL_DIAMOND:
15101       RaiseScore(level.score[SC_DIAMOND]);
15102       break;
15103     case EL_CRYSTAL:
15104       RaiseScore(level.score[SC_CRYSTAL]);
15105       break;
15106     case EL_PEARL:
15107       RaiseScore(level.score[SC_PEARL]);
15108       break;
15109     case EL_BUG:
15110     case EL_BD_BUTTERFLY:
15111     case EL_SP_ELECTRON:
15112       RaiseScore(level.score[SC_BUG]);
15113       break;
15114     case EL_SPACESHIP:
15115     case EL_BD_FIREFLY:
15116     case EL_SP_SNIKSNAK:
15117       RaiseScore(level.score[SC_SPACESHIP]);
15118       break;
15119     case EL_YAMYAM:
15120     case EL_DARK_YAMYAM:
15121       RaiseScore(level.score[SC_YAMYAM]);
15122       break;
15123     case EL_ROBOT:
15124       RaiseScore(level.score[SC_ROBOT]);
15125       break;
15126     case EL_PACMAN:
15127       RaiseScore(level.score[SC_PACMAN]);
15128       break;
15129     case EL_NUT:
15130       RaiseScore(level.score[SC_NUT]);
15131       break;
15132     case EL_DYNAMITE:
15133     case EL_EM_DYNAMITE:
15134     case EL_SP_DISK_RED:
15135     case EL_DYNABOMB_INCREASE_NUMBER:
15136     case EL_DYNABOMB_INCREASE_SIZE:
15137     case EL_DYNABOMB_INCREASE_POWER:
15138       RaiseScore(level.score[SC_DYNAMITE]);
15139       break;
15140     case EL_SHIELD_NORMAL:
15141     case EL_SHIELD_DEADLY:
15142       RaiseScore(level.score[SC_SHIELD]);
15143       break;
15144     case EL_EXTRA_TIME:
15145       RaiseScore(level.extra_time_score);
15146       break;
15147     case EL_KEY_1:
15148     case EL_KEY_2:
15149     case EL_KEY_3:
15150     case EL_KEY_4:
15151     case EL_EM_KEY_1:
15152     case EL_EM_KEY_2:
15153     case EL_EM_KEY_3:
15154     case EL_EM_KEY_4:
15155     case EL_EMC_KEY_5:
15156     case EL_EMC_KEY_6:
15157     case EL_EMC_KEY_7:
15158     case EL_EMC_KEY_8:
15159     case EL_DC_KEY_WHITE:
15160       RaiseScore(level.score[SC_KEY]);
15161       break;
15162     default:
15163       RaiseScore(element_info[element].collect_score);
15164       break;
15165   }
15166 }
15167
15168 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15169 {
15170   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15171   {
15172     // closing door required in case of envelope style request dialogs
15173     if (!skip_request)
15174     {
15175       // prevent short reactivation of overlay buttons while closing door
15176       SetOverlayActive(FALSE);
15177
15178       CloseDoor(DOOR_CLOSE_1);
15179     }
15180
15181     if (network.enabled)
15182       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15183     else
15184     {
15185       if (quick_quit)
15186         FadeSkipNextFadeIn();
15187
15188       SetGameStatus(GAME_MODE_MAIN);
15189
15190       DrawMainMenu();
15191     }
15192   }
15193   else          // continue playing the game
15194   {
15195     if (tape.playing && tape.deactivate_display)
15196       TapeDeactivateDisplayOff(TRUE);
15197
15198     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15199
15200     if (tape.playing && tape.deactivate_display)
15201       TapeDeactivateDisplayOn();
15202   }
15203 }
15204
15205 void RequestQuitGame(boolean ask_if_really_quit)
15206 {
15207   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15208   boolean skip_request = game.all_players_gone || quick_quit;
15209
15210   RequestQuitGameExt(skip_request, quick_quit,
15211                      "Do you really want to quit the game?");
15212 }
15213
15214 void RequestRestartGame(char *message)
15215 {
15216   game.restart_game_message = NULL;
15217
15218   boolean has_started_game = hasStartedNetworkGame();
15219   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15220
15221   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15222   {
15223     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15224   }
15225   else
15226   {
15227     SetGameStatus(GAME_MODE_MAIN);
15228
15229     DrawMainMenu();
15230   }
15231 }
15232
15233 void CheckGameOver(void)
15234 {
15235   static boolean last_game_over = FALSE;
15236   static int game_over_delay = 0;
15237   int game_over_delay_value = 50;
15238   boolean game_over = checkGameFailed();
15239
15240   // do not handle game over if request dialog is already active
15241   if (game.request_active)
15242     return;
15243
15244   // do not ask to play again if game was never actually played
15245   if (!game.GamePlayed)
15246     return;
15247
15248   if (!game_over)
15249   {
15250     last_game_over = FALSE;
15251     game_over_delay = game_over_delay_value;
15252
15253     return;
15254   }
15255
15256   if (game_over_delay > 0)
15257   {
15258     game_over_delay--;
15259
15260     return;
15261   }
15262
15263   if (last_game_over != game_over)
15264     game.restart_game_message = (hasStartedNetworkGame() ?
15265                                  "Game over! Play it again?" :
15266                                  "Game over!");
15267
15268   last_game_over = game_over;
15269 }
15270
15271 boolean checkGameSolved(void)
15272 {
15273   // set for all game engines if level was solved
15274   return game.LevelSolved_GameEnd;
15275 }
15276
15277 boolean checkGameFailed(void)
15278 {
15279   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15280     return (game_em.game_over && !game_em.level_solved);
15281   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15282     return (game_sp.game_over && !game_sp.level_solved);
15283   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15284     return (game_mm.game_over && !game_mm.level_solved);
15285   else                          // GAME_ENGINE_TYPE_RND
15286     return (game.GameOver && !game.LevelSolved);
15287 }
15288
15289 boolean checkGameEnded(void)
15290 {
15291   return (checkGameSolved() || checkGameFailed());
15292 }
15293
15294
15295 // ----------------------------------------------------------------------------
15296 // random generator functions
15297 // ----------------------------------------------------------------------------
15298
15299 unsigned int InitEngineRandom_RND(int seed)
15300 {
15301   game.num_random_calls = 0;
15302
15303   return InitEngineRandom(seed);
15304 }
15305
15306 unsigned int RND(int max)
15307 {
15308   if (max > 0)
15309   {
15310     game.num_random_calls++;
15311
15312     return GetEngineRandom(max);
15313   }
15314
15315   return 0;
15316 }
15317
15318
15319 // ----------------------------------------------------------------------------
15320 // game engine snapshot handling functions
15321 // ----------------------------------------------------------------------------
15322
15323 struct EngineSnapshotInfo
15324 {
15325   // runtime values for custom element collect score
15326   int collect_score[NUM_CUSTOM_ELEMENTS];
15327
15328   // runtime values for group element choice position
15329   int choice_pos[NUM_GROUP_ELEMENTS];
15330
15331   // runtime values for belt position animations
15332   int belt_graphic[4][NUM_BELT_PARTS];
15333   int belt_anim_mode[4][NUM_BELT_PARTS];
15334 };
15335
15336 static struct EngineSnapshotInfo engine_snapshot_rnd;
15337 static char *snapshot_level_identifier = NULL;
15338 static int snapshot_level_nr = -1;
15339
15340 static void SaveEngineSnapshotValues_RND(void)
15341 {
15342   static int belt_base_active_element[4] =
15343   {
15344     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15345     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15346     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15347     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15348   };
15349   int i, j;
15350
15351   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15352   {
15353     int element = EL_CUSTOM_START + i;
15354
15355     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15356   }
15357
15358   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15359   {
15360     int element = EL_GROUP_START + i;
15361
15362     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15363   }
15364
15365   for (i = 0; i < 4; i++)
15366   {
15367     for (j = 0; j < NUM_BELT_PARTS; j++)
15368     {
15369       int element = belt_base_active_element[i] + j;
15370       int graphic = el2img(element);
15371       int anim_mode = graphic_info[graphic].anim_mode;
15372
15373       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15374       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15375     }
15376   }
15377 }
15378
15379 static void LoadEngineSnapshotValues_RND(void)
15380 {
15381   unsigned int num_random_calls = game.num_random_calls;
15382   int i, j;
15383
15384   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15385   {
15386     int element = EL_CUSTOM_START + i;
15387
15388     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15389   }
15390
15391   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15392   {
15393     int element = EL_GROUP_START + i;
15394
15395     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15396   }
15397
15398   for (i = 0; i < 4; i++)
15399   {
15400     for (j = 0; j < NUM_BELT_PARTS; j++)
15401     {
15402       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15403       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15404
15405       graphic_info[graphic].anim_mode = anim_mode;
15406     }
15407   }
15408
15409   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15410   {
15411     InitRND(tape.random_seed);
15412     for (i = 0; i < num_random_calls; i++)
15413       RND(1);
15414   }
15415
15416   if (game.num_random_calls != num_random_calls)
15417   {
15418     Error(ERR_INFO, "number of random calls out of sync");
15419     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15420     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15421     Error(ERR_EXIT, "this should not happen -- please debug");
15422   }
15423 }
15424
15425 void FreeEngineSnapshotSingle(void)
15426 {
15427   FreeSnapshotSingle();
15428
15429   setString(&snapshot_level_identifier, NULL);
15430   snapshot_level_nr = -1;
15431 }
15432
15433 void FreeEngineSnapshotList(void)
15434 {
15435   FreeSnapshotList();
15436 }
15437
15438 static ListNode *SaveEngineSnapshotBuffers(void)
15439 {
15440   ListNode *buffers = NULL;
15441
15442   // copy some special values to a structure better suited for the snapshot
15443
15444   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15445     SaveEngineSnapshotValues_RND();
15446   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15447     SaveEngineSnapshotValues_EM();
15448   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15449     SaveEngineSnapshotValues_SP(&buffers);
15450   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15451     SaveEngineSnapshotValues_MM(&buffers);
15452
15453   // save values stored in special snapshot structure
15454
15455   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15456     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15457   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15458     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15459   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15460     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15461   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15462     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15463
15464   // save further RND engine values
15465
15466   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15467   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15468   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15469
15470   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15471   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15472   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15473   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15474   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15475
15476   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15477   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15478   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15479
15480   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15481
15482   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15483   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15484
15485   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15486   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15487   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15488   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15489   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15490   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15491   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15492   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15493   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15494   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15495   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15496   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15497   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15498   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15499   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15500   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15501   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15502   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15503
15504   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15505   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15506
15507   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15508   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15509   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15510
15511   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15512   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15513
15514   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15515   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15516   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15517   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15518   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15519
15520   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15521   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15522
15523 #if 0
15524   ListNode *node = engine_snapshot_list_rnd;
15525   int num_bytes = 0;
15526
15527   while (node != NULL)
15528   {
15529     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15530
15531     node = node->next;
15532   }
15533
15534   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15535 #endif
15536
15537   return buffers;
15538 }
15539
15540 void SaveEngineSnapshotSingle(void)
15541 {
15542   ListNode *buffers = SaveEngineSnapshotBuffers();
15543
15544   // finally save all snapshot buffers to single snapshot
15545   SaveSnapshotSingle(buffers);
15546
15547   // save level identification information
15548   setString(&snapshot_level_identifier, leveldir_current->identifier);
15549   snapshot_level_nr = level_nr;
15550 }
15551
15552 boolean CheckSaveEngineSnapshotToList(void)
15553 {
15554   boolean save_snapshot =
15555     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15556      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15557       game.snapshot.changed_action) ||
15558      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15559       game.snapshot.collected_item));
15560
15561   game.snapshot.changed_action = FALSE;
15562   game.snapshot.collected_item = FALSE;
15563   game.snapshot.save_snapshot = save_snapshot;
15564
15565   return save_snapshot;
15566 }
15567
15568 void SaveEngineSnapshotToList(void)
15569 {
15570   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15571       tape.quick_resume)
15572     return;
15573
15574   ListNode *buffers = SaveEngineSnapshotBuffers();
15575
15576   // finally save all snapshot buffers to snapshot list
15577   SaveSnapshotToList(buffers);
15578 }
15579
15580 void SaveEngineSnapshotToListInitial(void)
15581 {
15582   FreeEngineSnapshotList();
15583
15584   SaveEngineSnapshotToList();
15585 }
15586
15587 static void LoadEngineSnapshotValues(void)
15588 {
15589   // restore special values from snapshot structure
15590
15591   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15592     LoadEngineSnapshotValues_RND();
15593   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15594     LoadEngineSnapshotValues_EM();
15595   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15596     LoadEngineSnapshotValues_SP();
15597   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15598     LoadEngineSnapshotValues_MM();
15599 }
15600
15601 void LoadEngineSnapshotSingle(void)
15602 {
15603   LoadSnapshotSingle();
15604
15605   LoadEngineSnapshotValues();
15606 }
15607
15608 static void LoadEngineSnapshot_Undo(int steps)
15609 {
15610   LoadSnapshotFromList_Older(steps);
15611
15612   LoadEngineSnapshotValues();
15613 }
15614
15615 static void LoadEngineSnapshot_Redo(int steps)
15616 {
15617   LoadSnapshotFromList_Newer(steps);
15618
15619   LoadEngineSnapshotValues();
15620 }
15621
15622 boolean CheckEngineSnapshotSingle(void)
15623 {
15624   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15625           snapshot_level_nr == level_nr);
15626 }
15627
15628 boolean CheckEngineSnapshotList(void)
15629 {
15630   return CheckSnapshotList();
15631 }
15632
15633
15634 // ---------- new game button stuff -------------------------------------------
15635
15636 static struct
15637 {
15638   int graphic;
15639   struct XY *pos;
15640   int gadget_id;
15641   boolean *setup_value;
15642   boolean allowed_on_tape;
15643   boolean is_touch_button;
15644   char *infotext;
15645 } gamebutton_info[NUM_GAME_BUTTONS] =
15646 {
15647   {
15648     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15649     GAME_CTRL_ID_STOP,                          NULL,
15650     TRUE, FALSE,                                "stop game"
15651   },
15652   {
15653     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15654     GAME_CTRL_ID_PAUSE,                         NULL,
15655     TRUE, FALSE,                                "pause game"
15656   },
15657   {
15658     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15659     GAME_CTRL_ID_PLAY,                          NULL,
15660     TRUE, FALSE,                                "play game"
15661   },
15662   {
15663     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15664     GAME_CTRL_ID_UNDO,                          NULL,
15665     TRUE, FALSE,                                "undo step"
15666   },
15667   {
15668     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15669     GAME_CTRL_ID_REDO,                          NULL,
15670     TRUE, FALSE,                                "redo step"
15671   },
15672   {
15673     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15674     GAME_CTRL_ID_SAVE,                          NULL,
15675     TRUE, FALSE,                                "save game"
15676   },
15677   {
15678     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15679     GAME_CTRL_ID_PAUSE2,                        NULL,
15680     TRUE, FALSE,                                "pause game"
15681   },
15682   {
15683     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15684     GAME_CTRL_ID_LOAD,                          NULL,
15685     TRUE, FALSE,                                "load game"
15686   },
15687   {
15688     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15689     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15690     FALSE, FALSE,                               "stop game"
15691   },
15692   {
15693     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15694     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15695     FALSE, FALSE,                               "pause game"
15696   },
15697   {
15698     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15699     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15700     FALSE, FALSE,                               "play game"
15701   },
15702   {
15703     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15704     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15705     FALSE, TRUE,                                "stop game"
15706   },
15707   {
15708     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15709     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15710     FALSE, TRUE,                                "pause game"
15711   },
15712   {
15713     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15714     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15715     TRUE, FALSE,                                "background music on/off"
15716   },
15717   {
15718     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15719     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15720     TRUE, FALSE,                                "sound loops on/off"
15721   },
15722   {
15723     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15724     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15725     TRUE, FALSE,                                "normal sounds on/off"
15726   },
15727   {
15728     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15729     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15730     FALSE, FALSE,                               "background music on/off"
15731   },
15732   {
15733     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15734     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15735     FALSE, FALSE,                               "sound loops on/off"
15736   },
15737   {
15738     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15739     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15740     FALSE, FALSE,                               "normal sounds on/off"
15741   }
15742 };
15743
15744 void CreateGameButtons(void)
15745 {
15746   int i;
15747
15748   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15749   {
15750     int graphic = gamebutton_info[i].graphic;
15751     struct GraphicInfo *gfx = &graphic_info[graphic];
15752     struct XY *pos = gamebutton_info[i].pos;
15753     struct GadgetInfo *gi;
15754     int button_type;
15755     boolean checked;
15756     unsigned int event_mask;
15757     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15758     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15759     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15760     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15761     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15762     int gd_x   = gfx->src_x;
15763     int gd_y   = gfx->src_y;
15764     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15765     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15766     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15767     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15768     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15769     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15770     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15771     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15772     int id = i;
15773
15774     if (gfx->bitmap == NULL)
15775     {
15776       game_gadget[id] = NULL;
15777
15778       continue;
15779     }
15780
15781     if (id == GAME_CTRL_ID_STOP ||
15782         id == GAME_CTRL_ID_PANEL_STOP ||
15783         id == GAME_CTRL_ID_TOUCH_STOP ||
15784         id == GAME_CTRL_ID_PLAY ||
15785         id == GAME_CTRL_ID_PANEL_PLAY ||
15786         id == GAME_CTRL_ID_SAVE ||
15787         id == GAME_CTRL_ID_LOAD)
15788     {
15789       button_type = GD_TYPE_NORMAL_BUTTON;
15790       checked = FALSE;
15791       event_mask = GD_EVENT_RELEASED;
15792     }
15793     else if (id == GAME_CTRL_ID_UNDO ||
15794              id == GAME_CTRL_ID_REDO)
15795     {
15796       button_type = GD_TYPE_NORMAL_BUTTON;
15797       checked = FALSE;
15798       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15799     }
15800     else
15801     {
15802       button_type = GD_TYPE_CHECK_BUTTON;
15803       checked = (gamebutton_info[i].setup_value != NULL ?
15804                  *gamebutton_info[i].setup_value : FALSE);
15805       event_mask = GD_EVENT_PRESSED;
15806     }
15807
15808     gi = CreateGadget(GDI_CUSTOM_ID, id,
15809                       GDI_IMAGE_ID, graphic,
15810                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15811                       GDI_X, base_x + x,
15812                       GDI_Y, base_y + y,
15813                       GDI_WIDTH, gfx->width,
15814                       GDI_HEIGHT, gfx->height,
15815                       GDI_TYPE, button_type,
15816                       GDI_STATE, GD_BUTTON_UNPRESSED,
15817                       GDI_CHECKED, checked,
15818                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15819                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15820                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15821                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15822                       GDI_DIRECT_DRAW, FALSE,
15823                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15824                       GDI_EVENT_MASK, event_mask,
15825                       GDI_CALLBACK_ACTION, HandleGameButtons,
15826                       GDI_END);
15827
15828     if (gi == NULL)
15829       Error(ERR_EXIT, "cannot create gadget");
15830
15831     game_gadget[id] = gi;
15832   }
15833 }
15834
15835 void FreeGameButtons(void)
15836 {
15837   int i;
15838
15839   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15840     FreeGadget(game_gadget[i]);
15841 }
15842
15843 static void UnmapGameButtonsAtSamePosition(int id)
15844 {
15845   int i;
15846
15847   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15848     if (i != id &&
15849         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15850         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15851       UnmapGadget(game_gadget[i]);
15852 }
15853
15854 static void UnmapGameButtonsAtSamePosition_All(void)
15855 {
15856   if (setup.show_snapshot_buttons)
15857   {
15858     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15859     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15860     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15861   }
15862   else
15863   {
15864     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15865     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15866     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15867
15868     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15869     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15870     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15871   }
15872 }
15873
15874 static void MapGameButtonsAtSamePosition(int id)
15875 {
15876   int i;
15877
15878   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15879     if (i != id &&
15880         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15881         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15882       MapGadget(game_gadget[i]);
15883
15884   UnmapGameButtonsAtSamePosition_All();
15885 }
15886
15887 void MapUndoRedoButtons(void)
15888 {
15889   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15890   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15891
15892   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15893   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15894 }
15895
15896 void UnmapUndoRedoButtons(void)
15897 {
15898   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15899   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15900
15901   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15902   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15903 }
15904
15905 void ModifyPauseButtons(void)
15906 {
15907   static int ids[] =
15908   {
15909     GAME_CTRL_ID_PAUSE,
15910     GAME_CTRL_ID_PAUSE2,
15911     GAME_CTRL_ID_PANEL_PAUSE,
15912     GAME_CTRL_ID_TOUCH_PAUSE,
15913     -1
15914   };
15915   int i;
15916
15917   for (i = 0; ids[i] > -1; i++)
15918     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15919 }
15920
15921 static void MapGameButtonsExt(boolean on_tape)
15922 {
15923   int i;
15924
15925   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15926     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15927         i != GAME_CTRL_ID_UNDO &&
15928         i != GAME_CTRL_ID_REDO)
15929       MapGadget(game_gadget[i]);
15930
15931   UnmapGameButtonsAtSamePosition_All();
15932
15933   RedrawGameButtons();
15934 }
15935
15936 static void UnmapGameButtonsExt(boolean on_tape)
15937 {
15938   int i;
15939
15940   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15941     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15942       UnmapGadget(game_gadget[i]);
15943 }
15944
15945 static void RedrawGameButtonsExt(boolean on_tape)
15946 {
15947   int i;
15948
15949   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15950     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15951       RedrawGadget(game_gadget[i]);
15952 }
15953
15954 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15955 {
15956   if (gi == NULL)
15957     return;
15958
15959   gi->checked = state;
15960 }
15961
15962 static void RedrawSoundButtonGadget(int id)
15963 {
15964   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15965              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15966              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15967              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15968              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15969              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15970              id);
15971
15972   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15973   RedrawGadget(game_gadget[id2]);
15974 }
15975
15976 void MapGameButtons(void)
15977 {
15978   MapGameButtonsExt(FALSE);
15979 }
15980
15981 void UnmapGameButtons(void)
15982 {
15983   UnmapGameButtonsExt(FALSE);
15984 }
15985
15986 void RedrawGameButtons(void)
15987 {
15988   RedrawGameButtonsExt(FALSE);
15989 }
15990
15991 void MapGameButtonsOnTape(void)
15992 {
15993   MapGameButtonsExt(TRUE);
15994 }
15995
15996 void UnmapGameButtonsOnTape(void)
15997 {
15998   UnmapGameButtonsExt(TRUE);
15999 }
16000
16001 void RedrawGameButtonsOnTape(void)
16002 {
16003   RedrawGameButtonsExt(TRUE);
16004 }
16005
16006 static void GameUndoRedoExt(void)
16007 {
16008   ClearPlayerAction();
16009
16010   tape.pausing = TRUE;
16011
16012   RedrawPlayfield();
16013   UpdateAndDisplayGameControlValues();
16014
16015   DrawCompleteVideoDisplay();
16016   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16017   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16018   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16019
16020   BackToFront();
16021 }
16022
16023 static void GameUndo(int steps)
16024 {
16025   if (!CheckEngineSnapshotList())
16026     return;
16027
16028   LoadEngineSnapshot_Undo(steps);
16029
16030   GameUndoRedoExt();
16031 }
16032
16033 static void GameRedo(int steps)
16034 {
16035   if (!CheckEngineSnapshotList())
16036     return;
16037
16038   LoadEngineSnapshot_Redo(steps);
16039
16040   GameUndoRedoExt();
16041 }
16042
16043 static void HandleGameButtonsExt(int id, int button)
16044 {
16045   static boolean game_undo_executed = FALSE;
16046   int steps = BUTTON_STEPSIZE(button);
16047   boolean handle_game_buttons =
16048     (game_status == GAME_MODE_PLAYING ||
16049      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16050
16051   if (!handle_game_buttons)
16052     return;
16053
16054   switch (id)
16055   {
16056     case GAME_CTRL_ID_STOP:
16057     case GAME_CTRL_ID_PANEL_STOP:
16058     case GAME_CTRL_ID_TOUCH_STOP:
16059       if (game_status == GAME_MODE_MAIN)
16060         break;
16061
16062       if (tape.playing)
16063         TapeStop();
16064       else
16065         RequestQuitGame(TRUE);
16066
16067       break;
16068
16069     case GAME_CTRL_ID_PAUSE:
16070     case GAME_CTRL_ID_PAUSE2:
16071     case GAME_CTRL_ID_PANEL_PAUSE:
16072     case GAME_CTRL_ID_TOUCH_PAUSE:
16073       if (network.enabled && game_status == GAME_MODE_PLAYING)
16074       {
16075         if (tape.pausing)
16076           SendToServer_ContinuePlaying();
16077         else
16078           SendToServer_PausePlaying();
16079       }
16080       else
16081         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16082
16083       game_undo_executed = FALSE;
16084
16085       break;
16086
16087     case GAME_CTRL_ID_PLAY:
16088     case GAME_CTRL_ID_PANEL_PLAY:
16089       if (game_status == GAME_MODE_MAIN)
16090       {
16091         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16092       }
16093       else if (tape.pausing)
16094       {
16095         if (network.enabled)
16096           SendToServer_ContinuePlaying();
16097         else
16098           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16099       }
16100       break;
16101
16102     case GAME_CTRL_ID_UNDO:
16103       // Important: When using "save snapshot when collecting an item" mode,
16104       // load last (current) snapshot for first "undo" after pressing "pause"
16105       // (else the last-but-one snapshot would be loaded, because the snapshot
16106       // pointer already points to the last snapshot when pressing "pause",
16107       // which is fine for "every step/move" mode, but not for "every collect")
16108       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16109           !game_undo_executed)
16110         steps--;
16111
16112       game_undo_executed = TRUE;
16113
16114       GameUndo(steps);
16115       break;
16116
16117     case GAME_CTRL_ID_REDO:
16118       GameRedo(steps);
16119       break;
16120
16121     case GAME_CTRL_ID_SAVE:
16122       TapeQuickSave();
16123       break;
16124
16125     case GAME_CTRL_ID_LOAD:
16126       TapeQuickLoad();
16127       break;
16128
16129     case SOUND_CTRL_ID_MUSIC:
16130     case SOUND_CTRL_ID_PANEL_MUSIC:
16131       if (setup.sound_music)
16132       { 
16133         setup.sound_music = FALSE;
16134
16135         FadeMusic();
16136       }
16137       else if (audio.music_available)
16138       { 
16139         setup.sound = setup.sound_music = TRUE;
16140
16141         SetAudioMode(setup.sound);
16142
16143         if (game_status == GAME_MODE_PLAYING)
16144           PlayLevelMusic();
16145       }
16146
16147       RedrawSoundButtonGadget(id);
16148
16149       break;
16150
16151     case SOUND_CTRL_ID_LOOPS:
16152     case SOUND_CTRL_ID_PANEL_LOOPS:
16153       if (setup.sound_loops)
16154         setup.sound_loops = FALSE;
16155       else if (audio.loops_available)
16156       {
16157         setup.sound = setup.sound_loops = TRUE;
16158
16159         SetAudioMode(setup.sound);
16160       }
16161
16162       RedrawSoundButtonGadget(id);
16163
16164       break;
16165
16166     case SOUND_CTRL_ID_SIMPLE:
16167     case SOUND_CTRL_ID_PANEL_SIMPLE:
16168       if (setup.sound_simple)
16169         setup.sound_simple = FALSE;
16170       else if (audio.sound_available)
16171       {
16172         setup.sound = setup.sound_simple = TRUE;
16173
16174         SetAudioMode(setup.sound);
16175       }
16176
16177       RedrawSoundButtonGadget(id);
16178
16179       break;
16180
16181     default:
16182       break;
16183   }
16184 }
16185
16186 static void HandleGameButtons(struct GadgetInfo *gi)
16187 {
16188   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16189 }
16190
16191 void HandleSoundButtonKeys(Key key)
16192 {
16193   if (key == setup.shortcut.sound_simple)
16194     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16195   else if (key == setup.shortcut.sound_loops)
16196     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16197   else if (key == setup.shortcut.sound_music)
16198     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16199 }