fixed handling of androids in EM engine for old tapes
[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   // always check if player was just killed and should be reanimated
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     case EL_SPRING_LEFT:
1881     case EL_SPRING_RIGHT:
1882       InitMovDir(x, y);
1883       break;
1884
1885     case EL_AMOEBA_FULL:
1886     case EL_BD_AMOEBA:
1887       InitAmoebaNr(x, y);
1888       break;
1889
1890     case EL_AMOEBA_DROP:
1891       if (y == lev_fieldy - 1)
1892       {
1893         Feld[x][y] = EL_AMOEBA_GROWING;
1894         Store[x][y] = EL_AMOEBA_WET;
1895       }
1896       break;
1897
1898     case EL_DYNAMITE_ACTIVE:
1899     case EL_SP_DISK_RED_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904       MovDelay[x][y] = 96;
1905       break;
1906
1907     case EL_EM_DYNAMITE_ACTIVE:
1908       MovDelay[x][y] = 32;
1909       break;
1910
1911     case EL_LAMP:
1912       game.lights_still_needed++;
1913       break;
1914
1915     case EL_PENGUIN:
1916       game.friends_still_needed++;
1917       break;
1918
1919     case EL_PIG:
1920     case EL_DRAGON:
1921       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922       break;
1923
1924     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936       if (init_game)
1937       {
1938         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1939         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1940         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1941
1942         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1943         {
1944           game.belt_dir[belt_nr] = belt_dir;
1945           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1946         }
1947         else    // more than one switch -- set it like the first switch
1948         {
1949           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1950         }
1951       }
1952       break;
1953
1954     case EL_LIGHT_SWITCH_ACTIVE:
1955       if (init_game)
1956         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1957       break;
1958
1959     case EL_INVISIBLE_STEELWALL:
1960     case EL_INVISIBLE_WALL:
1961     case EL_INVISIBLE_SAND:
1962       if (game.light_time_left > 0 ||
1963           game.lenses_time_left > 0)
1964         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1965       break;
1966
1967     case EL_EMC_MAGIC_BALL:
1968       if (game.ball_active)
1969         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1970       break;
1971
1972     case EL_EMC_MAGIC_BALL_SWITCH:
1973       if (game.ball_active)
1974         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1975       break;
1976
1977     case EL_TRIGGER_PLAYER:
1978     case EL_TRIGGER_ELEMENT:
1979     case EL_TRIGGER_CE_VALUE:
1980     case EL_TRIGGER_CE_SCORE:
1981     case EL_SELF:
1982     case EL_ANY_ELEMENT:
1983     case EL_CURRENT_CE_VALUE:
1984     case EL_CURRENT_CE_SCORE:
1985     case EL_PREV_CE_1:
1986     case EL_PREV_CE_2:
1987     case EL_PREV_CE_3:
1988     case EL_PREV_CE_4:
1989     case EL_PREV_CE_5:
1990     case EL_PREV_CE_6:
1991     case EL_PREV_CE_7:
1992     case EL_PREV_CE_8:
1993     case EL_NEXT_CE_1:
1994     case EL_NEXT_CE_2:
1995     case EL_NEXT_CE_3:
1996     case EL_NEXT_CE_4:
1997     case EL_NEXT_CE_5:
1998     case EL_NEXT_CE_6:
1999     case EL_NEXT_CE_7:
2000     case EL_NEXT_CE_8:
2001       // reference elements should not be used on the playfield
2002       Feld[x][y] = EL_EMPTY;
2003       break;
2004
2005     default:
2006       if (IS_CUSTOM_ELEMENT(element))
2007       {
2008         if (CAN_MOVE(element))
2009           InitMovDir(x, y);
2010
2011         if (!element_info[element].use_last_ce_value || init_game)
2012           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2013       }
2014       else if (IS_GROUP_ELEMENT(element))
2015       {
2016         Feld[x][y] = GetElementFromGroupElement(element);
2017
2018         InitField(x, y, init_game);
2019       }
2020
2021       break;
2022   }
2023
2024   if (!init_game)
2025     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2026 }
2027
2028 static void InitField_WithBug1(int x, int y, boolean init_game)
2029 {
2030   InitField(x, y, init_game);
2031
2032   // not needed to call InitMovDir() -- already done by InitField()!
2033   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2034       CAN_MOVE(Feld[x][y]))
2035     InitMovDir(x, y);
2036 }
2037
2038 static void InitField_WithBug2(int x, int y, boolean init_game)
2039 {
2040   int old_element = Feld[x][y];
2041
2042   InitField(x, y, init_game);
2043
2044   // not needed to call InitMovDir() -- already done by InitField()!
2045   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2046       CAN_MOVE(old_element) &&
2047       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2048     InitMovDir(x, y);
2049
2050   /* this case is in fact a combination of not less than three bugs:
2051      first, it calls InitMovDir() for elements that can move, although this is
2052      already done by InitField(); then, it checks the element that was at this
2053      field _before_ the call to InitField() (which can change it); lastly, it
2054      was not called for "mole with direction" elements, which were treated as
2055      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2056   */
2057 }
2058
2059 static int get_key_element_from_nr(int key_nr)
2060 {
2061   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2062                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2063                           EL_EM_KEY_1 : EL_KEY_1);
2064
2065   return key_base_element + key_nr;
2066 }
2067
2068 static int get_next_dropped_element(struct PlayerInfo *player)
2069 {
2070   return (player->inventory_size > 0 ?
2071           player->inventory_element[player->inventory_size - 1] :
2072           player->inventory_infinite_element != EL_UNDEFINED ?
2073           player->inventory_infinite_element :
2074           player->dynabombs_left > 0 ?
2075           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2076           EL_UNDEFINED);
2077 }
2078
2079 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2080 {
2081   // pos >= 0: get element from bottom of the stack;
2082   // pos <  0: get element from top of the stack
2083
2084   if (pos < 0)
2085   {
2086     int min_inventory_size = -pos;
2087     int inventory_pos = player->inventory_size - min_inventory_size;
2088     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2089
2090     return (player->inventory_size >= min_inventory_size ?
2091             player->inventory_element[inventory_pos] :
2092             player->inventory_infinite_element != EL_UNDEFINED ?
2093             player->inventory_infinite_element :
2094             player->dynabombs_left >= min_dynabombs_left ?
2095             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2096             EL_UNDEFINED);
2097   }
2098   else
2099   {
2100     int min_dynabombs_left = pos + 1;
2101     int min_inventory_size = pos + 1 - player->dynabombs_left;
2102     int inventory_pos = pos - player->dynabombs_left;
2103
2104     return (player->inventory_infinite_element != EL_UNDEFINED ?
2105             player->inventory_infinite_element :
2106             player->dynabombs_left >= min_dynabombs_left ?
2107             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2108             player->inventory_size >= min_inventory_size ?
2109             player->inventory_element[inventory_pos] :
2110             EL_UNDEFINED);
2111   }
2112 }
2113
2114 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2115 {
2116   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2117   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2118   int compare_result;
2119
2120   if (gpo1->sort_priority != gpo2->sort_priority)
2121     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2122   else
2123     compare_result = gpo1->nr - gpo2->nr;
2124
2125   return compare_result;
2126 }
2127
2128 int getPlayerInventorySize(int player_nr)
2129 {
2130   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2131     return game_em.ply[player_nr]->dynamite;
2132   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2133     return game_sp.red_disk_count;
2134   else
2135     return stored_player[player_nr].inventory_size;
2136 }
2137
2138 static void InitGameControlValues(void)
2139 {
2140   int i;
2141
2142   for (i = 0; game_panel_controls[i].nr != -1; i++)
2143   {
2144     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2145     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2146     struct TextPosInfo *pos = gpc->pos;
2147     int nr = gpc->nr;
2148     int type = gpc->type;
2149
2150     if (nr != i)
2151     {
2152       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2153       Error(ERR_EXIT, "this should not happen -- please debug");
2154     }
2155
2156     // force update of game controls after initialization
2157     gpc->value = gpc->last_value = -1;
2158     gpc->frame = gpc->last_frame = -1;
2159     gpc->gfx_frame = -1;
2160
2161     // determine panel value width for later calculation of alignment
2162     if (type == TYPE_INTEGER || type == TYPE_STRING)
2163     {
2164       pos->width = pos->size * getFontWidth(pos->font);
2165       pos->height = getFontHeight(pos->font);
2166     }
2167     else if (type == TYPE_ELEMENT)
2168     {
2169       pos->width = pos->size;
2170       pos->height = pos->size;
2171     }
2172
2173     // fill structure for game panel draw order
2174     gpo->nr = gpc->nr;
2175     gpo->sort_priority = pos->sort_priority;
2176   }
2177
2178   // sort game panel controls according to sort_priority and control number
2179   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2180         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2181 }
2182
2183 static void UpdatePlayfieldElementCount(void)
2184 {
2185   boolean use_element_count = FALSE;
2186   int i, j, x, y;
2187
2188   // first check if it is needed at all to calculate playfield element count
2189   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2190     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2191       use_element_count = TRUE;
2192
2193   if (!use_element_count)
2194     return;
2195
2196   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2197     element_info[i].element_count = 0;
2198
2199   SCAN_PLAYFIELD(x, y)
2200   {
2201     element_info[Feld[x][y]].element_count++;
2202   }
2203
2204   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2205     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2206       if (IS_IN_GROUP(j, i))
2207         element_info[EL_GROUP_START + i].element_count +=
2208           element_info[j].element_count;
2209 }
2210
2211 static void UpdateGameControlValues(void)
2212 {
2213   int i, k;
2214   int time = (game.LevelSolved ?
2215               game.LevelSolved_CountingTime :
2216               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2217               game_em.lev->time :
2218               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2219               game_sp.time_played :
2220               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2221               game_mm.energy_left :
2222               game.no_time_limit ? TimePlayed : TimeLeft);
2223   int score = (game.LevelSolved ?
2224                game.LevelSolved_CountingScore :
2225                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226                game_em.lev->score :
2227                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2228                game_sp.score :
2229                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2230                game_mm.score :
2231                game.score);
2232   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2233               game_em.lev->gems_needed :
2234               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2235               game_sp.infotrons_still_needed :
2236               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2237               game_mm.kettles_still_needed :
2238               game.gems_still_needed);
2239   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2240                      game_em.lev->gems_needed > 0 :
2241                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2242                      game_sp.infotrons_still_needed > 0 :
2243                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2244                      game_mm.kettles_still_needed > 0 ||
2245                      game_mm.lights_still_needed > 0 :
2246                      game.gems_still_needed > 0 ||
2247                      game.sokoban_fields_still_needed > 0 ||
2248                      game.sokoban_objects_still_needed > 0 ||
2249                      game.lights_still_needed > 0);
2250   int health = (game.LevelSolved ?
2251                 game.LevelSolved_CountingHealth :
2252                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2253                 MM_HEALTH(game_mm.laser_overload_value) :
2254                 game.health);
2255
2256   UpdatePlayfieldElementCount();
2257
2258   // update game panel control values
2259
2260   // used instead of "level_nr" (for network games)
2261   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2262   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2263
2264   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2265   for (i = 0; i < MAX_NUM_KEYS; i++)
2266     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2267   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2268   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2269
2270   if (game.centered_player_nr == -1)
2271   {
2272     for (i = 0; i < MAX_PLAYERS; i++)
2273     {
2274       // only one player in Supaplex game engine
2275       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2276         break;
2277
2278       for (k = 0; k < MAX_NUM_KEYS; k++)
2279       {
2280         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2281         {
2282           if (game_em.ply[i]->keys & (1 << k))
2283             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284               get_key_element_from_nr(k);
2285         }
2286         else if (stored_player[i].key[k])
2287           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2288             get_key_element_from_nr(k);
2289       }
2290
2291       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292         getPlayerInventorySize(i);
2293
2294       if (stored_player[i].num_white_keys > 0)
2295         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2296           EL_DC_KEY_WHITE;
2297
2298       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2299         stored_player[i].num_white_keys;
2300     }
2301   }
2302   else
2303   {
2304     int player_nr = game.centered_player_nr;
2305
2306     for (k = 0; k < MAX_NUM_KEYS; k++)
2307     {
2308       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2309       {
2310         if (game_em.ply[player_nr]->keys & (1 << k))
2311           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2312             get_key_element_from_nr(k);
2313       }
2314       else if (stored_player[player_nr].key[k])
2315         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2316           get_key_element_from_nr(k);
2317     }
2318
2319     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2320       getPlayerInventorySize(player_nr);
2321
2322     if (stored_player[player_nr].num_white_keys > 0)
2323       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2324
2325     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2326       stored_player[player_nr].num_white_keys;
2327   }
2328
2329   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2330   {
2331     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2332       get_inventory_element_from_pos(local_player, i);
2333     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2334       get_inventory_element_from_pos(local_player, -i - 1);
2335   }
2336
2337   game_panel_controls[GAME_PANEL_SCORE].value = score;
2338   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2339
2340   game_panel_controls[GAME_PANEL_TIME].value = time;
2341
2342   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2343   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2344   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2345
2346   if (level.time == 0)
2347     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2348   else
2349     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2350
2351   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2352   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2353
2354   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2355
2356   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2357     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2358      EL_EMPTY);
2359   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2360     local_player->shield_normal_time_left;
2361   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2362     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2363      EL_EMPTY);
2364   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2365     local_player->shield_deadly_time_left;
2366
2367   game_panel_controls[GAME_PANEL_EXIT].value =
2368     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2369
2370   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2371     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2372   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2373     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2374      EL_EMC_MAGIC_BALL_SWITCH);
2375
2376   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2377     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2378   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2379     game.light_time_left;
2380
2381   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2382     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2383   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2384     game.timegate_time_left;
2385
2386   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2387     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2388
2389   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2390     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2391   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2392     game.lenses_time_left;
2393
2394   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2395     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2396   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2397     game.magnify_time_left;
2398
2399   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2400     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2401      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2402      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2403      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2404      EL_BALLOON_SWITCH_NONE);
2405
2406   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2407     local_player->dynabomb_count;
2408   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2409     local_player->dynabomb_size;
2410   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2411     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2412
2413   game_panel_controls[GAME_PANEL_PENGUINS].value =
2414     game.friends_still_needed;
2415
2416   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2417     game.sokoban_objects_still_needed;
2418   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2419     game.sokoban_fields_still_needed;
2420
2421   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2422     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2423
2424   for (i = 0; i < NUM_BELTS; i++)
2425   {
2426     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2427       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2428        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2429     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2430       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2431   }
2432
2433   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2434     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2435   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2436     game.magic_wall_time_left;
2437
2438   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2439     local_player->gravity;
2440
2441   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2442     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2443
2444   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2445     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2446       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2447        game.panel.element[i].id : EL_UNDEFINED);
2448
2449   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2450     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2451       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2452        element_info[game.panel.element_count[i].id].element_count : 0);
2453
2454   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2455     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2456       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2457        element_info[game.panel.ce_score[i].id].collect_score : 0);
2458
2459   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2460     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2461       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2462        element_info[game.panel.ce_score_element[i].id].collect_score :
2463        EL_UNDEFINED);
2464
2465   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2466   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2467   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2468
2469   // update game panel control frames
2470
2471   for (i = 0; game_panel_controls[i].nr != -1; i++)
2472   {
2473     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2474
2475     if (gpc->type == TYPE_ELEMENT)
2476     {
2477       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2478       {
2479         int last_anim_random_frame = gfx.anim_random_frame;
2480         int element = gpc->value;
2481         int graphic = el2panelimg(element);
2482
2483         if (gpc->value != gpc->last_value)
2484         {
2485           gpc->gfx_frame = 0;
2486           gpc->gfx_random = INIT_GFX_RANDOM();
2487         }
2488         else
2489         {
2490           gpc->gfx_frame++;
2491
2492           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2493               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2494             gpc->gfx_random = INIT_GFX_RANDOM();
2495         }
2496
2497         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2498           gfx.anim_random_frame = gpc->gfx_random;
2499
2500         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2501           gpc->gfx_frame = element_info[element].collect_score;
2502
2503         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2504                                               gpc->gfx_frame);
2505
2506         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2507           gfx.anim_random_frame = last_anim_random_frame;
2508       }
2509     }
2510     else if (gpc->type == TYPE_GRAPHIC)
2511     {
2512       if (gpc->graphic != IMG_UNDEFINED)
2513       {
2514         int last_anim_random_frame = gfx.anim_random_frame;
2515         int graphic = gpc->graphic;
2516
2517         if (gpc->value != gpc->last_value)
2518         {
2519           gpc->gfx_frame = 0;
2520           gpc->gfx_random = INIT_GFX_RANDOM();
2521         }
2522         else
2523         {
2524           gpc->gfx_frame++;
2525
2526           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2527               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2528             gpc->gfx_random = INIT_GFX_RANDOM();
2529         }
2530
2531         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2532           gfx.anim_random_frame = gpc->gfx_random;
2533
2534         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2535
2536         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2537           gfx.anim_random_frame = last_anim_random_frame;
2538       }
2539     }
2540   }
2541 }
2542
2543 static void DisplayGameControlValues(void)
2544 {
2545   boolean redraw_panel = FALSE;
2546   int i;
2547
2548   for (i = 0; game_panel_controls[i].nr != -1; i++)
2549   {
2550     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2551
2552     if (PANEL_DEACTIVATED(gpc->pos))
2553       continue;
2554
2555     if (gpc->value == gpc->last_value &&
2556         gpc->frame == gpc->last_frame)
2557       continue;
2558
2559     redraw_panel = TRUE;
2560   }
2561
2562   if (!redraw_panel)
2563     return;
2564
2565   // copy default game door content to main double buffer
2566
2567   // !!! CHECK AGAIN !!!
2568   SetPanelBackground();
2569   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2570   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2571
2572   // redraw game control buttons
2573   RedrawGameButtons();
2574
2575   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2576
2577   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2578   {
2579     int nr = game_panel_order[i].nr;
2580     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2581     struct TextPosInfo *pos = gpc->pos;
2582     int type = gpc->type;
2583     int value = gpc->value;
2584     int frame = gpc->frame;
2585     int size = pos->size;
2586     int font = pos->font;
2587     boolean draw_masked = pos->draw_masked;
2588     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2589
2590     if (PANEL_DEACTIVATED(pos))
2591       continue;
2592
2593     gpc->last_value = value;
2594     gpc->last_frame = frame;
2595
2596     if (type == TYPE_INTEGER)
2597     {
2598       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2599           nr == GAME_PANEL_TIME)
2600       {
2601         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2602
2603         if (use_dynamic_size)           // use dynamic number of digits
2604         {
2605           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2606           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2607           int size2 = size1 + 1;
2608           int font1 = pos->font;
2609           int font2 = pos->font_alt;
2610
2611           size = (value < value_change ? size1 : size2);
2612           font = (value < value_change ? font1 : font2);
2613         }
2614       }
2615
2616       // correct text size if "digits" is zero or less
2617       if (size <= 0)
2618         size = strlen(int2str(value, size));
2619
2620       // dynamically correct text alignment
2621       pos->width = size * getFontWidth(font);
2622
2623       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2624                   int2str(value, size), font, mask_mode);
2625     }
2626     else if (type == TYPE_ELEMENT)
2627     {
2628       int element, graphic;
2629       Bitmap *src_bitmap;
2630       int src_x, src_y;
2631       int width, height;
2632       int dst_x = PANEL_XPOS(pos);
2633       int dst_y = PANEL_YPOS(pos);
2634
2635       if (value != EL_UNDEFINED && value != EL_EMPTY)
2636       {
2637         element = value;
2638         graphic = el2panelimg(value);
2639
2640         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2641
2642         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2643           size = TILESIZE;
2644
2645         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2646                               &src_x, &src_y);
2647
2648         width  = graphic_info[graphic].width  * size / TILESIZE;
2649         height = graphic_info[graphic].height * size / TILESIZE;
2650
2651         if (draw_masked)
2652           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2653                            dst_x, dst_y);
2654         else
2655           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2656                      dst_x, dst_y);
2657       }
2658     }
2659     else if (type == TYPE_GRAPHIC)
2660     {
2661       int graphic        = gpc->graphic;
2662       int graphic_active = gpc->graphic_active;
2663       Bitmap *src_bitmap;
2664       int src_x, src_y;
2665       int width, height;
2666       int dst_x = PANEL_XPOS(pos);
2667       int dst_y = PANEL_YPOS(pos);
2668       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2669                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2670
2671       if (graphic != IMG_UNDEFINED && !skip)
2672       {
2673         if (pos->style == STYLE_REVERSE)
2674           value = 100 - value;
2675
2676         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2677
2678         if (pos->direction & MV_HORIZONTAL)
2679         {
2680           width  = graphic_info[graphic_active].width * value / 100;
2681           height = graphic_info[graphic_active].height;
2682
2683           if (pos->direction == MV_LEFT)
2684           {
2685             src_x += graphic_info[graphic_active].width - width;
2686             dst_x += graphic_info[graphic_active].width - width;
2687           }
2688         }
2689         else
2690         {
2691           width  = graphic_info[graphic_active].width;
2692           height = graphic_info[graphic_active].height * value / 100;
2693
2694           if (pos->direction == MV_UP)
2695           {
2696             src_y += graphic_info[graphic_active].height - height;
2697             dst_y += graphic_info[graphic_active].height - height;
2698           }
2699         }
2700
2701         if (draw_masked)
2702           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2703                            dst_x, dst_y);
2704         else
2705           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2706                      dst_x, dst_y);
2707
2708         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2709
2710         if (pos->direction & MV_HORIZONTAL)
2711         {
2712           if (pos->direction == MV_RIGHT)
2713           {
2714             src_x += width;
2715             dst_x += width;
2716           }
2717           else
2718           {
2719             dst_x = PANEL_XPOS(pos);
2720           }
2721
2722           width = graphic_info[graphic].width - width;
2723         }
2724         else
2725         {
2726           if (pos->direction == MV_DOWN)
2727           {
2728             src_y += height;
2729             dst_y += height;
2730           }
2731           else
2732           {
2733             dst_y = PANEL_YPOS(pos);
2734           }
2735
2736           height = graphic_info[graphic].height - height;
2737         }
2738
2739         if (draw_masked)
2740           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2741                            dst_x, dst_y);
2742         else
2743           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2744                      dst_x, dst_y);
2745       }
2746     }
2747     else if (type == TYPE_STRING)
2748     {
2749       boolean active = (value != 0);
2750       char *state_normal = "off";
2751       char *state_active = "on";
2752       char *state = (active ? state_active : state_normal);
2753       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2754                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2755                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2756                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2757
2758       if (nr == GAME_PANEL_GRAVITY_STATE)
2759       {
2760         int font1 = pos->font;          // (used for normal state)
2761         int font2 = pos->font_alt;      // (used for active state)
2762
2763         font = (active ? font2 : font1);
2764       }
2765
2766       if (s != NULL)
2767       {
2768         char *s_cut;
2769
2770         if (size <= 0)
2771         {
2772           // don't truncate output if "chars" is zero or less
2773           size = strlen(s);
2774
2775           // dynamically correct text alignment
2776           pos->width = size * getFontWidth(font);
2777         }
2778
2779         s_cut = getStringCopyN(s, size);
2780
2781         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2782                     s_cut, font, mask_mode);
2783
2784         free(s_cut);
2785       }
2786     }
2787
2788     redraw_mask |= REDRAW_DOOR_1;
2789   }
2790
2791   SetGameStatus(GAME_MODE_PLAYING);
2792 }
2793
2794 void UpdateAndDisplayGameControlValues(void)
2795 {
2796   if (tape.deactivate_display)
2797     return;
2798
2799   UpdateGameControlValues();
2800   DisplayGameControlValues();
2801 }
2802
2803 #if 0
2804 static void UpdateGameDoorValues(void)
2805 {
2806   UpdateGameControlValues();
2807 }
2808 #endif
2809
2810 void DrawGameDoorValues(void)
2811 {
2812   DisplayGameControlValues();
2813 }
2814
2815
2816 // ============================================================================
2817 // InitGameEngine()
2818 // ----------------------------------------------------------------------------
2819 // initialize game engine due to level / tape version number
2820 // ============================================================================
2821
2822 static void InitGameEngine(void)
2823 {
2824   int i, j, k, l, x, y;
2825
2826   // set game engine from tape file when re-playing, else from level file
2827   game.engine_version = (tape.playing ? tape.engine_version :
2828                          level.game_version);
2829
2830   // set single or multi-player game mode (needed for re-playing tapes)
2831   game.team_mode = setup.team_mode;
2832
2833   if (tape.playing)
2834   {
2835     int num_players = 0;
2836
2837     for (i = 0; i < MAX_PLAYERS; i++)
2838       if (tape.player_participates[i])
2839         num_players++;
2840
2841     // multi-player tapes contain input data for more than one player
2842     game.team_mode = (num_players > 1);
2843   }
2844
2845 #if 0
2846   printf("level %d: level.game_version  == %06d\n", level_nr,
2847          level.game_version);
2848   printf("          tape.file_version   == %06d\n",
2849          tape.file_version);
2850   printf("          tape.game_version   == %06d\n",
2851          tape.game_version);
2852   printf("          tape.engine_version == %06d\n",
2853          tape.engine_version);
2854   printf("       => game.engine_version == %06d [tape mode: %s]\n",
2855          game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2856 #endif
2857
2858   // --------------------------------------------------------------------------
2859   // set flags for bugs and changes according to active game engine version
2860   // --------------------------------------------------------------------------
2861
2862   /*
2863     Summary of bugfix:
2864     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2865
2866     Bug was introduced in version:
2867     2.0.1
2868
2869     Bug was fixed in version:
2870     4.1.4.2
2871
2872     Description:
2873     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2874     but the property "can fall" was missing, which caused some levels to be
2875     unsolvable. This was fixed in version 4.1.4.2.
2876
2877     Affected levels/tapes:
2878     An example for a tape that was fixed by this bugfix is tape 029 from the
2879     level set "rnd_sam_bateman".
2880     The wrong behaviour will still be used for all levels or tapes that were
2881     created/recorded with it. An example for this is tape 023 from the level
2882     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2883   */
2884
2885   boolean use_amoeba_dropping_cannot_fall_bug =
2886     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2887       game.engine_version <= VERSION_IDENT(4,1,4,1)) ||
2888      (tape.playing &&
2889       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2890       tape.game_version <= VERSION_IDENT(4,1,4,1)));
2891
2892   /*
2893     Summary of bugfix/change:
2894     Fixed move speed of elements entering or leaving magic wall.
2895
2896     Fixed/changed in version:
2897     2.0.1
2898
2899     Description:
2900     Before 2.0.1, move speed of elements entering or leaving magic wall was
2901     twice as fast as it is now.
2902     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2903
2904     Affected levels/tapes:
2905     The first condition is generally needed for all levels/tapes before version
2906     2.0.1, which might use the old behaviour before it was changed; known tapes
2907     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2908     The second condition is an exception from the above case and is needed for
2909     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2910     above, but before it was known that this change would break tapes like the
2911     above and was fixed in 4.1.4.2, so that the changed behaviour was active
2912     although the engine version while recording maybe was before 2.0.1. There
2913     are a lot of tapes that are affected by this exception, like tape 006 from
2914     the level set "rnd_conor_mancone".
2915   */
2916
2917   boolean use_old_move_stepsize_for_magic_wall =
2918     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2919      !(tape.playing &&
2920        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2921        tape.game_version <  VERSION_IDENT(4,1,4,2)));
2922
2923   /*
2924     Summary of bugfix/change:
2925     Fixed handling for custom elements that change when pushed by the player.
2926
2927     Fixed/changed in version:
2928     3.1.0
2929
2930     Description:
2931     Before 3.1.0, custom elements that "change when pushing" changed directly
2932     after the player started pushing them (until then handled in "DigField()").
2933     Since 3.1.0, these custom elements are not changed until the "pushing"
2934     move of the element is finished (now handled in "ContinueMoving()").
2935
2936     Affected levels/tapes:
2937     The first condition is generally needed for all levels/tapes before version
2938     3.1.0, which might use the old behaviour before it was changed; known tapes
2939     that are affected are some tapes from the level set "Walpurgis Gardens" by
2940     Jamie Cullen.
2941     The second condition is an exception from the above case and is needed for
2942     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2943     above (including some development versions of 3.1.0), but before it was
2944     known that this change would break tapes like the above and was fixed in
2945     3.1.1, so that the changed behaviour was active although the engine version
2946     while recording maybe was before 3.1.0. There is at least one tape that is
2947     affected by this exception, which is the tape for the one-level set "Bug
2948     Machine" by Juergen Bonhagen.
2949   */
2950
2951   game.use_change_when_pushing_bug =
2952     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2953      !(tape.playing &&
2954        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2955        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2956
2957   /*
2958     Summary of bugfix/change:
2959     Fixed handling for blocking the field the player leaves when moving.
2960
2961     Fixed/changed in version:
2962     3.1.1
2963
2964     Description:
2965     Before 3.1.1, when "block last field when moving" was enabled, the field
2966     the player is leaving when moving was blocked for the time of the move,
2967     and was directly unblocked afterwards. This resulted in the last field
2968     being blocked for exactly one less than the number of frames of one player
2969     move. Additionally, even when blocking was disabled, the last field was
2970     blocked for exactly one frame.
2971     Since 3.1.1, due to changes in player movement handling, the last field
2972     is not blocked at all when blocking is disabled. When blocking is enabled,
2973     the last field is blocked for exactly the number of frames of one player
2974     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2975     last field is blocked for exactly one more than the number of frames of
2976     one player move.
2977
2978     Affected levels/tapes:
2979     (!!! yet to be determined -- probably many !!!)
2980   */
2981
2982   game.use_block_last_field_bug =
2983     (game.engine_version < VERSION_IDENT(3,1,1,0));
2984
2985   /* various special flags and settings for native Emerald Mine game engine */
2986
2987   game_em.use_single_button =
2988     (game.engine_version > VERSION_IDENT(4,0,0,2));
2989
2990   game_em.use_snap_key_bug =
2991     (game.engine_version < VERSION_IDENT(4,0,1,0));
2992
2993   game_em.use_old_explosions =
2994     (game.engine_version < VERSION_IDENT(4,1,4,2));
2995
2996   game_em.use_old_android =
2997     (game.engine_version < VERSION_IDENT(4,1,4,2));
2998
2999   game_em.use_wrap_around =
3000     (game.engine_version > VERSION_IDENT(4,1,4,1));
3001
3002   // --------------------------------------------------------------------------
3003
3004   // set maximal allowed number of custom element changes per game frame
3005   game.max_num_changes_per_frame = 1;
3006
3007   // default scan direction: scan playfield from top/left to bottom/right
3008   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3009
3010   // dynamically adjust element properties according to game engine version
3011   InitElementPropertiesEngine(game.engine_version);
3012
3013   // ---------- initialize special element properties -------------------------
3014
3015   // "EL_AMOEBA_DROPPING" missed property "can fall" between 2.0.1 and 4.1.4.1
3016   if (use_amoeba_dropping_cannot_fall_bug)
3017     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3018
3019   // ---------- initialize player's initial move delay ------------------------
3020
3021   // dynamically adjust player properties according to level information
3022   for (i = 0; i < MAX_PLAYERS; i++)
3023     game.initial_move_delay_value[i] =
3024       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3025
3026   // dynamically adjust player properties according to game engine version
3027   for (i = 0; i < MAX_PLAYERS; i++)
3028     game.initial_move_delay[i] =
3029       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3030        game.initial_move_delay_value[i] : 0);
3031
3032   // ---------- initialize player's initial push delay ------------------------
3033
3034   // dynamically adjust player properties according to game engine version
3035   game.initial_push_delay_value =
3036     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3037
3038   // ---------- initialize changing elements ----------------------------------
3039
3040   // initialize changing elements information
3041   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3042   {
3043     struct ElementInfo *ei = &element_info[i];
3044
3045     // this pointer might have been changed in the level editor
3046     ei->change = &ei->change_page[0];
3047
3048     if (!IS_CUSTOM_ELEMENT(i))
3049     {
3050       ei->change->target_element = EL_EMPTY_SPACE;
3051       ei->change->delay_fixed = 0;
3052       ei->change->delay_random = 0;
3053       ei->change->delay_frames = 1;
3054     }
3055
3056     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3057     {
3058       ei->has_change_event[j] = FALSE;
3059
3060       ei->event_page_nr[j] = 0;
3061       ei->event_page[j] = &ei->change_page[0];
3062     }
3063   }
3064
3065   // add changing elements from pre-defined list
3066   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3067   {
3068     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3069     struct ElementInfo *ei = &element_info[ch_delay->element];
3070
3071     ei->change->target_element       = ch_delay->target_element;
3072     ei->change->delay_fixed          = ch_delay->change_delay;
3073
3074     ei->change->pre_change_function  = ch_delay->pre_change_function;
3075     ei->change->change_function      = ch_delay->change_function;
3076     ei->change->post_change_function = ch_delay->post_change_function;
3077
3078     ei->change->can_change = TRUE;
3079     ei->change->can_change_or_has_action = TRUE;
3080
3081     ei->has_change_event[CE_DELAY] = TRUE;
3082
3083     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3084     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3085   }
3086
3087   // ---------- initialize internal run-time variables ------------------------
3088
3089   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3090   {
3091     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3092
3093     for (j = 0; j < ei->num_change_pages; j++)
3094     {
3095       ei->change_page[j].can_change_or_has_action =
3096         (ei->change_page[j].can_change |
3097          ei->change_page[j].has_action);
3098     }
3099   }
3100
3101   // add change events from custom element configuration
3102   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3103   {
3104     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3105
3106     for (j = 0; j < ei->num_change_pages; j++)
3107     {
3108       if (!ei->change_page[j].can_change_or_has_action)
3109         continue;
3110
3111       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3112       {
3113         // only add event page for the first page found with this event
3114         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3115         {
3116           ei->has_change_event[k] = TRUE;
3117
3118           ei->event_page_nr[k] = j;
3119           ei->event_page[k] = &ei->change_page[j];
3120         }
3121       }
3122     }
3123   }
3124
3125   // ---------- initialize reference elements in change conditions ------------
3126
3127   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3128   {
3129     int element = EL_CUSTOM_START + i;
3130     struct ElementInfo *ei = &element_info[element];
3131
3132     for (j = 0; j < ei->num_change_pages; j++)
3133     {
3134       int trigger_element = ei->change_page[j].initial_trigger_element;
3135
3136       if (trigger_element >= EL_PREV_CE_8 &&
3137           trigger_element <= EL_NEXT_CE_8)
3138         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3139
3140       ei->change_page[j].trigger_element = trigger_element;
3141     }
3142   }
3143
3144   // ---------- initialize run-time trigger player and element ----------------
3145
3146   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3147   {
3148     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3149
3150     for (j = 0; j < ei->num_change_pages; j++)
3151     {
3152       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3153       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3154       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3155       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3156       ei->change_page[j].actual_trigger_ce_value = 0;
3157       ei->change_page[j].actual_trigger_ce_score = 0;
3158     }
3159   }
3160
3161   // ---------- initialize trigger events -------------------------------------
3162
3163   // initialize trigger events information
3164   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3165     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3166       trigger_events[i][j] = FALSE;
3167
3168   // add trigger events from element change event properties
3169   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3170   {
3171     struct ElementInfo *ei = &element_info[i];
3172
3173     for (j = 0; j < ei->num_change_pages; j++)
3174     {
3175       if (!ei->change_page[j].can_change_or_has_action)
3176         continue;
3177
3178       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3179       {
3180         int trigger_element = ei->change_page[j].trigger_element;
3181
3182         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3183         {
3184           if (ei->change_page[j].has_event[k])
3185           {
3186             if (IS_GROUP_ELEMENT(trigger_element))
3187             {
3188               struct ElementGroupInfo *group =
3189                 element_info[trigger_element].group;
3190
3191               for (l = 0; l < group->num_elements_resolved; l++)
3192                 trigger_events[group->element_resolved[l]][k] = TRUE;
3193             }
3194             else if (trigger_element == EL_ANY_ELEMENT)
3195               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3196                 trigger_events[l][k] = TRUE;
3197             else
3198               trigger_events[trigger_element][k] = TRUE;
3199           }
3200         }
3201       }
3202     }
3203   }
3204
3205   // ---------- initialize push delay -----------------------------------------
3206
3207   // initialize push delay values to default
3208   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3209   {
3210     if (!IS_CUSTOM_ELEMENT(i))
3211     {
3212       // set default push delay values (corrected since version 3.0.7-1)
3213       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3214       {
3215         element_info[i].push_delay_fixed = 2;
3216         element_info[i].push_delay_random = 8;
3217       }
3218       else
3219       {
3220         element_info[i].push_delay_fixed = 8;
3221         element_info[i].push_delay_random = 8;
3222       }
3223     }
3224   }
3225
3226   // set push delay value for certain elements from pre-defined list
3227   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3228   {
3229     int e = push_delay_list[i].element;
3230
3231     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3232     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3233   }
3234
3235   // set push delay value for Supaplex elements for newer engine versions
3236   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3237   {
3238     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3239     {
3240       if (IS_SP_ELEMENT(i))
3241       {
3242         // set SP push delay to just enough to push under a falling zonk
3243         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3244
3245         element_info[i].push_delay_fixed  = delay;
3246         element_info[i].push_delay_random = 0;
3247       }
3248     }
3249   }
3250
3251   // ---------- initialize move stepsize --------------------------------------
3252
3253   // initialize move stepsize values to default
3254   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3255     if (!IS_CUSTOM_ELEMENT(i))
3256       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3257
3258   // set move stepsize value for certain elements from pre-defined list
3259   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3260   {
3261     int e = move_stepsize_list[i].element;
3262
3263     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3264
3265     // set move stepsize value for certain elements for older engine versions
3266     if (use_old_move_stepsize_for_magic_wall)
3267     {
3268       if (e == EL_MAGIC_WALL_FILLING ||
3269           e == EL_MAGIC_WALL_EMPTYING ||
3270           e == EL_BD_MAGIC_WALL_FILLING ||
3271           e == EL_BD_MAGIC_WALL_EMPTYING)
3272         element_info[e].move_stepsize *= 2;
3273     }
3274   }
3275
3276   // ---------- initialize collect score --------------------------------------
3277
3278   // initialize collect score values for custom elements from initial value
3279   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3280     if (IS_CUSTOM_ELEMENT(i))
3281       element_info[i].collect_score = element_info[i].collect_score_initial;
3282
3283   // ---------- initialize collect count --------------------------------------
3284
3285   // initialize collect count values for non-custom elements
3286   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3287     if (!IS_CUSTOM_ELEMENT(i))
3288       element_info[i].collect_count_initial = 0;
3289
3290   // add collect count values for all elements from pre-defined list
3291   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3292     element_info[collect_count_list[i].element].collect_count_initial =
3293       collect_count_list[i].count;
3294
3295   // ---------- initialize access direction -----------------------------------
3296
3297   // initialize access direction values to default (access from every side)
3298   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3299     if (!IS_CUSTOM_ELEMENT(i))
3300       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3301
3302   // set access direction value for certain elements from pre-defined list
3303   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3304     element_info[access_direction_list[i].element].access_direction =
3305       access_direction_list[i].direction;
3306
3307   // ---------- initialize explosion content ----------------------------------
3308   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3309   {
3310     if (IS_CUSTOM_ELEMENT(i))
3311       continue;
3312
3313     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3314     {
3315       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3316
3317       element_info[i].content.e[x][y] =
3318         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3319          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3320          i == EL_PLAYER_3 ? EL_EMERALD :
3321          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3322          i == EL_MOLE ? EL_EMERALD_RED :
3323          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3324          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3325          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3326          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3327          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3328          i == EL_WALL_EMERALD ? EL_EMERALD :
3329          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3330          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3331          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3332          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3333          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3334          i == EL_WALL_PEARL ? EL_PEARL :
3335          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3336          EL_EMPTY);
3337     }
3338   }
3339
3340   // ---------- initialize recursion detection --------------------------------
3341   recursion_loop_depth = 0;
3342   recursion_loop_detected = FALSE;
3343   recursion_loop_element = EL_UNDEFINED;
3344
3345   // ---------- initialize graphics engine ------------------------------------
3346   game.scroll_delay_value =
3347     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3348      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3349      !setup.forced_scroll_delay           ? 0 :
3350      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3351   game.scroll_delay_value =
3352     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3353
3354   // ---------- initialize game engine snapshots ------------------------------
3355   for (i = 0; i < MAX_PLAYERS; i++)
3356     game.snapshot.last_action[i] = 0;
3357   game.snapshot.changed_action = FALSE;
3358   game.snapshot.collected_item = FALSE;
3359   game.snapshot.mode =
3360     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3361      SNAPSHOT_MODE_EVERY_STEP :
3362      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3363      SNAPSHOT_MODE_EVERY_MOVE :
3364      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3365      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3366   game.snapshot.save_snapshot = FALSE;
3367
3368   // ---------- initialize level time for Supaplex engine ---------------------
3369   // Supaplex levels with time limit currently unsupported -- should be added
3370   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3371     level.time = 0;
3372
3373   // ---------- initialize flags for handling game actions --------------------
3374
3375   // set flags for game actions to default values
3376   game.use_key_actions = TRUE;
3377   game.use_mouse_actions = FALSE;
3378
3379   // when using Mirror Magic game engine, handle mouse events only
3380   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3381   {
3382     game.use_key_actions = FALSE;
3383     game.use_mouse_actions = TRUE;
3384   }
3385
3386   // check for custom elements with mouse click events
3387   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3388   {
3389     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3390     {
3391       int element = EL_CUSTOM_START + i;
3392
3393       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3394           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3395           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3396           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3397         game.use_mouse_actions = TRUE;
3398     }
3399   }
3400 }
3401
3402 static int get_num_special_action(int element, int action_first,
3403                                   int action_last)
3404 {
3405   int num_special_action = 0;
3406   int i, j;
3407
3408   for (i = action_first; i <= action_last; i++)
3409   {
3410     boolean found = FALSE;
3411
3412     for (j = 0; j < NUM_DIRECTIONS; j++)
3413       if (el_act_dir2img(element, i, j) !=
3414           el_act_dir2img(element, ACTION_DEFAULT, j))
3415         found = TRUE;
3416
3417     if (found)
3418       num_special_action++;
3419     else
3420       break;
3421   }
3422
3423   return num_special_action;
3424 }
3425
3426
3427 // ============================================================================
3428 // InitGame()
3429 // ----------------------------------------------------------------------------
3430 // initialize and start new game
3431 // ============================================================================
3432
3433 #if DEBUG_INIT_PLAYER
3434 static void DebugPrintPlayerStatus(char *message)
3435 {
3436   int i;
3437
3438   if (!options.debug)
3439     return;
3440
3441   printf("%s:\n", message);
3442
3443   for (i = 0; i < MAX_PLAYERS; i++)
3444   {
3445     struct PlayerInfo *player = &stored_player[i];
3446
3447     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3448            i + 1,
3449            player->present,
3450            player->connected,
3451            player->connected_locally,
3452            player->connected_network,
3453            player->active);
3454
3455     if (local_player == player)
3456       printf(" (local player)");
3457
3458     printf("\n");
3459   }
3460 }
3461 #endif
3462
3463 void InitGame(void)
3464 {
3465   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3466   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3467   int fade_mask = REDRAW_FIELD;
3468
3469   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3470   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3471   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3472   int initial_move_dir = MV_DOWN;
3473   int i, j, x, y;
3474
3475   // required here to update video display before fading (FIX THIS)
3476   DrawMaskedBorder(REDRAW_DOOR_2);
3477
3478   if (!game.restart_level)
3479     CloseDoor(DOOR_CLOSE_1);
3480
3481   SetGameStatus(GAME_MODE_PLAYING);
3482
3483   if (level_editor_test_game)
3484     FadeSkipNextFadeOut();
3485   else
3486     FadeSetEnterScreen();
3487
3488   if (CheckFadeAll())
3489     fade_mask = REDRAW_ALL;
3490
3491   FadeLevelSoundsAndMusic();
3492
3493   ExpireSoundLoops(TRUE);
3494
3495   FadeOut(fade_mask);
3496
3497   if (level_editor_test_game)
3498     FadeSkipNextFadeIn();
3499
3500   // needed if different viewport properties defined for playing
3501   ChangeViewportPropertiesIfNeeded();
3502
3503   ClearField();
3504
3505   DrawCompleteVideoDisplay();
3506
3507   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3508
3509   InitGameEngine();
3510   InitGameControlValues();
3511
3512   // initialize tape actions from game when recording tape
3513   if (tape.recording)
3514   {
3515     tape.use_key_actions   = game.use_key_actions;
3516     tape.use_mouse_actions = game.use_mouse_actions;
3517   }
3518
3519   // don't play tapes over network
3520   network_playing = (network.enabled && !tape.playing);
3521
3522   for (i = 0; i < MAX_PLAYERS; i++)
3523   {
3524     struct PlayerInfo *player = &stored_player[i];
3525
3526     player->index_nr = i;
3527     player->index_bit = (1 << i);
3528     player->element_nr = EL_PLAYER_1 + i;
3529
3530     player->present = FALSE;
3531     player->active = FALSE;
3532     player->mapped = FALSE;
3533
3534     player->killed = FALSE;
3535     player->reanimated = FALSE;
3536     player->buried = FALSE;
3537
3538     player->action = 0;
3539     player->effective_action = 0;
3540     player->programmed_action = 0;
3541     player->snap_action = 0;
3542
3543     player->mouse_action.lx = 0;
3544     player->mouse_action.ly = 0;
3545     player->mouse_action.button = 0;
3546     player->mouse_action.button_hint = 0;
3547
3548     player->effective_mouse_action.lx = 0;
3549     player->effective_mouse_action.ly = 0;
3550     player->effective_mouse_action.button = 0;
3551     player->effective_mouse_action.button_hint = 0;
3552
3553     for (j = 0; j < MAX_NUM_KEYS; j++)
3554       player->key[j] = FALSE;
3555
3556     player->num_white_keys = 0;
3557
3558     player->dynabomb_count = 0;
3559     player->dynabomb_size = 1;
3560     player->dynabombs_left = 0;
3561     player->dynabomb_xl = FALSE;
3562
3563     player->MovDir = initial_move_dir;
3564     player->MovPos = 0;
3565     player->GfxPos = 0;
3566     player->GfxDir = initial_move_dir;
3567     player->GfxAction = ACTION_DEFAULT;
3568     player->Frame = 0;
3569     player->StepFrame = 0;
3570
3571     player->initial_element = player->element_nr;
3572     player->artwork_element =
3573       (level.use_artwork_element[i] ? level.artwork_element[i] :
3574        player->element_nr);
3575     player->use_murphy = FALSE;
3576
3577     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3578     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3579
3580     player->gravity = level.initial_player_gravity[i];
3581
3582     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3583
3584     player->actual_frame_counter = 0;
3585
3586     player->step_counter = 0;
3587
3588     player->last_move_dir = initial_move_dir;
3589
3590     player->is_active = FALSE;
3591
3592     player->is_waiting = FALSE;
3593     player->is_moving = FALSE;
3594     player->is_auto_moving = FALSE;
3595     player->is_digging = FALSE;
3596     player->is_snapping = FALSE;
3597     player->is_collecting = FALSE;
3598     player->is_pushing = FALSE;
3599     player->is_switching = FALSE;
3600     player->is_dropping = FALSE;
3601     player->is_dropping_pressed = FALSE;
3602
3603     player->is_bored = FALSE;
3604     player->is_sleeping = FALSE;
3605
3606     player->was_waiting = TRUE;
3607     player->was_moving = FALSE;
3608     player->was_snapping = FALSE;
3609     player->was_dropping = FALSE;
3610
3611     player->force_dropping = FALSE;
3612
3613     player->frame_counter_bored = -1;
3614     player->frame_counter_sleeping = -1;
3615
3616     player->anim_delay_counter = 0;
3617     player->post_delay_counter = 0;
3618
3619     player->dir_waiting = initial_move_dir;
3620     player->action_waiting = ACTION_DEFAULT;
3621     player->last_action_waiting = ACTION_DEFAULT;
3622     player->special_action_bored = ACTION_DEFAULT;
3623     player->special_action_sleeping = ACTION_DEFAULT;
3624
3625     player->switch_x = -1;
3626     player->switch_y = -1;
3627
3628     player->drop_x = -1;
3629     player->drop_y = -1;
3630
3631     player->show_envelope = 0;
3632
3633     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3634
3635     player->push_delay       = -1;      // initialized when pushing starts
3636     player->push_delay_value = game.initial_push_delay_value;
3637
3638     player->drop_delay = 0;
3639     player->drop_pressed_delay = 0;
3640
3641     player->last_jx = -1;
3642     player->last_jy = -1;
3643     player->jx = -1;
3644     player->jy = -1;
3645
3646     player->shield_normal_time_left = 0;
3647     player->shield_deadly_time_left = 0;
3648
3649     player->inventory_infinite_element = EL_UNDEFINED;
3650     player->inventory_size = 0;
3651
3652     if (level.use_initial_inventory[i])
3653     {
3654       for (j = 0; j < level.initial_inventory_size[i]; j++)
3655       {
3656         int element = level.initial_inventory_content[i][j];
3657         int collect_count = element_info[element].collect_count_initial;
3658         int k;
3659
3660         if (!IS_CUSTOM_ELEMENT(element))
3661           collect_count = 1;
3662
3663         if (collect_count == 0)
3664           player->inventory_infinite_element = element;
3665         else
3666           for (k = 0; k < collect_count; k++)
3667             if (player->inventory_size < MAX_INVENTORY_SIZE)
3668               player->inventory_element[player->inventory_size++] = element;
3669       }
3670     }
3671
3672     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3673     SnapField(player, 0, 0);
3674
3675     map_player_action[i] = i;
3676   }
3677
3678   network_player_action_received = FALSE;
3679
3680   // initial null action
3681   if (network_playing)
3682     SendToServer_MovePlayer(MV_NONE);
3683
3684   FrameCounter = 0;
3685   TimeFrames = 0;
3686   TimePlayed = 0;
3687   TimeLeft = level.time;
3688   TapeTime = 0;
3689
3690   ScreenMovDir = MV_NONE;
3691   ScreenMovPos = 0;
3692   ScreenGfxPos = 0;
3693
3694   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3695
3696   game.robot_wheel_x = -1;
3697   game.robot_wheel_y = -1;
3698
3699   game.exit_x = -1;
3700   game.exit_y = -1;
3701
3702   game.all_players_gone = FALSE;
3703
3704   game.LevelSolved = FALSE;
3705   game.GameOver = FALSE;
3706
3707   game.GamePlayed = !tape.playing;
3708
3709   game.LevelSolved_GameWon = FALSE;
3710   game.LevelSolved_GameEnd = FALSE;
3711   game.LevelSolved_SaveTape = FALSE;
3712   game.LevelSolved_SaveScore = FALSE;
3713
3714   game.LevelSolved_CountingTime = 0;
3715   game.LevelSolved_CountingScore = 0;
3716   game.LevelSolved_CountingHealth = 0;
3717
3718   game.panel.active = TRUE;
3719
3720   game.no_time_limit = (level.time == 0);
3721
3722   game.yamyam_content_nr = 0;
3723   game.robot_wheel_active = FALSE;
3724   game.magic_wall_active = FALSE;
3725   game.magic_wall_time_left = 0;
3726   game.light_time_left = 0;
3727   game.timegate_time_left = 0;
3728   game.switchgate_pos = 0;
3729   game.wind_direction = level.wind_direction_initial;
3730
3731   game.score = 0;
3732   game.score_final = 0;
3733
3734   game.health = MAX_HEALTH;
3735   game.health_final = MAX_HEALTH;
3736
3737   game.gems_still_needed = level.gems_needed;
3738   game.sokoban_fields_still_needed = 0;
3739   game.sokoban_objects_still_needed = 0;
3740   game.lights_still_needed = 0;
3741   game.players_still_needed = 0;
3742   game.friends_still_needed = 0;
3743
3744   game.lenses_time_left = 0;
3745   game.magnify_time_left = 0;
3746
3747   game.ball_active = level.ball_active_initial;
3748   game.ball_content_nr = 0;
3749
3750   game.explosions_delayed = TRUE;
3751
3752   game.envelope_active = FALSE;
3753
3754   for (i = 0; i < NUM_BELTS; i++)
3755   {
3756     game.belt_dir[i] = MV_NONE;
3757     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3758   }
3759
3760   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3761     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3762
3763 #if DEBUG_INIT_PLAYER
3764   DebugPrintPlayerStatus("Player status at level initialization");
3765 #endif
3766
3767   SCAN_PLAYFIELD(x, y)
3768   {
3769     Feld[x][y] = Last[x][y] = level.field[x][y];
3770     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3771     ChangeDelay[x][y] = 0;
3772     ChangePage[x][y] = -1;
3773     CustomValue[x][y] = 0;              // initialized in InitField()
3774     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3775     AmoebaNr[x][y] = 0;
3776     WasJustMoving[x][y] = 0;
3777     WasJustFalling[x][y] = 0;
3778     CheckCollision[x][y] = 0;
3779     CheckImpact[x][y] = 0;
3780     Stop[x][y] = FALSE;
3781     Pushed[x][y] = FALSE;
3782
3783     ChangeCount[x][y] = 0;
3784     ChangeEvent[x][y] = -1;
3785
3786     ExplodePhase[x][y] = 0;
3787     ExplodeDelay[x][y] = 0;
3788     ExplodeField[x][y] = EX_TYPE_NONE;
3789
3790     RunnerVisit[x][y] = 0;
3791     PlayerVisit[x][y] = 0;
3792
3793     GfxFrame[x][y] = 0;
3794     GfxRandom[x][y] = INIT_GFX_RANDOM();
3795     GfxElement[x][y] = EL_UNDEFINED;
3796     GfxAction[x][y] = ACTION_DEFAULT;
3797     GfxDir[x][y] = MV_NONE;
3798     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3799   }
3800
3801   SCAN_PLAYFIELD(x, y)
3802   {
3803     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3804       emulate_bd = FALSE;
3805     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3806       emulate_sb = FALSE;
3807     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3808       emulate_sp = FALSE;
3809
3810     InitField(x, y, TRUE);
3811
3812     ResetGfxAnimation(x, y);
3813   }
3814
3815   InitBeltMovement();
3816
3817   for (i = 0; i < MAX_PLAYERS; i++)
3818   {
3819     struct PlayerInfo *player = &stored_player[i];
3820
3821     // set number of special actions for bored and sleeping animation
3822     player->num_special_action_bored =
3823       get_num_special_action(player->artwork_element,
3824                              ACTION_BORING_1, ACTION_BORING_LAST);
3825     player->num_special_action_sleeping =
3826       get_num_special_action(player->artwork_element,
3827                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3828   }
3829
3830   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3831                     emulate_sb ? EMU_SOKOBAN :
3832                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3833
3834   // initialize type of slippery elements
3835   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3836   {
3837     if (!IS_CUSTOM_ELEMENT(i))
3838     {
3839       // default: elements slip down either to the left or right randomly
3840       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3841
3842       // SP style elements prefer to slip down on the left side
3843       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3844         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3845
3846       // BD style elements prefer to slip down on the left side
3847       if (game.emulation == EMU_BOULDERDASH)
3848         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3849     }
3850   }
3851
3852   // initialize explosion and ignition delay
3853   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3854   {
3855     if (!IS_CUSTOM_ELEMENT(i))
3856     {
3857       int num_phase = 8;
3858       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3859                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3860                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3861       int last_phase = (num_phase + 1) * delay;
3862       int half_phase = (num_phase / 2) * delay;
3863
3864       element_info[i].explosion_delay = last_phase - 1;
3865       element_info[i].ignition_delay = half_phase;
3866
3867       if (i == EL_BLACK_ORB)
3868         element_info[i].ignition_delay = 1;
3869     }
3870   }
3871
3872   // correct non-moving belts to start moving left
3873   for (i = 0; i < NUM_BELTS; i++)
3874     if (game.belt_dir[i] == MV_NONE)
3875       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3876
3877 #if USE_NEW_PLAYER_ASSIGNMENTS
3878   // use preferred player also in local single-player mode
3879   if (!network.enabled && !game.team_mode)
3880   {
3881     int new_index_nr = setup.network_player_nr;
3882
3883     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3884     {
3885       for (i = 0; i < MAX_PLAYERS; i++)
3886         stored_player[i].connected_locally = FALSE;
3887
3888       stored_player[new_index_nr].connected_locally = TRUE;
3889     }
3890   }
3891
3892   for (i = 0; i < MAX_PLAYERS; i++)
3893   {
3894     stored_player[i].connected = FALSE;
3895
3896     // in network game mode, the local player might not be the first player
3897     if (stored_player[i].connected_locally)
3898       local_player = &stored_player[i];
3899   }
3900
3901   if (!network.enabled)
3902     local_player->connected = TRUE;
3903
3904   if (tape.playing)
3905   {
3906     for (i = 0; i < MAX_PLAYERS; i++)
3907       stored_player[i].connected = tape.player_participates[i];
3908   }
3909   else if (network.enabled)
3910   {
3911     // add team mode players connected over the network (needed for correct
3912     // assignment of player figures from level to locally playing players)
3913
3914     for (i = 0; i < MAX_PLAYERS; i++)
3915       if (stored_player[i].connected_network)
3916         stored_player[i].connected = TRUE;
3917   }
3918   else if (game.team_mode)
3919   {
3920     // try to guess locally connected team mode players (needed for correct
3921     // assignment of player figures from level to locally playing players)
3922
3923     for (i = 0; i < MAX_PLAYERS; i++)
3924       if (setup.input[i].use_joystick ||
3925           setup.input[i].key.left != KSYM_UNDEFINED)
3926         stored_player[i].connected = TRUE;
3927   }
3928
3929 #if DEBUG_INIT_PLAYER
3930   DebugPrintPlayerStatus("Player status after level initialization");
3931 #endif
3932
3933 #if DEBUG_INIT_PLAYER
3934   if (options.debug)
3935     printf("Reassigning players ...\n");
3936 #endif
3937
3938   // check if any connected player was not found in playfield
3939   for (i = 0; i < MAX_PLAYERS; i++)
3940   {
3941     struct PlayerInfo *player = &stored_player[i];
3942
3943     if (player->connected && !player->present)
3944     {
3945       struct PlayerInfo *field_player = NULL;
3946
3947 #if DEBUG_INIT_PLAYER
3948       if (options.debug)
3949         printf("- looking for field player for player %d ...\n", i + 1);
3950 #endif
3951
3952       // assign first free player found that is present in the playfield
3953
3954       // first try: look for unmapped playfield player that is not connected
3955       for (j = 0; j < MAX_PLAYERS; j++)
3956         if (field_player == NULL &&
3957             stored_player[j].present &&
3958             !stored_player[j].mapped &&
3959             !stored_player[j].connected)
3960           field_player = &stored_player[j];
3961
3962       // second try: look for *any* unmapped playfield player
3963       for (j = 0; j < MAX_PLAYERS; j++)
3964         if (field_player == NULL &&
3965             stored_player[j].present &&
3966             !stored_player[j].mapped)
3967           field_player = &stored_player[j];
3968
3969       if (field_player != NULL)
3970       {
3971         int jx = field_player->jx, jy = field_player->jy;
3972
3973 #if DEBUG_INIT_PLAYER
3974         if (options.debug)
3975           printf("- found player %d\n", field_player->index_nr + 1);
3976 #endif
3977
3978         player->present = FALSE;
3979         player->active = FALSE;
3980
3981         field_player->present = TRUE;
3982         field_player->active = TRUE;
3983
3984         /*
3985         player->initial_element = field_player->initial_element;
3986         player->artwork_element = field_player->artwork_element;
3987
3988         player->block_last_field       = field_player->block_last_field;
3989         player->block_delay_adjustment = field_player->block_delay_adjustment;
3990         */
3991
3992         StorePlayer[jx][jy] = field_player->element_nr;
3993
3994         field_player->jx = field_player->last_jx = jx;
3995         field_player->jy = field_player->last_jy = jy;
3996
3997         if (local_player == player)
3998           local_player = field_player;
3999
4000         map_player_action[field_player->index_nr] = i;
4001
4002         field_player->mapped = TRUE;
4003
4004 #if DEBUG_INIT_PLAYER
4005         if (options.debug)
4006           printf("- map_player_action[%d] == %d\n",
4007                  field_player->index_nr + 1, i + 1);
4008 #endif
4009       }
4010     }
4011
4012     if (player->connected && player->present)
4013       player->mapped = TRUE;
4014   }
4015
4016 #if DEBUG_INIT_PLAYER
4017   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4018 #endif
4019
4020 #else
4021
4022   // check if any connected player was not found in playfield
4023   for (i = 0; i < MAX_PLAYERS; i++)
4024   {
4025     struct PlayerInfo *player = &stored_player[i];
4026
4027     if (player->connected && !player->present)
4028     {
4029       for (j = 0; j < MAX_PLAYERS; j++)
4030       {
4031         struct PlayerInfo *field_player = &stored_player[j];
4032         int jx = field_player->jx, jy = field_player->jy;
4033
4034         // assign first free player found that is present in the playfield
4035         if (field_player->present && !field_player->connected)
4036         {
4037           player->present = TRUE;
4038           player->active = TRUE;
4039
4040           field_player->present = FALSE;
4041           field_player->active = FALSE;
4042
4043           player->initial_element = field_player->initial_element;
4044           player->artwork_element = field_player->artwork_element;
4045
4046           player->block_last_field       = field_player->block_last_field;
4047           player->block_delay_adjustment = field_player->block_delay_adjustment;
4048
4049           StorePlayer[jx][jy] = player->element_nr;
4050
4051           player->jx = player->last_jx = jx;
4052           player->jy = player->last_jy = jy;
4053
4054           break;
4055         }
4056       }
4057     }
4058   }
4059 #endif
4060
4061 #if 0
4062   printf("::: local_player->present == %d\n", local_player->present);
4063 #endif
4064
4065   // set focus to local player for network games, else to all players
4066   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4067   game.centered_player_nr_next = game.centered_player_nr;
4068   game.set_centered_player = FALSE;
4069   game.set_centered_player_wrap = FALSE;
4070
4071   if (network_playing && tape.recording)
4072   {
4073     // store client dependent player focus when recording network games
4074     tape.centered_player_nr_next = game.centered_player_nr_next;
4075     tape.set_centered_player = TRUE;
4076   }
4077
4078   if (tape.playing)
4079   {
4080     // when playing a tape, eliminate all players who do not participate
4081
4082 #if USE_NEW_PLAYER_ASSIGNMENTS
4083
4084     if (!game.team_mode)
4085     {
4086       for (i = 0; i < MAX_PLAYERS; i++)
4087       {
4088         if (stored_player[i].active &&
4089             !tape.player_participates[map_player_action[i]])
4090         {
4091           struct PlayerInfo *player = &stored_player[i];
4092           int jx = player->jx, jy = player->jy;
4093
4094 #if DEBUG_INIT_PLAYER
4095           if (options.debug)
4096             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4097 #endif
4098
4099           player->active = FALSE;
4100           StorePlayer[jx][jy] = 0;
4101           Feld[jx][jy] = EL_EMPTY;
4102         }
4103       }
4104     }
4105
4106 #else
4107
4108     for (i = 0; i < MAX_PLAYERS; i++)
4109     {
4110       if (stored_player[i].active &&
4111           !tape.player_participates[i])
4112       {
4113         struct PlayerInfo *player = &stored_player[i];
4114         int jx = player->jx, jy = player->jy;
4115
4116         player->active = FALSE;
4117         StorePlayer[jx][jy] = 0;
4118         Feld[jx][jy] = EL_EMPTY;
4119       }
4120     }
4121 #endif
4122   }
4123   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4124   {
4125     // when in single player mode, eliminate all but the local player
4126
4127     for (i = 0; i < MAX_PLAYERS; i++)
4128     {
4129       struct PlayerInfo *player = &stored_player[i];
4130
4131       if (player->active && player != local_player)
4132       {
4133         int jx = player->jx, jy = player->jy;
4134
4135         player->active = FALSE;
4136         player->present = FALSE;
4137
4138         StorePlayer[jx][jy] = 0;
4139         Feld[jx][jy] = EL_EMPTY;
4140       }
4141     }
4142   }
4143
4144   for (i = 0; i < MAX_PLAYERS; i++)
4145     if (stored_player[i].active)
4146       game.players_still_needed++;
4147
4148   if (level.solved_by_one_player)
4149     game.players_still_needed = 1;
4150
4151   // when recording the game, store which players take part in the game
4152   if (tape.recording)
4153   {
4154 #if USE_NEW_PLAYER_ASSIGNMENTS
4155     for (i = 0; i < MAX_PLAYERS; i++)
4156       if (stored_player[i].connected)
4157         tape.player_participates[i] = TRUE;
4158 #else
4159     for (i = 0; i < MAX_PLAYERS; i++)
4160       if (stored_player[i].active)
4161         tape.player_participates[i] = TRUE;
4162 #endif
4163   }
4164
4165 #if DEBUG_INIT_PLAYER
4166   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4167 #endif
4168
4169   if (BorderElement == EL_EMPTY)
4170   {
4171     SBX_Left = 0;
4172     SBX_Right = lev_fieldx - SCR_FIELDX;
4173     SBY_Upper = 0;
4174     SBY_Lower = lev_fieldy - SCR_FIELDY;
4175   }
4176   else
4177   {
4178     SBX_Left = -1;
4179     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4180     SBY_Upper = -1;
4181     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4182   }
4183
4184   if (full_lev_fieldx <= SCR_FIELDX)
4185     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4186   if (full_lev_fieldy <= SCR_FIELDY)
4187     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4188
4189   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4190     SBX_Left--;
4191   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4192     SBY_Upper--;
4193
4194   // if local player not found, look for custom element that might create
4195   // the player (make some assumptions about the right custom element)
4196   if (!local_player->present)
4197   {
4198     int start_x = 0, start_y = 0;
4199     int found_rating = 0;
4200     int found_element = EL_UNDEFINED;
4201     int player_nr = local_player->index_nr;
4202
4203     SCAN_PLAYFIELD(x, y)
4204     {
4205       int element = Feld[x][y];
4206       int content;
4207       int xx, yy;
4208       boolean is_player;
4209
4210       if (level.use_start_element[player_nr] &&
4211           level.start_element[player_nr] == element &&
4212           found_rating < 4)
4213       {
4214         start_x = x;
4215         start_y = y;
4216
4217         found_rating = 4;
4218         found_element = element;
4219       }
4220
4221       if (!IS_CUSTOM_ELEMENT(element))
4222         continue;
4223
4224       if (CAN_CHANGE(element))
4225       {
4226         for (i = 0; i < element_info[element].num_change_pages; i++)
4227         {
4228           // check for player created from custom element as single target
4229           content = element_info[element].change_page[i].target_element;
4230           is_player = ELEM_IS_PLAYER(content);
4231
4232           if (is_player && (found_rating < 3 ||
4233                             (found_rating == 3 && element < found_element)))
4234           {
4235             start_x = x;
4236             start_y = y;
4237
4238             found_rating = 3;
4239             found_element = element;
4240           }
4241         }
4242       }
4243
4244       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4245       {
4246         // check for player created from custom element as explosion content
4247         content = element_info[element].content.e[xx][yy];
4248         is_player = ELEM_IS_PLAYER(content);
4249
4250         if (is_player && (found_rating < 2 ||
4251                           (found_rating == 2 && element < found_element)))
4252         {
4253           start_x = x + xx - 1;
4254           start_y = y + yy - 1;
4255
4256           found_rating = 2;
4257           found_element = element;
4258         }
4259
4260         if (!CAN_CHANGE(element))
4261           continue;
4262
4263         for (i = 0; i < element_info[element].num_change_pages; i++)
4264         {
4265           // check for player created from custom element as extended target
4266           content =
4267             element_info[element].change_page[i].target_content.e[xx][yy];
4268
4269           is_player = ELEM_IS_PLAYER(content);
4270
4271           if (is_player && (found_rating < 1 ||
4272                             (found_rating == 1 && element < found_element)))
4273           {
4274             start_x = x + xx - 1;
4275             start_y = y + yy - 1;
4276
4277             found_rating = 1;
4278             found_element = element;
4279           }
4280         }
4281       }
4282     }
4283
4284     scroll_x = SCROLL_POSITION_X(start_x);
4285     scroll_y = SCROLL_POSITION_Y(start_y);
4286   }
4287   else
4288   {
4289     scroll_x = SCROLL_POSITION_X(local_player->jx);
4290     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4291   }
4292
4293   // !!! FIX THIS (START) !!!
4294   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4295   {
4296     InitGameEngine_EM();
4297   }
4298   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4299   {
4300     InitGameEngine_SP();
4301   }
4302   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4303   {
4304     InitGameEngine_MM();
4305   }
4306   else
4307   {
4308     DrawLevel(REDRAW_FIELD);
4309     DrawAllPlayers();
4310
4311     // after drawing the level, correct some elements
4312     if (game.timegate_time_left == 0)
4313       CloseAllOpenTimegates();
4314   }
4315
4316   // blit playfield from scroll buffer to normal back buffer for fading in
4317   BlitScreenToBitmap(backbuffer);
4318   // !!! FIX THIS (END) !!!
4319
4320   DrawMaskedBorder(fade_mask);
4321
4322   FadeIn(fade_mask);
4323
4324 #if 1
4325   // full screen redraw is required at this point in the following cases:
4326   // - special editor door undrawn when game was started from level editor
4327   // - drawing area (playfield) was changed and has to be removed completely
4328   redraw_mask = REDRAW_ALL;
4329   BackToFront();
4330 #endif
4331
4332   if (!game.restart_level)
4333   {
4334     // copy default game door content to main double buffer
4335
4336     // !!! CHECK AGAIN !!!
4337     SetPanelBackground();
4338     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4339     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4340   }
4341
4342   SetPanelBackground();
4343   SetDrawBackgroundMask(REDRAW_DOOR_1);
4344
4345   UpdateAndDisplayGameControlValues();
4346
4347   if (!game.restart_level)
4348   {
4349     UnmapGameButtons();
4350     UnmapTapeButtons();
4351
4352     FreeGameButtons();
4353     CreateGameButtons();
4354
4355     MapGameButtons();
4356     MapTapeButtons();
4357
4358     // copy actual game door content to door double buffer for OpenDoor()
4359     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4360
4361     OpenDoor(DOOR_OPEN_ALL);
4362
4363     KeyboardAutoRepeatOffUnlessAutoplay();
4364
4365 #if DEBUG_INIT_PLAYER
4366     DebugPrintPlayerStatus("Player status (final)");
4367 #endif
4368   }
4369
4370   UnmapAllGadgets();
4371
4372   MapGameButtons();
4373   MapTapeButtons();
4374
4375   if (!game.restart_level && !tape.playing)
4376   {
4377     LevelStats_incPlayed(level_nr);
4378
4379     SaveLevelSetup_SeriesInfo();
4380   }
4381
4382   game.restart_level = FALSE;
4383   game.restart_game_message = NULL;
4384   game.request_active = FALSE;
4385
4386   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4387     InitGameActions_MM();
4388
4389   SaveEngineSnapshotToListInitial();
4390
4391   if (!game.restart_level)
4392   {
4393     PlaySound(SND_GAME_STARTING);
4394
4395     if (setup.sound_music)
4396       PlayLevelMusic();
4397   }
4398 }
4399
4400 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4401                         int actual_player_x, int actual_player_y)
4402 {
4403   // this is used for non-R'n'D game engines to update certain engine values
4404
4405   // needed to determine if sounds are played within the visible screen area
4406   scroll_x = actual_scroll_x;
4407   scroll_y = actual_scroll_y;
4408
4409   // needed to get player position for "follow finger" playing input method
4410   local_player->jx = actual_player_x;
4411   local_player->jy = actual_player_y;
4412 }
4413
4414 void InitMovDir(int x, int y)
4415 {
4416   int i, element = Feld[x][y];
4417   static int xy[4][2] =
4418   {
4419     {  0, +1 },
4420     { +1,  0 },
4421     {  0, -1 },
4422     { -1,  0 }
4423   };
4424   static int direction[3][4] =
4425   {
4426     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4427     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4428     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4429   };
4430
4431   switch (element)
4432   {
4433     case EL_BUG_RIGHT:
4434     case EL_BUG_UP:
4435     case EL_BUG_LEFT:
4436     case EL_BUG_DOWN:
4437       Feld[x][y] = EL_BUG;
4438       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4439       break;
4440
4441     case EL_SPACESHIP_RIGHT:
4442     case EL_SPACESHIP_UP:
4443     case EL_SPACESHIP_LEFT:
4444     case EL_SPACESHIP_DOWN:
4445       Feld[x][y] = EL_SPACESHIP;
4446       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4447       break;
4448
4449     case EL_BD_BUTTERFLY_RIGHT:
4450     case EL_BD_BUTTERFLY_UP:
4451     case EL_BD_BUTTERFLY_LEFT:
4452     case EL_BD_BUTTERFLY_DOWN:
4453       Feld[x][y] = EL_BD_BUTTERFLY;
4454       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4455       break;
4456
4457     case EL_BD_FIREFLY_RIGHT:
4458     case EL_BD_FIREFLY_UP:
4459     case EL_BD_FIREFLY_LEFT:
4460     case EL_BD_FIREFLY_DOWN:
4461       Feld[x][y] = EL_BD_FIREFLY;
4462       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4463       break;
4464
4465     case EL_PACMAN_RIGHT:
4466     case EL_PACMAN_UP:
4467     case EL_PACMAN_LEFT:
4468     case EL_PACMAN_DOWN:
4469       Feld[x][y] = EL_PACMAN;
4470       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4471       break;
4472
4473     case EL_YAMYAM_LEFT:
4474     case EL_YAMYAM_RIGHT:
4475     case EL_YAMYAM_UP:
4476     case EL_YAMYAM_DOWN:
4477       Feld[x][y] = EL_YAMYAM;
4478       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4479       break;
4480
4481     case EL_SP_SNIKSNAK:
4482       MovDir[x][y] = MV_UP;
4483       break;
4484
4485     case EL_SP_ELECTRON:
4486       MovDir[x][y] = MV_LEFT;
4487       break;
4488
4489     case EL_MOLE_LEFT:
4490     case EL_MOLE_RIGHT:
4491     case EL_MOLE_UP:
4492     case EL_MOLE_DOWN:
4493       Feld[x][y] = EL_MOLE;
4494       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4495       break;
4496
4497     case EL_SPRING_LEFT:
4498     case EL_SPRING_RIGHT:
4499       Feld[x][y] = EL_SPRING;
4500       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4501       break;
4502
4503     default:
4504       if (IS_CUSTOM_ELEMENT(element))
4505       {
4506         struct ElementInfo *ei = &element_info[element];
4507         int move_direction_initial = ei->move_direction_initial;
4508         int move_pattern = ei->move_pattern;
4509
4510         if (move_direction_initial == MV_START_PREVIOUS)
4511         {
4512           if (MovDir[x][y] != MV_NONE)
4513             return;
4514
4515           move_direction_initial = MV_START_AUTOMATIC;
4516         }
4517
4518         if (move_direction_initial == MV_START_RANDOM)
4519           MovDir[x][y] = 1 << RND(4);
4520         else if (move_direction_initial & MV_ANY_DIRECTION)
4521           MovDir[x][y] = move_direction_initial;
4522         else if (move_pattern == MV_ALL_DIRECTIONS ||
4523                  move_pattern == MV_TURNING_LEFT ||
4524                  move_pattern == MV_TURNING_RIGHT ||
4525                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4526                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4527                  move_pattern == MV_TURNING_RANDOM)
4528           MovDir[x][y] = 1 << RND(4);
4529         else if (move_pattern == MV_HORIZONTAL)
4530           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4531         else if (move_pattern == MV_VERTICAL)
4532           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4533         else if (move_pattern & MV_ANY_DIRECTION)
4534           MovDir[x][y] = element_info[element].move_pattern;
4535         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4536                  move_pattern == MV_ALONG_RIGHT_SIDE)
4537         {
4538           // use random direction as default start direction
4539           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4540             MovDir[x][y] = 1 << RND(4);
4541
4542           for (i = 0; i < NUM_DIRECTIONS; i++)
4543           {
4544             int x1 = x + xy[i][0];
4545             int y1 = y + xy[i][1];
4546
4547             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4548             {
4549               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4550                 MovDir[x][y] = direction[0][i];
4551               else
4552                 MovDir[x][y] = direction[1][i];
4553
4554               break;
4555             }
4556           }
4557         }                
4558       }
4559       else
4560       {
4561         MovDir[x][y] = 1 << RND(4);
4562
4563         if (element != EL_BUG &&
4564             element != EL_SPACESHIP &&
4565             element != EL_BD_BUTTERFLY &&
4566             element != EL_BD_FIREFLY)
4567           break;
4568
4569         for (i = 0; i < NUM_DIRECTIONS; i++)
4570         {
4571           int x1 = x + xy[i][0];
4572           int y1 = y + xy[i][1];
4573
4574           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4575           {
4576             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4577             {
4578               MovDir[x][y] = direction[0][i];
4579               break;
4580             }
4581             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4582                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4583             {
4584               MovDir[x][y] = direction[1][i];
4585               break;
4586             }
4587           }
4588         }
4589       }
4590       break;
4591   }
4592
4593   GfxDir[x][y] = MovDir[x][y];
4594 }
4595
4596 void InitAmoebaNr(int x, int y)
4597 {
4598   int i;
4599   int group_nr = AmoebeNachbarNr(x, y);
4600
4601   if (group_nr == 0)
4602   {
4603     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4604     {
4605       if (AmoebaCnt[i] == 0)
4606       {
4607         group_nr = i;
4608         break;
4609       }
4610     }
4611   }
4612
4613   AmoebaNr[x][y] = group_nr;
4614   AmoebaCnt[group_nr]++;
4615   AmoebaCnt2[group_nr]++;
4616 }
4617
4618 static void LevelSolved(void)
4619 {
4620   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4621       game.players_still_needed > 0)
4622     return;
4623
4624   game.LevelSolved = TRUE;
4625   game.GameOver = TRUE;
4626
4627   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4628                       game_em.lev->score :
4629                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4630                       game_mm.score :
4631                       game.score);
4632   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4633                        MM_HEALTH(game_mm.laser_overload_value) :
4634                        game.health);
4635
4636   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4637   game.LevelSolved_CountingScore = game.score_final;
4638   game.LevelSolved_CountingHealth = game.health_final;
4639 }
4640
4641 void GameWon(void)
4642 {
4643   static int time_count_steps;
4644   static int time, time_final;
4645   static int score, score_final;
4646   static int health, health_final;
4647   static int game_over_delay_1 = 0;
4648   static int game_over_delay_2 = 0;
4649   static int game_over_delay_3 = 0;
4650   int game_over_delay_value_1 = 50;
4651   int game_over_delay_value_2 = 25;
4652   int game_over_delay_value_3 = 50;
4653
4654   if (!game.LevelSolved_GameWon)
4655   {
4656     int i;
4657
4658     // do not start end game actions before the player stops moving (to exit)
4659     if (local_player->active && local_player->MovPos)
4660       return;
4661
4662     game.LevelSolved_GameWon = TRUE;
4663     game.LevelSolved_SaveTape = tape.recording;
4664     game.LevelSolved_SaveScore = !tape.playing;
4665
4666     if (!tape.playing)
4667     {
4668       LevelStats_incSolved(level_nr);
4669
4670       SaveLevelSetup_SeriesInfo();
4671     }
4672
4673     if (tape.auto_play)         // tape might already be stopped here
4674       tape.auto_play_level_solved = TRUE;
4675
4676     TapeStop();
4677
4678     game_over_delay_1 = 0;
4679     game_over_delay_2 = 0;
4680     game_over_delay_3 = game_over_delay_value_3;
4681
4682     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4683     score = score_final = game.score_final;
4684     health = health_final = game.health_final;
4685
4686     if (level.score[SC_TIME_BONUS] > 0)
4687     {
4688       if (TimeLeft > 0)
4689       {
4690         time_final = 0;
4691         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4692       }
4693       else if (game.no_time_limit && TimePlayed < 999)
4694       {
4695         time_final = 999;
4696         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4697       }
4698
4699       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4700
4701       game_over_delay_1 = game_over_delay_value_1;
4702
4703       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4704       {
4705         health_final = 0;
4706         score_final += health * level.score[SC_TIME_BONUS];
4707
4708         game_over_delay_2 = game_over_delay_value_2;
4709       }
4710
4711       game.score_final = score_final;
4712       game.health_final = health_final;
4713     }
4714
4715     if (level_editor_test_game)
4716     {
4717       time = time_final;
4718       score = score_final;
4719
4720       game.LevelSolved_CountingTime = time;
4721       game.LevelSolved_CountingScore = score;
4722
4723       game_panel_controls[GAME_PANEL_TIME].value = time;
4724       game_panel_controls[GAME_PANEL_SCORE].value = score;
4725
4726       DisplayGameControlValues();
4727     }
4728
4729     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4730     {
4731       // check if last player has left the level
4732       if (game.exit_x >= 0 &&
4733           game.exit_y >= 0)
4734       {
4735         int x = game.exit_x;
4736         int y = game.exit_y;
4737         int element = Feld[x][y];
4738
4739         // close exit door after last player
4740         if ((game.all_players_gone &&
4741              (element == EL_EXIT_OPEN ||
4742               element == EL_SP_EXIT_OPEN ||
4743               element == EL_STEEL_EXIT_OPEN)) ||
4744             element == EL_EM_EXIT_OPEN ||
4745             element == EL_EM_STEEL_EXIT_OPEN)
4746         {
4747
4748           Feld[x][y] =
4749             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4750              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4751              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4752              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4753              EL_EM_STEEL_EXIT_CLOSING);
4754
4755           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4756         }
4757
4758         // player disappears
4759         DrawLevelField(x, y);
4760       }
4761
4762       for (i = 0; i < MAX_PLAYERS; i++)
4763       {
4764         struct PlayerInfo *player = &stored_player[i];
4765
4766         if (player->present)
4767         {
4768           RemovePlayer(player);
4769
4770           // player disappears
4771           DrawLevelField(player->jx, player->jy);
4772         }
4773       }
4774     }
4775
4776     PlaySound(SND_GAME_WINNING);
4777   }
4778
4779   if (game_over_delay_1 > 0)
4780   {
4781     game_over_delay_1--;
4782
4783     return;
4784   }
4785
4786   if (time != time_final)
4787   {
4788     int time_to_go = ABS(time_final - time);
4789     int time_count_dir = (time < time_final ? +1 : -1);
4790
4791     if (time_to_go < time_count_steps)
4792       time_count_steps = 1;
4793
4794     time  += time_count_steps * time_count_dir;
4795     score += time_count_steps * level.score[SC_TIME_BONUS];
4796
4797     game.LevelSolved_CountingTime = time;
4798     game.LevelSolved_CountingScore = score;
4799
4800     game_panel_controls[GAME_PANEL_TIME].value = time;
4801     game_panel_controls[GAME_PANEL_SCORE].value = score;
4802
4803     DisplayGameControlValues();
4804
4805     if (time == time_final)
4806       StopSound(SND_GAME_LEVELTIME_BONUS);
4807     else if (setup.sound_loops)
4808       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4809     else
4810       PlaySound(SND_GAME_LEVELTIME_BONUS);
4811
4812     return;
4813   }
4814
4815   if (game_over_delay_2 > 0)
4816   {
4817     game_over_delay_2--;
4818
4819     return;
4820   }
4821
4822   if (health != health_final)
4823   {
4824     int health_count_dir = (health < health_final ? +1 : -1);
4825
4826     health += health_count_dir;
4827     score  += level.score[SC_TIME_BONUS];
4828
4829     game.LevelSolved_CountingHealth = health;
4830     game.LevelSolved_CountingScore = score;
4831
4832     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4833     game_panel_controls[GAME_PANEL_SCORE].value = score;
4834
4835     DisplayGameControlValues();
4836
4837     if (health == health_final)
4838       StopSound(SND_GAME_LEVELTIME_BONUS);
4839     else if (setup.sound_loops)
4840       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4841     else
4842       PlaySound(SND_GAME_LEVELTIME_BONUS);
4843
4844     return;
4845   }
4846
4847   game.panel.active = FALSE;
4848
4849   if (game_over_delay_3 > 0)
4850   {
4851     game_over_delay_3--;
4852
4853     return;
4854   }
4855
4856   GameEnd();
4857 }
4858
4859 void GameEnd(void)
4860 {
4861   // used instead of "level_nr" (needed for network games)
4862   int last_level_nr = levelset.level_nr;
4863   int hi_pos;
4864
4865   game.LevelSolved_GameEnd = TRUE;
4866
4867   if (game.LevelSolved_SaveTape)
4868   {
4869     // make sure that request dialog to save tape does not open door again
4870     if (!global.use_envelope_request)
4871       CloseDoor(DOOR_CLOSE_1);
4872
4873     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4874   }
4875
4876   // if no tape is to be saved, close both doors simultaneously
4877   CloseDoor(DOOR_CLOSE_ALL);
4878
4879   if (level_editor_test_game)
4880   {
4881     SetGameStatus(GAME_MODE_MAIN);
4882
4883     DrawMainMenu();
4884
4885     return;
4886   }
4887
4888   if (!game.LevelSolved_SaveScore)
4889   {
4890     SetGameStatus(GAME_MODE_MAIN);
4891
4892     DrawMainMenu();
4893
4894     return;
4895   }
4896
4897   if (level_nr == leveldir_current->handicap_level)
4898   {
4899     leveldir_current->handicap_level++;
4900
4901     SaveLevelSetup_SeriesInfo();
4902   }
4903
4904   if (setup.increment_levels &&
4905       level_nr < leveldir_current->last_level &&
4906       !network_playing)
4907   {
4908     level_nr++;         // advance to next level
4909     TapeErase();        // start with empty tape
4910
4911     if (setup.auto_play_next_level)
4912     {
4913       LoadLevel(level_nr);
4914
4915       SaveLevelSetup_SeriesInfo();
4916     }
4917   }
4918
4919   hi_pos = NewHiScore(last_level_nr);
4920
4921   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4922   {
4923     SetGameStatus(GAME_MODE_SCORES);
4924
4925     DrawHallOfFame(last_level_nr, hi_pos);
4926   }
4927   else if (setup.auto_play_next_level && setup.increment_levels &&
4928            last_level_nr < leveldir_current->last_level &&
4929            !network_playing)
4930   {
4931     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4932   }
4933   else
4934   {
4935     SetGameStatus(GAME_MODE_MAIN);
4936
4937     DrawMainMenu();
4938   }
4939 }
4940
4941 int NewHiScore(int level_nr)
4942 {
4943   int k, l;
4944   int position = -1;
4945   boolean one_score_entry_per_name = !program.many_scores_per_name;
4946
4947   LoadScore(level_nr);
4948
4949   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4950       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4951     return -1;
4952
4953   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4954   {
4955     if (game.score_final > highscore[k].Score)
4956     {
4957       // player has made it to the hall of fame
4958
4959       if (k < MAX_SCORE_ENTRIES - 1)
4960       {
4961         int m = MAX_SCORE_ENTRIES - 1;
4962
4963         if (one_score_entry_per_name)
4964         {
4965           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4966             if (strEqual(setup.player_name, highscore[l].Name))
4967               m = l;
4968
4969           if (m == k)   // player's new highscore overwrites his old one
4970             goto put_into_list;
4971         }
4972
4973         for (l = m; l > k; l--)
4974         {
4975           strcpy(highscore[l].Name, highscore[l - 1].Name);
4976           highscore[l].Score = highscore[l - 1].Score;
4977         }
4978       }
4979
4980       put_into_list:
4981
4982       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4983       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4984       highscore[k].Score = game.score_final;
4985       position = k;
4986
4987       break;
4988     }
4989     else if (one_score_entry_per_name &&
4990              !strncmp(setup.player_name, highscore[k].Name,
4991                       MAX_PLAYER_NAME_LEN))
4992       break;    // player already there with a higher score
4993   }
4994
4995   if (position >= 0) 
4996     SaveScore(level_nr);
4997
4998   return position;
4999 }
5000
5001 static int getElementMoveStepsizeExt(int x, int y, int direction)
5002 {
5003   int element = Feld[x][y];
5004   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5005   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5006   int horiz_move = (dx != 0);
5007   int sign = (horiz_move ? dx : dy);
5008   int step = sign * element_info[element].move_stepsize;
5009
5010   // special values for move stepsize for spring and things on conveyor belt
5011   if (horiz_move)
5012   {
5013     if (CAN_FALL(element) &&
5014         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5015       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5016     else if (element == EL_SPRING)
5017       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5018   }
5019
5020   return step;
5021 }
5022
5023 static int getElementMoveStepsize(int x, int y)
5024 {
5025   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5026 }
5027
5028 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5029 {
5030   if (player->GfxAction != action || player->GfxDir != dir)
5031   {
5032     player->GfxAction = action;
5033     player->GfxDir = dir;
5034     player->Frame = 0;
5035     player->StepFrame = 0;
5036   }
5037 }
5038
5039 static void ResetGfxFrame(int x, int y)
5040 {
5041   // profiling showed that "autotest" spends 10~20% of its time in this function
5042   if (DrawingDeactivatedField())
5043     return;
5044
5045   int element = Feld[x][y];
5046   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5047
5048   if (graphic_info[graphic].anim_global_sync)
5049     GfxFrame[x][y] = FrameCounter;
5050   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5051     GfxFrame[x][y] = CustomValue[x][y];
5052   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5053     GfxFrame[x][y] = element_info[element].collect_score;
5054   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5055     GfxFrame[x][y] = ChangeDelay[x][y];
5056 }
5057
5058 static void ResetGfxAnimation(int x, int y)
5059 {
5060   GfxAction[x][y] = ACTION_DEFAULT;
5061   GfxDir[x][y] = MovDir[x][y];
5062   GfxFrame[x][y] = 0;
5063
5064   ResetGfxFrame(x, y);
5065 }
5066
5067 static void ResetRandomAnimationValue(int x, int y)
5068 {
5069   GfxRandom[x][y] = INIT_GFX_RANDOM();
5070 }
5071
5072 static void InitMovingField(int x, int y, int direction)
5073 {
5074   int element = Feld[x][y];
5075   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5076   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5077   int newx = x + dx;
5078   int newy = y + dy;
5079   boolean is_moving_before, is_moving_after;
5080
5081   // check if element was/is moving or being moved before/after mode change
5082   is_moving_before = (WasJustMoving[x][y] != 0);
5083   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5084
5085   // reset animation only for moving elements which change direction of moving
5086   // or which just started or stopped moving
5087   // (else CEs with property "can move" / "not moving" are reset each frame)
5088   if (is_moving_before != is_moving_after ||
5089       direction != MovDir[x][y])
5090     ResetGfxAnimation(x, y);
5091
5092   MovDir[x][y] = direction;
5093   GfxDir[x][y] = direction;
5094
5095   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5096                      direction == MV_DOWN && CAN_FALL(element) ?
5097                      ACTION_FALLING : ACTION_MOVING);
5098
5099   // this is needed for CEs with property "can move" / "not moving"
5100
5101   if (is_moving_after)
5102   {
5103     if (Feld[newx][newy] == EL_EMPTY)
5104       Feld[newx][newy] = EL_BLOCKED;
5105
5106     MovDir[newx][newy] = MovDir[x][y];
5107
5108     CustomValue[newx][newy] = CustomValue[x][y];
5109
5110     GfxFrame[newx][newy] = GfxFrame[x][y];
5111     GfxRandom[newx][newy] = GfxRandom[x][y];
5112     GfxAction[newx][newy] = GfxAction[x][y];
5113     GfxDir[newx][newy] = GfxDir[x][y];
5114   }
5115 }
5116
5117 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5118 {
5119   int direction = MovDir[x][y];
5120   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5121   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5122
5123   *goes_to_x = newx;
5124   *goes_to_y = newy;
5125 }
5126
5127 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5128 {
5129   int oldx = x, oldy = y;
5130   int direction = MovDir[x][y];
5131
5132   if (direction == MV_LEFT)
5133     oldx++;
5134   else if (direction == MV_RIGHT)
5135     oldx--;
5136   else if (direction == MV_UP)
5137     oldy++;
5138   else if (direction == MV_DOWN)
5139     oldy--;
5140
5141   *comes_from_x = oldx;
5142   *comes_from_y = oldy;
5143 }
5144
5145 static int MovingOrBlocked2Element(int x, int y)
5146 {
5147   int element = Feld[x][y];
5148
5149   if (element == EL_BLOCKED)
5150   {
5151     int oldx, oldy;
5152
5153     Blocked2Moving(x, y, &oldx, &oldy);
5154     return Feld[oldx][oldy];
5155   }
5156   else
5157     return element;
5158 }
5159
5160 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5161 {
5162   // like MovingOrBlocked2Element(), but if element is moving
5163   // and (x,y) is the field the moving element is just leaving,
5164   // return EL_BLOCKED instead of the element value
5165   int element = Feld[x][y];
5166
5167   if (IS_MOVING(x, y))
5168   {
5169     if (element == EL_BLOCKED)
5170     {
5171       int oldx, oldy;
5172
5173       Blocked2Moving(x, y, &oldx, &oldy);
5174       return Feld[oldx][oldy];
5175     }
5176     else
5177       return EL_BLOCKED;
5178   }
5179   else
5180     return element;
5181 }
5182
5183 static void RemoveField(int x, int y)
5184 {
5185   Feld[x][y] = EL_EMPTY;
5186
5187   MovPos[x][y] = 0;
5188   MovDir[x][y] = 0;
5189   MovDelay[x][y] = 0;
5190
5191   CustomValue[x][y] = 0;
5192
5193   AmoebaNr[x][y] = 0;
5194   ChangeDelay[x][y] = 0;
5195   ChangePage[x][y] = -1;
5196   Pushed[x][y] = FALSE;
5197
5198   GfxElement[x][y] = EL_UNDEFINED;
5199   GfxAction[x][y] = ACTION_DEFAULT;
5200   GfxDir[x][y] = MV_NONE;
5201 }
5202
5203 static void RemoveMovingField(int x, int y)
5204 {
5205   int oldx = x, oldy = y, newx = x, newy = y;
5206   int element = Feld[x][y];
5207   int next_element = EL_UNDEFINED;
5208
5209   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5210     return;
5211
5212   if (IS_MOVING(x, y))
5213   {
5214     Moving2Blocked(x, y, &newx, &newy);
5215
5216     if (Feld[newx][newy] != EL_BLOCKED)
5217     {
5218       // element is moving, but target field is not free (blocked), but
5219       // already occupied by something different (example: acid pool);
5220       // in this case, only remove the moving field, but not the target
5221
5222       RemoveField(oldx, oldy);
5223
5224       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5225
5226       TEST_DrawLevelField(oldx, oldy);
5227
5228       return;
5229     }
5230   }
5231   else if (element == EL_BLOCKED)
5232   {
5233     Blocked2Moving(x, y, &oldx, &oldy);
5234     if (!IS_MOVING(oldx, oldy))
5235       return;
5236   }
5237
5238   if (element == EL_BLOCKED &&
5239       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5240        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5241        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5242        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5243        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5244        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5245     next_element = get_next_element(Feld[oldx][oldy]);
5246
5247   RemoveField(oldx, oldy);
5248   RemoveField(newx, newy);
5249
5250   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5251
5252   if (next_element != EL_UNDEFINED)
5253     Feld[oldx][oldy] = next_element;
5254
5255   TEST_DrawLevelField(oldx, oldy);
5256   TEST_DrawLevelField(newx, newy);
5257 }
5258
5259 void DrawDynamite(int x, int y)
5260 {
5261   int sx = SCREENX(x), sy = SCREENY(y);
5262   int graphic = el2img(Feld[x][y]);
5263   int frame;
5264
5265   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5266     return;
5267
5268   if (IS_WALKABLE_INSIDE(Back[x][y]))
5269     return;
5270
5271   if (Back[x][y])
5272     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5273   else if (Store[x][y])
5274     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5275
5276   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5277
5278   if (Back[x][y] || Store[x][y])
5279     DrawGraphicThruMask(sx, sy, graphic, frame);
5280   else
5281     DrawGraphic(sx, sy, graphic, frame);
5282 }
5283
5284 static void CheckDynamite(int x, int y)
5285 {
5286   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5287   {
5288     MovDelay[x][y]--;
5289
5290     if (MovDelay[x][y] != 0)
5291     {
5292       DrawDynamite(x, y);
5293       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5294
5295       return;
5296     }
5297   }
5298
5299   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5300
5301   Bang(x, y);
5302 }
5303
5304 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5305 {
5306   boolean num_checked_players = 0;
5307   int i;
5308
5309   for (i = 0; i < MAX_PLAYERS; i++)
5310   {
5311     if (stored_player[i].active)
5312     {
5313       int sx = stored_player[i].jx;
5314       int sy = stored_player[i].jy;
5315
5316       if (num_checked_players == 0)
5317       {
5318         *sx1 = *sx2 = sx;
5319         *sy1 = *sy2 = sy;
5320       }
5321       else
5322       {
5323         *sx1 = MIN(*sx1, sx);
5324         *sy1 = MIN(*sy1, sy);
5325         *sx2 = MAX(*sx2, sx);
5326         *sy2 = MAX(*sy2, sy);
5327       }
5328
5329       num_checked_players++;
5330     }
5331   }
5332 }
5333
5334 static boolean checkIfAllPlayersFitToScreen_RND(void)
5335 {
5336   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5337
5338   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5339
5340   return (sx2 - sx1 < SCR_FIELDX &&
5341           sy2 - sy1 < SCR_FIELDY);
5342 }
5343
5344 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5345 {
5346   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5347
5348   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5349
5350   *sx = (sx1 + sx2) / 2;
5351   *sy = (sy1 + sy2) / 2;
5352 }
5353
5354 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5355                                boolean center_screen, boolean quick_relocation)
5356 {
5357   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5358   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5359   boolean no_delay = (tape.warp_forward);
5360   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5361   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5362   int new_scroll_x, new_scroll_y;
5363
5364   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5365   {
5366     // case 1: quick relocation inside visible screen (without scrolling)
5367
5368     RedrawPlayfield();
5369
5370     return;
5371   }
5372
5373   if (!level.shifted_relocation || center_screen)
5374   {
5375     // relocation _with_ centering of screen
5376
5377     new_scroll_x = SCROLL_POSITION_X(x);
5378     new_scroll_y = SCROLL_POSITION_Y(y);
5379   }
5380   else
5381   {
5382     // relocation _without_ centering of screen
5383
5384     int center_scroll_x = SCROLL_POSITION_X(old_x);
5385     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5386     int offset_x = x + (scroll_x - center_scroll_x);
5387     int offset_y = y + (scroll_y - center_scroll_y);
5388
5389     // for new screen position, apply previous offset to center position
5390     new_scroll_x = SCROLL_POSITION_X(offset_x);
5391     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5392   }
5393
5394   if (quick_relocation)
5395   {
5396     // case 2: quick relocation (redraw without visible scrolling)
5397
5398     scroll_x = new_scroll_x;
5399     scroll_y = new_scroll_y;
5400
5401     RedrawPlayfield();
5402
5403     return;
5404   }
5405
5406   // case 3: visible relocation (with scrolling to new position)
5407
5408   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5409
5410   SetVideoFrameDelay(wait_delay_value);
5411
5412   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5413   {
5414     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5415     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5416
5417     if (dx == 0 && dy == 0)             // no scrolling needed at all
5418       break;
5419
5420     scroll_x -= dx;
5421     scroll_y -= dy;
5422
5423     // set values for horizontal/vertical screen scrolling (half tile size)
5424     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5425     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5426     int pos_x = dx * TILEX / 2;
5427     int pos_y = dy * TILEY / 2;
5428     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5429     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5430
5431     ScrollLevel(dx, dy);
5432     DrawAllPlayers();
5433
5434     // scroll in two steps of half tile size to make things smoother
5435     BlitScreenToBitmapExt_RND(window, fx, fy);
5436
5437     // scroll second step to align at full tile size
5438     BlitScreenToBitmap(window);
5439   }
5440
5441   DrawAllPlayers();
5442   BackToFront();
5443
5444   SetVideoFrameDelay(frame_delay_value_old);
5445 }
5446
5447 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5448 {
5449   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5450   int player_nr = GET_PLAYER_NR(el_player);
5451   struct PlayerInfo *player = &stored_player[player_nr];
5452   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5453   boolean no_delay = (tape.warp_forward);
5454   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5455   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5456   int old_jx = player->jx;
5457   int old_jy = player->jy;
5458   int old_element = Feld[old_jx][old_jy];
5459   int element = Feld[jx][jy];
5460   boolean player_relocated = (old_jx != jx || old_jy != jy);
5461
5462   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5463   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5464   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5465   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5466   int leave_side_horiz = move_dir_horiz;
5467   int leave_side_vert  = move_dir_vert;
5468   int enter_side = enter_side_horiz | enter_side_vert;
5469   int leave_side = leave_side_horiz | leave_side_vert;
5470
5471   if (player->buried)           // do not reanimate dead player
5472     return;
5473
5474   if (!player_relocated)        // no need to relocate the player
5475     return;
5476
5477   if (IS_PLAYER(jx, jy))        // player already placed at new position
5478   {
5479     RemoveField(jx, jy);        // temporarily remove newly placed player
5480     DrawLevelField(jx, jy);
5481   }
5482
5483   if (player->present)
5484   {
5485     while (player->MovPos)
5486     {
5487       ScrollPlayer(player, SCROLL_GO_ON);
5488       ScrollScreen(NULL, SCROLL_GO_ON);
5489
5490       AdvanceFrameAndPlayerCounters(player->index_nr);
5491
5492       DrawPlayer(player);
5493
5494       BackToFront_WithFrameDelay(wait_delay_value);
5495     }
5496
5497     DrawPlayer(player);         // needed here only to cleanup last field
5498     DrawLevelField(player->jx, player->jy);     // remove player graphic
5499
5500     player->is_moving = FALSE;
5501   }
5502
5503   if (IS_CUSTOM_ELEMENT(old_element))
5504     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5505                                CE_LEFT_BY_PLAYER,
5506                                player->index_bit, leave_side);
5507
5508   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5509                                       CE_PLAYER_LEAVES_X,
5510                                       player->index_bit, leave_side);
5511
5512   Feld[jx][jy] = el_player;
5513   InitPlayerField(jx, jy, el_player, TRUE);
5514
5515   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5516      possible that the relocation target field did not contain a player element,
5517      but a walkable element, to which the new player was relocated -- in this
5518      case, restore that (already initialized!) element on the player field */
5519   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5520   {
5521     Feld[jx][jy] = element;     // restore previously existing element
5522   }
5523
5524   // only visually relocate centered player
5525   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5526                      FALSE, level.instant_relocation);
5527
5528   TestIfPlayerTouchesBadThing(jx, jy);
5529   TestIfPlayerTouchesCustomElement(jx, jy);
5530
5531   if (IS_CUSTOM_ELEMENT(element))
5532     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5533                                player->index_bit, enter_side);
5534
5535   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5536                                       player->index_bit, enter_side);
5537
5538   if (player->is_switching)
5539   {
5540     /* ensure that relocation while still switching an element does not cause
5541        a new element to be treated as also switched directly after relocation
5542        (this is important for teleporter switches that teleport the player to
5543        a place where another teleporter switch is in the same direction, which
5544        would then incorrectly be treated as immediately switched before the
5545        direction key that caused the switch was released) */
5546
5547     player->switch_x += jx - old_jx;
5548     player->switch_y += jy - old_jy;
5549   }
5550 }
5551
5552 static void Explode(int ex, int ey, int phase, int mode)
5553 {
5554   int x, y;
5555   int last_phase;
5556   int border_element;
5557
5558   // !!! eliminate this variable !!!
5559   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5560
5561   if (game.explosions_delayed)
5562   {
5563     ExplodeField[ex][ey] = mode;
5564     return;
5565   }
5566
5567   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5568   {
5569     int center_element = Feld[ex][ey];
5570     int artwork_element, explosion_element;     // set these values later
5571
5572     // remove things displayed in background while burning dynamite
5573     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5574       Back[ex][ey] = 0;
5575
5576     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5577     {
5578       // put moving element to center field (and let it explode there)
5579       center_element = MovingOrBlocked2Element(ex, ey);
5580       RemoveMovingField(ex, ey);
5581       Feld[ex][ey] = center_element;
5582     }
5583
5584     // now "center_element" is finally determined -- set related values now
5585     artwork_element = center_element;           // for custom player artwork
5586     explosion_element = center_element;         // for custom player artwork
5587
5588     if (IS_PLAYER(ex, ey))
5589     {
5590       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5591
5592       artwork_element = stored_player[player_nr].artwork_element;
5593
5594       if (level.use_explosion_element[player_nr])
5595       {
5596         explosion_element = level.explosion_element[player_nr];
5597         artwork_element = explosion_element;
5598       }
5599     }
5600
5601     if (mode == EX_TYPE_NORMAL ||
5602         mode == EX_TYPE_CENTER ||
5603         mode == EX_TYPE_CROSS)
5604       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5605
5606     last_phase = element_info[explosion_element].explosion_delay + 1;
5607
5608     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5609     {
5610       int xx = x - ex + 1;
5611       int yy = y - ey + 1;
5612       int element;
5613
5614       if (!IN_LEV_FIELD(x, y) ||
5615           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5616           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5617         continue;
5618
5619       element = Feld[x][y];
5620
5621       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5622       {
5623         element = MovingOrBlocked2Element(x, y);
5624
5625         if (!IS_EXPLOSION_PROOF(element))
5626           RemoveMovingField(x, y);
5627       }
5628
5629       // indestructible elements can only explode in center (but not flames)
5630       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5631                                            mode == EX_TYPE_BORDER)) ||
5632           element == EL_FLAMES)
5633         continue;
5634
5635       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5636          behaviour, for example when touching a yamyam that explodes to rocks
5637          with active deadly shield, a rock is created under the player !!! */
5638       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5639 #if 0
5640       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5641           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5642            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5643 #else
5644       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5645 #endif
5646       {
5647         if (IS_ACTIVE_BOMB(element))
5648         {
5649           // re-activate things under the bomb like gate or penguin
5650           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5651           Back[x][y] = 0;
5652         }
5653
5654         continue;
5655       }
5656
5657       // save walkable background elements while explosion on same tile
5658       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5659           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5660         Back[x][y] = element;
5661
5662       // ignite explodable elements reached by other explosion
5663       if (element == EL_EXPLOSION)
5664         element = Store2[x][y];
5665
5666       if (AmoebaNr[x][y] &&
5667           (element == EL_AMOEBA_FULL ||
5668            element == EL_BD_AMOEBA ||
5669            element == EL_AMOEBA_GROWING))
5670       {
5671         AmoebaCnt[AmoebaNr[x][y]]--;
5672         AmoebaCnt2[AmoebaNr[x][y]]--;
5673       }
5674
5675       RemoveField(x, y);
5676
5677       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5678       {
5679         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5680
5681         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5682
5683         if (PLAYERINFO(ex, ey)->use_murphy)
5684           Store[x][y] = EL_EMPTY;
5685       }
5686
5687       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5688       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5689       else if (ELEM_IS_PLAYER(center_element))
5690         Store[x][y] = EL_EMPTY;
5691       else if (center_element == EL_YAMYAM)
5692         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5693       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5694         Store[x][y] = element_info[center_element].content.e[xx][yy];
5695 #if 1
5696       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5697       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5698       // otherwise) -- FIX THIS !!!
5699       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5700         Store[x][y] = element_info[element].content.e[1][1];
5701 #else
5702       else if (!CAN_EXPLODE(element))
5703         Store[x][y] = element_info[element].content.e[1][1];
5704 #endif
5705       else
5706         Store[x][y] = EL_EMPTY;
5707
5708       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5709           center_element == EL_AMOEBA_TO_DIAMOND)
5710         Store2[x][y] = element;
5711
5712       Feld[x][y] = EL_EXPLOSION;
5713       GfxElement[x][y] = artwork_element;
5714
5715       ExplodePhase[x][y] = 1;
5716       ExplodeDelay[x][y] = last_phase;
5717
5718       Stop[x][y] = TRUE;
5719     }
5720
5721     if (center_element == EL_YAMYAM)
5722       game.yamyam_content_nr =
5723         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5724
5725     return;
5726   }
5727
5728   if (Stop[ex][ey])
5729     return;
5730
5731   x = ex;
5732   y = ey;
5733
5734   if (phase == 1)
5735     GfxFrame[x][y] = 0;         // restart explosion animation
5736
5737   last_phase = ExplodeDelay[x][y];
5738
5739   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5740
5741   // this can happen if the player leaves an explosion just in time
5742   if (GfxElement[x][y] == EL_UNDEFINED)
5743     GfxElement[x][y] = EL_EMPTY;
5744
5745   border_element = Store2[x][y];
5746   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5747     border_element = StorePlayer[x][y];
5748
5749   if (phase == element_info[border_element].ignition_delay ||
5750       phase == last_phase)
5751   {
5752     boolean border_explosion = FALSE;
5753
5754     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5755         !PLAYER_EXPLOSION_PROTECTED(x, y))
5756     {
5757       KillPlayerUnlessExplosionProtected(x, y);
5758       border_explosion = TRUE;
5759     }
5760     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5761     {
5762       Feld[x][y] = Store2[x][y];
5763       Store2[x][y] = 0;
5764       Bang(x, y);
5765       border_explosion = TRUE;
5766     }
5767     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5768     {
5769       AmoebeUmwandeln(x, y);
5770       Store2[x][y] = 0;
5771       border_explosion = TRUE;
5772     }
5773
5774     // if an element just explodes due to another explosion (chain-reaction),
5775     // do not immediately end the new explosion when it was the last frame of
5776     // the explosion (as it would be done in the following "if"-statement!)
5777     if (border_explosion && phase == last_phase)
5778       return;
5779   }
5780
5781   if (phase == last_phase)
5782   {
5783     int element;
5784
5785     element = Feld[x][y] = Store[x][y];
5786     Store[x][y] = Store2[x][y] = 0;
5787     GfxElement[x][y] = EL_UNDEFINED;
5788
5789     // player can escape from explosions and might therefore be still alive
5790     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5791         element <= EL_PLAYER_IS_EXPLODING_4)
5792     {
5793       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5794       int explosion_element = EL_PLAYER_1 + player_nr;
5795       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5796       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5797
5798       if (level.use_explosion_element[player_nr])
5799         explosion_element = level.explosion_element[player_nr];
5800
5801       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5802                     element_info[explosion_element].content.e[xx][yy]);
5803     }
5804
5805     // restore probably existing indestructible background element
5806     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5807       element = Feld[x][y] = Back[x][y];
5808     Back[x][y] = 0;
5809
5810     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5811     GfxDir[x][y] = MV_NONE;
5812     ChangeDelay[x][y] = 0;
5813     ChangePage[x][y] = -1;
5814
5815     CustomValue[x][y] = 0;
5816
5817     InitField_WithBug2(x, y, FALSE);
5818
5819     TEST_DrawLevelField(x, y);
5820
5821     TestIfElementTouchesCustomElement(x, y);
5822
5823     if (GFX_CRUMBLED(element))
5824       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5825
5826     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5827       StorePlayer[x][y] = 0;
5828
5829     if (ELEM_IS_PLAYER(element))
5830       RelocatePlayer(x, y, element);
5831   }
5832   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5833   {
5834     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5835     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5836
5837     if (phase == delay)
5838       TEST_DrawLevelFieldCrumbled(x, y);
5839
5840     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5841     {
5842       DrawLevelElement(x, y, Back[x][y]);
5843       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5844     }
5845     else if (IS_WALKABLE_UNDER(Back[x][y]))
5846     {
5847       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5848       DrawLevelElementThruMask(x, y, Back[x][y]);
5849     }
5850     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5851       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5852   }
5853 }
5854
5855 static void DynaExplode(int ex, int ey)
5856 {
5857   int i, j;
5858   int dynabomb_element = Feld[ex][ey];
5859   int dynabomb_size = 1;
5860   boolean dynabomb_xl = FALSE;
5861   struct PlayerInfo *player;
5862   static int xy[4][2] =
5863   {
5864     { 0, -1 },
5865     { -1, 0 },
5866     { +1, 0 },
5867     { 0, +1 }
5868   };
5869
5870   if (IS_ACTIVE_BOMB(dynabomb_element))
5871   {
5872     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5873     dynabomb_size = player->dynabomb_size;
5874     dynabomb_xl = player->dynabomb_xl;
5875     player->dynabombs_left++;
5876   }
5877
5878   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5879
5880   for (i = 0; i < NUM_DIRECTIONS; i++)
5881   {
5882     for (j = 1; j <= dynabomb_size; j++)
5883     {
5884       int x = ex + j * xy[i][0];
5885       int y = ey + j * xy[i][1];
5886       int element;
5887
5888       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5889         break;
5890
5891       element = Feld[x][y];
5892
5893       // do not restart explosions of fields with active bombs
5894       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5895         continue;
5896
5897       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5898
5899       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5900           !IS_DIGGABLE(element) && !dynabomb_xl)
5901         break;
5902     }
5903   }
5904 }
5905
5906 void Bang(int x, int y)
5907 {
5908   int element = MovingOrBlocked2Element(x, y);
5909   int explosion_type = EX_TYPE_NORMAL;
5910
5911   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5912   {
5913     struct PlayerInfo *player = PLAYERINFO(x, y);
5914
5915     element = Feld[x][y] = player->initial_element;
5916
5917     if (level.use_explosion_element[player->index_nr])
5918     {
5919       int explosion_element = level.explosion_element[player->index_nr];
5920
5921       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5922         explosion_type = EX_TYPE_CROSS;
5923       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5924         explosion_type = EX_TYPE_CENTER;
5925     }
5926   }
5927
5928   switch (element)
5929   {
5930     case EL_BUG:
5931     case EL_SPACESHIP:
5932     case EL_BD_BUTTERFLY:
5933     case EL_BD_FIREFLY:
5934     case EL_YAMYAM:
5935     case EL_DARK_YAMYAM:
5936     case EL_ROBOT:
5937     case EL_PACMAN:
5938     case EL_MOLE:
5939       RaiseScoreElement(element);
5940       break;
5941
5942     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5943     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5944     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5945     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5946     case EL_DYNABOMB_INCREASE_NUMBER:
5947     case EL_DYNABOMB_INCREASE_SIZE:
5948     case EL_DYNABOMB_INCREASE_POWER:
5949       explosion_type = EX_TYPE_DYNA;
5950       break;
5951
5952     case EL_DC_LANDMINE:
5953       explosion_type = EX_TYPE_CENTER;
5954       break;
5955
5956     case EL_PENGUIN:
5957     case EL_LAMP:
5958     case EL_LAMP_ACTIVE:
5959     case EL_AMOEBA_TO_DIAMOND:
5960       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5961         explosion_type = EX_TYPE_CENTER;
5962       break;
5963
5964     default:
5965       if (element_info[element].explosion_type == EXPLODES_CROSS)
5966         explosion_type = EX_TYPE_CROSS;
5967       else if (element_info[element].explosion_type == EXPLODES_1X1)
5968         explosion_type = EX_TYPE_CENTER;
5969       break;
5970   }
5971
5972   if (explosion_type == EX_TYPE_DYNA)
5973     DynaExplode(x, y);
5974   else
5975     Explode(x, y, EX_PHASE_START, explosion_type);
5976
5977   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5978 }
5979
5980 static void SplashAcid(int x, int y)
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_LEFT;
5986
5987   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5988       (!IN_LEV_FIELD(x + 1, y - 2) ||
5989        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5990     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5991
5992   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5993 }
5994
5995 static void InitBeltMovement(void)
5996 {
5997   static int belt_base_element[4] =
5998   {
5999     EL_CONVEYOR_BELT_1_LEFT,
6000     EL_CONVEYOR_BELT_2_LEFT,
6001     EL_CONVEYOR_BELT_3_LEFT,
6002     EL_CONVEYOR_BELT_4_LEFT
6003   };
6004   static int belt_base_active_element[4] =
6005   {
6006     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6007     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6008     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6009     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6010   };
6011
6012   int x, y, i, j;
6013
6014   // set frame order for belt animation graphic according to belt direction
6015   for (i = 0; i < NUM_BELTS; i++)
6016   {
6017     int belt_nr = i;
6018
6019     for (j = 0; j < NUM_BELT_PARTS; j++)
6020     {
6021       int element = belt_base_active_element[belt_nr] + j;
6022       int graphic_1 = el2img(element);
6023       int graphic_2 = el2panelimg(element);
6024
6025       if (game.belt_dir[i] == MV_LEFT)
6026       {
6027         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6028         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6029       }
6030       else
6031       {
6032         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6033         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6034       }
6035     }
6036   }
6037
6038   SCAN_PLAYFIELD(x, y)
6039   {
6040     int element = Feld[x][y];
6041
6042     for (i = 0; i < NUM_BELTS; i++)
6043     {
6044       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6045       {
6046         int e_belt_nr = getBeltNrFromBeltElement(element);
6047         int belt_nr = i;
6048
6049         if (e_belt_nr == belt_nr)
6050         {
6051           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6052
6053           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6054         }
6055       }
6056     }
6057   }
6058 }
6059
6060 static void ToggleBeltSwitch(int x, int y)
6061 {
6062   static int belt_base_element[4] =
6063   {
6064     EL_CONVEYOR_BELT_1_LEFT,
6065     EL_CONVEYOR_BELT_2_LEFT,
6066     EL_CONVEYOR_BELT_3_LEFT,
6067     EL_CONVEYOR_BELT_4_LEFT
6068   };
6069   static int belt_base_active_element[4] =
6070   {
6071     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6072     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6073     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6074     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6075   };
6076   static int belt_base_switch_element[4] =
6077   {
6078     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6079     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6080     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6081     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6082   };
6083   static int belt_move_dir[4] =
6084   {
6085     MV_LEFT,
6086     MV_NONE,
6087     MV_RIGHT,
6088     MV_NONE,
6089   };
6090
6091   int element = Feld[x][y];
6092   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6093   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6094   int belt_dir = belt_move_dir[belt_dir_nr];
6095   int xx, yy, i;
6096
6097   if (!IS_BELT_SWITCH(element))
6098     return;
6099
6100   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6101   game.belt_dir[belt_nr] = belt_dir;
6102
6103   if (belt_dir_nr == 3)
6104     belt_dir_nr = 1;
6105
6106   // set frame order for belt animation graphic according to belt direction
6107   for (i = 0; i < NUM_BELT_PARTS; i++)
6108   {
6109     int element = belt_base_active_element[belt_nr] + i;
6110     int graphic_1 = el2img(element);
6111     int graphic_2 = el2panelimg(element);
6112
6113     if (belt_dir == MV_LEFT)
6114     {
6115       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6116       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6117     }
6118     else
6119     {
6120       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6121       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6122     }
6123   }
6124
6125   SCAN_PLAYFIELD(xx, yy)
6126   {
6127     int element = Feld[xx][yy];
6128
6129     if (IS_BELT_SWITCH(element))
6130     {
6131       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6132
6133       if (e_belt_nr == belt_nr)
6134       {
6135         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6136         TEST_DrawLevelField(xx, yy);
6137       }
6138     }
6139     else if (IS_BELT(element) && belt_dir != MV_NONE)
6140     {
6141       int e_belt_nr = getBeltNrFromBeltElement(element);
6142
6143       if (e_belt_nr == belt_nr)
6144       {
6145         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6146
6147         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6148         TEST_DrawLevelField(xx, yy);
6149       }
6150     }
6151     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6152     {
6153       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6154
6155       if (e_belt_nr == belt_nr)
6156       {
6157         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6158
6159         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6160         TEST_DrawLevelField(xx, yy);
6161       }
6162     }
6163   }
6164 }
6165
6166 static void ToggleSwitchgateSwitch(int x, int y)
6167 {
6168   int xx, yy;
6169
6170   game.switchgate_pos = !game.switchgate_pos;
6171
6172   SCAN_PLAYFIELD(xx, yy)
6173   {
6174     int element = Feld[xx][yy];
6175
6176     if (element == EL_SWITCHGATE_SWITCH_UP)
6177     {
6178       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6179       TEST_DrawLevelField(xx, yy);
6180     }
6181     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6182     {
6183       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6184       TEST_DrawLevelField(xx, yy);
6185     }
6186     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6187     {
6188       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6189       TEST_DrawLevelField(xx, yy);
6190     }
6191     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6192     {
6193       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6194       TEST_DrawLevelField(xx, yy);
6195     }
6196     else if (element == EL_SWITCHGATE_OPEN ||
6197              element == EL_SWITCHGATE_OPENING)
6198     {
6199       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6200
6201       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6202     }
6203     else if (element == EL_SWITCHGATE_CLOSED ||
6204              element == EL_SWITCHGATE_CLOSING)
6205     {
6206       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6207
6208       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6209     }
6210   }
6211 }
6212
6213 static int getInvisibleActiveFromInvisibleElement(int element)
6214 {
6215   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6216           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6217           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6218           element);
6219 }
6220
6221 static int getInvisibleFromInvisibleActiveElement(int element)
6222 {
6223   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6224           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6225           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6226           element);
6227 }
6228
6229 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6230 {
6231   int x, y;
6232
6233   SCAN_PLAYFIELD(x, y)
6234   {
6235     int element = Feld[x][y];
6236
6237     if (element == EL_LIGHT_SWITCH &&
6238         game.light_time_left > 0)
6239     {
6240       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6241       TEST_DrawLevelField(x, y);
6242     }
6243     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6244              game.light_time_left == 0)
6245     {
6246       Feld[x][y] = EL_LIGHT_SWITCH;
6247       TEST_DrawLevelField(x, y);
6248     }
6249     else if (element == EL_EMC_DRIPPER &&
6250              game.light_time_left > 0)
6251     {
6252       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6253       TEST_DrawLevelField(x, y);
6254     }
6255     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6256              game.light_time_left == 0)
6257     {
6258       Feld[x][y] = EL_EMC_DRIPPER;
6259       TEST_DrawLevelField(x, y);
6260     }
6261     else if (element == EL_INVISIBLE_STEELWALL ||
6262              element == EL_INVISIBLE_WALL ||
6263              element == EL_INVISIBLE_SAND)
6264     {
6265       if (game.light_time_left > 0)
6266         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6267
6268       TEST_DrawLevelField(x, y);
6269
6270       // uncrumble neighbour fields, if needed
6271       if (element == EL_INVISIBLE_SAND)
6272         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6273     }
6274     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6275              element == EL_INVISIBLE_WALL_ACTIVE ||
6276              element == EL_INVISIBLE_SAND_ACTIVE)
6277     {
6278       if (game.light_time_left == 0)
6279         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6280
6281       TEST_DrawLevelField(x, y);
6282
6283       // re-crumble neighbour fields, if needed
6284       if (element == EL_INVISIBLE_SAND)
6285         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6286     }
6287   }
6288 }
6289
6290 static void RedrawAllInvisibleElementsForLenses(void)
6291 {
6292   int x, y;
6293
6294   SCAN_PLAYFIELD(x, y)
6295   {
6296     int element = Feld[x][y];
6297
6298     if (element == EL_EMC_DRIPPER &&
6299         game.lenses_time_left > 0)
6300     {
6301       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6302       TEST_DrawLevelField(x, y);
6303     }
6304     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6305              game.lenses_time_left == 0)
6306     {
6307       Feld[x][y] = EL_EMC_DRIPPER;
6308       TEST_DrawLevelField(x, y);
6309     }
6310     else if (element == EL_INVISIBLE_STEELWALL ||
6311              element == EL_INVISIBLE_WALL ||
6312              element == EL_INVISIBLE_SAND)
6313     {
6314       if (game.lenses_time_left > 0)
6315         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6316
6317       TEST_DrawLevelField(x, y);
6318
6319       // uncrumble neighbour fields, if needed
6320       if (element == EL_INVISIBLE_SAND)
6321         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6322     }
6323     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6324              element == EL_INVISIBLE_WALL_ACTIVE ||
6325              element == EL_INVISIBLE_SAND_ACTIVE)
6326     {
6327       if (game.lenses_time_left == 0)
6328         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6329
6330       TEST_DrawLevelField(x, y);
6331
6332       // re-crumble neighbour fields, if needed
6333       if (element == EL_INVISIBLE_SAND)
6334         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6335     }
6336   }
6337 }
6338
6339 static void RedrawAllInvisibleElementsForMagnifier(void)
6340 {
6341   int x, y;
6342
6343   SCAN_PLAYFIELD(x, y)
6344   {
6345     int element = Feld[x][y];
6346
6347     if (element == EL_EMC_FAKE_GRASS &&
6348         game.magnify_time_left > 0)
6349     {
6350       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6351       TEST_DrawLevelField(x, y);
6352     }
6353     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6354              game.magnify_time_left == 0)
6355     {
6356       Feld[x][y] = EL_EMC_FAKE_GRASS;
6357       TEST_DrawLevelField(x, y);
6358     }
6359     else if (IS_GATE_GRAY(element) &&
6360              game.magnify_time_left > 0)
6361     {
6362       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6363                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6364                     IS_EM_GATE_GRAY(element) ?
6365                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6366                     IS_EMC_GATE_GRAY(element) ?
6367                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6368                     IS_DC_GATE_GRAY(element) ?
6369                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6370                     element);
6371       TEST_DrawLevelField(x, y);
6372     }
6373     else if (IS_GATE_GRAY_ACTIVE(element) &&
6374              game.magnify_time_left == 0)
6375     {
6376       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6377                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6378                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6379                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6380                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6381                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6382                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6383                     EL_DC_GATE_WHITE_GRAY :
6384                     element);
6385       TEST_DrawLevelField(x, y);
6386     }
6387   }
6388 }
6389
6390 static void ToggleLightSwitch(int x, int y)
6391 {
6392   int element = Feld[x][y];
6393
6394   game.light_time_left =
6395     (element == EL_LIGHT_SWITCH ?
6396      level.time_light * FRAMES_PER_SECOND : 0);
6397
6398   RedrawAllLightSwitchesAndInvisibleElements();
6399 }
6400
6401 static void ActivateTimegateSwitch(int x, int y)
6402 {
6403   int xx, yy;
6404
6405   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6406
6407   SCAN_PLAYFIELD(xx, yy)
6408   {
6409     int element = Feld[xx][yy];
6410
6411     if (element == EL_TIMEGATE_CLOSED ||
6412         element == EL_TIMEGATE_CLOSING)
6413     {
6414       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6415       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6416     }
6417
6418     /*
6419     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6420     {
6421       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6422       TEST_DrawLevelField(xx, yy);
6423     }
6424     */
6425
6426   }
6427
6428   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6429                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6430 }
6431
6432 static void Impact(int x, int y)
6433 {
6434   boolean last_line = (y == lev_fieldy - 1);
6435   boolean object_hit = FALSE;
6436   boolean impact = (last_line || object_hit);
6437   int element = Feld[x][y];
6438   int smashed = EL_STEELWALL;
6439
6440   if (!last_line)       // check if element below was hit
6441   {
6442     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6443       return;
6444
6445     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6446                                          MovDir[x][y + 1] != MV_DOWN ||
6447                                          MovPos[x][y + 1] <= TILEY / 2));
6448
6449     // do not smash moving elements that left the smashed field in time
6450     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6451         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6452       object_hit = FALSE;
6453
6454 #if USE_QUICKSAND_IMPACT_BUGFIX
6455     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6456     {
6457       RemoveMovingField(x, y + 1);
6458       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6459       Feld[x][y + 2] = EL_ROCK;
6460       TEST_DrawLevelField(x, y + 2);
6461
6462       object_hit = TRUE;
6463     }
6464
6465     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6466     {
6467       RemoveMovingField(x, y + 1);
6468       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6469       Feld[x][y + 2] = EL_ROCK;
6470       TEST_DrawLevelField(x, y + 2);
6471
6472       object_hit = TRUE;
6473     }
6474 #endif
6475
6476     if (object_hit)
6477       smashed = MovingOrBlocked2Element(x, y + 1);
6478
6479     impact = (last_line || object_hit);
6480   }
6481
6482   if (!last_line && smashed == EL_ACID) // element falls into acid
6483   {
6484     SplashAcid(x, y + 1);
6485     return;
6486   }
6487
6488   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6489   // only reset graphic animation if graphic really changes after impact
6490   if (impact &&
6491       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6492   {
6493     ResetGfxAnimation(x, y);
6494     TEST_DrawLevelField(x, y);
6495   }
6496
6497   if (impact && CAN_EXPLODE_IMPACT(element))
6498   {
6499     Bang(x, y);
6500     return;
6501   }
6502   else if (impact && element == EL_PEARL &&
6503            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6504   {
6505     ResetGfxAnimation(x, y);
6506
6507     Feld[x][y] = EL_PEARL_BREAKING;
6508     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6509     return;
6510   }
6511   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6512   {
6513     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6514
6515     return;
6516   }
6517
6518   if (impact && element == EL_AMOEBA_DROP)
6519   {
6520     if (object_hit && IS_PLAYER(x, y + 1))
6521       KillPlayerUnlessEnemyProtected(x, y + 1);
6522     else if (object_hit && smashed == EL_PENGUIN)
6523       Bang(x, y + 1);
6524     else
6525     {
6526       Feld[x][y] = EL_AMOEBA_GROWING;
6527       Store[x][y] = EL_AMOEBA_WET;
6528
6529       ResetRandomAnimationValue(x, y);
6530     }
6531     return;
6532   }
6533
6534   if (object_hit)               // check which object was hit
6535   {
6536     if ((CAN_PASS_MAGIC_WALL(element) && 
6537          (smashed == EL_MAGIC_WALL ||
6538           smashed == EL_BD_MAGIC_WALL)) ||
6539         (CAN_PASS_DC_MAGIC_WALL(element) &&
6540          smashed == EL_DC_MAGIC_WALL))
6541     {
6542       int xx, yy;
6543       int activated_magic_wall =
6544         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6545          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6546          EL_DC_MAGIC_WALL_ACTIVE);
6547
6548       // activate magic wall / mill
6549       SCAN_PLAYFIELD(xx, yy)
6550       {
6551         if (Feld[xx][yy] == smashed)
6552           Feld[xx][yy] = activated_magic_wall;
6553       }
6554
6555       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6556       game.magic_wall_active = TRUE;
6557
6558       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6559                             SND_MAGIC_WALL_ACTIVATING :
6560                             smashed == EL_BD_MAGIC_WALL ?
6561                             SND_BD_MAGIC_WALL_ACTIVATING :
6562                             SND_DC_MAGIC_WALL_ACTIVATING));
6563     }
6564
6565     if (IS_PLAYER(x, y + 1))
6566     {
6567       if (CAN_SMASH_PLAYER(element))
6568       {
6569         KillPlayerUnlessEnemyProtected(x, y + 1);
6570         return;
6571       }
6572     }
6573     else if (smashed == EL_PENGUIN)
6574     {
6575       if (CAN_SMASH_PLAYER(element))
6576       {
6577         Bang(x, y + 1);
6578         return;
6579       }
6580     }
6581     else if (element == EL_BD_DIAMOND)
6582     {
6583       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6584       {
6585         Bang(x, y + 1);
6586         return;
6587       }
6588     }
6589     else if (((element == EL_SP_INFOTRON ||
6590                element == EL_SP_ZONK) &&
6591               (smashed == EL_SP_SNIKSNAK ||
6592                smashed == EL_SP_ELECTRON ||
6593                smashed == EL_SP_DISK_ORANGE)) ||
6594              (element == EL_SP_INFOTRON &&
6595               smashed == EL_SP_DISK_YELLOW))
6596     {
6597       Bang(x, y + 1);
6598       return;
6599     }
6600     else if (CAN_SMASH_EVERYTHING(element))
6601     {
6602       if (IS_CLASSIC_ENEMY(smashed) ||
6603           CAN_EXPLODE_SMASHED(smashed))
6604       {
6605         Bang(x, y + 1);
6606         return;
6607       }
6608       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6609       {
6610         if (smashed == EL_LAMP ||
6611             smashed == EL_LAMP_ACTIVE)
6612         {
6613           Bang(x, y + 1);
6614           return;
6615         }
6616         else if (smashed == EL_NUT)
6617         {
6618           Feld[x][y + 1] = EL_NUT_BREAKING;
6619           PlayLevelSound(x, y, SND_NUT_BREAKING);
6620           RaiseScoreElement(EL_NUT);
6621           return;
6622         }
6623         else if (smashed == EL_PEARL)
6624         {
6625           ResetGfxAnimation(x, y);
6626
6627           Feld[x][y + 1] = EL_PEARL_BREAKING;
6628           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6629           return;
6630         }
6631         else if (smashed == EL_DIAMOND)
6632         {
6633           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6634           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6635           return;
6636         }
6637         else if (IS_BELT_SWITCH(smashed))
6638         {
6639           ToggleBeltSwitch(x, y + 1);
6640         }
6641         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6642                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6643                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6644                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6645         {
6646           ToggleSwitchgateSwitch(x, y + 1);
6647         }
6648         else if (smashed == EL_LIGHT_SWITCH ||
6649                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6650         {
6651           ToggleLightSwitch(x, y + 1);
6652         }
6653         else
6654         {
6655           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6656
6657           CheckElementChangeBySide(x, y + 1, smashed, element,
6658                                    CE_SWITCHED, CH_SIDE_TOP);
6659           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6660                                             CH_SIDE_TOP);
6661         }
6662       }
6663       else
6664       {
6665         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6666       }
6667     }
6668   }
6669
6670   // play sound of magic wall / mill
6671   if (!last_line &&
6672       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6673        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6674        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6675   {
6676     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6677       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6678     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6679       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6680     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6681       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6682
6683     return;
6684   }
6685
6686   // play sound of object that hits the ground
6687   if (last_line || object_hit)
6688     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6689 }
6690
6691 static void TurnRoundExt(int x, int y)
6692 {
6693   static struct
6694   {
6695     int dx, dy;
6696   } move_xy[] =
6697   {
6698     {  0,  0 },
6699     { -1,  0 },
6700     { +1,  0 },
6701     {  0,  0 },
6702     {  0, -1 },
6703     {  0,  0 }, { 0, 0 }, { 0, 0 },
6704     {  0, +1 }
6705   };
6706   static struct
6707   {
6708     int left, right, back;
6709   } turn[] =
6710   {
6711     { 0,        0,              0        },
6712     { MV_DOWN,  MV_UP,          MV_RIGHT },
6713     { MV_UP,    MV_DOWN,        MV_LEFT  },
6714     { 0,        0,              0        },
6715     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6716     { 0,        0,              0        },
6717     { 0,        0,              0        },
6718     { 0,        0,              0        },
6719     { MV_RIGHT, MV_LEFT,        MV_UP    }
6720   };
6721
6722   int element = Feld[x][y];
6723   int move_pattern = element_info[element].move_pattern;
6724
6725   int old_move_dir = MovDir[x][y];
6726   int left_dir  = turn[old_move_dir].left;
6727   int right_dir = turn[old_move_dir].right;
6728   int back_dir  = turn[old_move_dir].back;
6729
6730   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6731   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6732   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6733   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6734
6735   int left_x  = x + left_dx,  left_y  = y + left_dy;
6736   int right_x = x + right_dx, right_y = y + right_dy;
6737   int move_x  = x + move_dx,  move_y  = y + move_dy;
6738
6739   int xx, yy;
6740
6741   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6742   {
6743     TestIfBadThingTouchesOtherBadThing(x, y);
6744
6745     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6746       MovDir[x][y] = right_dir;
6747     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6748       MovDir[x][y] = left_dir;
6749
6750     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6751       MovDelay[x][y] = 9;
6752     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6753       MovDelay[x][y] = 1;
6754   }
6755   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6756   {
6757     TestIfBadThingTouchesOtherBadThing(x, y);
6758
6759     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6760       MovDir[x][y] = left_dir;
6761     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6762       MovDir[x][y] = right_dir;
6763
6764     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6765       MovDelay[x][y] = 9;
6766     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6767       MovDelay[x][y] = 1;
6768   }
6769   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6770   {
6771     TestIfBadThingTouchesOtherBadThing(x, y);
6772
6773     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6774       MovDir[x][y] = left_dir;
6775     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6776       MovDir[x][y] = right_dir;
6777
6778     if (MovDir[x][y] != old_move_dir)
6779       MovDelay[x][y] = 9;
6780   }
6781   else if (element == EL_YAMYAM)
6782   {
6783     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6784     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6785
6786     if (can_turn_left && can_turn_right)
6787       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6788     else if (can_turn_left)
6789       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6790     else if (can_turn_right)
6791       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6792     else
6793       MovDir[x][y] = back_dir;
6794
6795     MovDelay[x][y] = 16 + 16 * RND(3);
6796   }
6797   else if (element == EL_DARK_YAMYAM)
6798   {
6799     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6800                                                          left_x, left_y);
6801     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6802                                                          right_x, right_y);
6803
6804     if (can_turn_left && can_turn_right)
6805       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6806     else if (can_turn_left)
6807       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6808     else if (can_turn_right)
6809       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6810     else
6811       MovDir[x][y] = back_dir;
6812
6813     MovDelay[x][y] = 16 + 16 * RND(3);
6814   }
6815   else if (element == EL_PACMAN)
6816   {
6817     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6818     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6819
6820     if (can_turn_left && can_turn_right)
6821       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6822     else if (can_turn_left)
6823       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6824     else if (can_turn_right)
6825       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6826     else
6827       MovDir[x][y] = back_dir;
6828
6829     MovDelay[x][y] = 6 + RND(40);
6830   }
6831   else if (element == EL_PIG)
6832   {
6833     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6834     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6835     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6836     boolean should_turn_left, should_turn_right, should_move_on;
6837     int rnd_value = 24;
6838     int rnd = RND(rnd_value);
6839
6840     should_turn_left = (can_turn_left &&
6841                         (!can_move_on ||
6842                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6843                                                    y + back_dy + left_dy)));
6844     should_turn_right = (can_turn_right &&
6845                          (!can_move_on ||
6846                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6847                                                     y + back_dy + right_dy)));
6848     should_move_on = (can_move_on &&
6849                       (!can_turn_left ||
6850                        !can_turn_right ||
6851                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6852                                                  y + move_dy + left_dy) ||
6853                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6854                                                  y + move_dy + right_dy)));
6855
6856     if (should_turn_left || should_turn_right || should_move_on)
6857     {
6858       if (should_turn_left && should_turn_right && should_move_on)
6859         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6860                         rnd < 2 * rnd_value / 3 ? right_dir :
6861                         old_move_dir);
6862       else if (should_turn_left && should_turn_right)
6863         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6864       else if (should_turn_left && should_move_on)
6865         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6866       else if (should_turn_right && should_move_on)
6867         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6868       else if (should_turn_left)
6869         MovDir[x][y] = left_dir;
6870       else if (should_turn_right)
6871         MovDir[x][y] = right_dir;
6872       else if (should_move_on)
6873         MovDir[x][y] = old_move_dir;
6874     }
6875     else if (can_move_on && rnd > rnd_value / 8)
6876       MovDir[x][y] = old_move_dir;
6877     else if (can_turn_left && can_turn_right)
6878       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6879     else if (can_turn_left && rnd > rnd_value / 8)
6880       MovDir[x][y] = left_dir;
6881     else if (can_turn_right && rnd > rnd_value/8)
6882       MovDir[x][y] = right_dir;
6883     else
6884       MovDir[x][y] = back_dir;
6885
6886     xx = x + move_xy[MovDir[x][y]].dx;
6887     yy = y + move_xy[MovDir[x][y]].dy;
6888
6889     if (!IN_LEV_FIELD(xx, yy) ||
6890         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6891       MovDir[x][y] = old_move_dir;
6892
6893     MovDelay[x][y] = 0;
6894   }
6895   else if (element == EL_DRAGON)
6896   {
6897     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6898     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6899     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6900     int rnd_value = 24;
6901     int rnd = RND(rnd_value);
6902
6903     if (can_move_on && rnd > rnd_value / 8)
6904       MovDir[x][y] = old_move_dir;
6905     else if (can_turn_left && can_turn_right)
6906       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6907     else if (can_turn_left && rnd > rnd_value / 8)
6908       MovDir[x][y] = left_dir;
6909     else if (can_turn_right && rnd > rnd_value / 8)
6910       MovDir[x][y] = right_dir;
6911     else
6912       MovDir[x][y] = back_dir;
6913
6914     xx = x + move_xy[MovDir[x][y]].dx;
6915     yy = y + move_xy[MovDir[x][y]].dy;
6916
6917     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6918       MovDir[x][y] = old_move_dir;
6919
6920     MovDelay[x][y] = 0;
6921   }
6922   else if (element == EL_MOLE)
6923   {
6924     boolean can_move_on =
6925       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6926                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6927                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6928     if (!can_move_on)
6929     {
6930       boolean can_turn_left =
6931         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6932                               IS_AMOEBOID(Feld[left_x][left_y])));
6933
6934       boolean can_turn_right =
6935         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6936                               IS_AMOEBOID(Feld[right_x][right_y])));
6937
6938       if (can_turn_left && can_turn_right)
6939         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6940       else if (can_turn_left)
6941         MovDir[x][y] = left_dir;
6942       else
6943         MovDir[x][y] = right_dir;
6944     }
6945
6946     if (MovDir[x][y] != old_move_dir)
6947       MovDelay[x][y] = 9;
6948   }
6949   else if (element == EL_BALLOON)
6950   {
6951     MovDir[x][y] = game.wind_direction;
6952     MovDelay[x][y] = 0;
6953   }
6954   else if (element == EL_SPRING)
6955   {
6956     if (MovDir[x][y] & MV_HORIZONTAL)
6957     {
6958       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6959           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6960       {
6961         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6962         ResetGfxAnimation(move_x, move_y);
6963         TEST_DrawLevelField(move_x, move_y);
6964
6965         MovDir[x][y] = back_dir;
6966       }
6967       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6968                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6969         MovDir[x][y] = MV_NONE;
6970     }
6971
6972     MovDelay[x][y] = 0;
6973   }
6974   else if (element == EL_ROBOT ||
6975            element == EL_SATELLITE ||
6976            element == EL_PENGUIN ||
6977            element == EL_EMC_ANDROID)
6978   {
6979     int attr_x = -1, attr_y = -1;
6980
6981     if (game.all_players_gone)
6982     {
6983       attr_x = game.exit_x;
6984       attr_y = game.exit_y;
6985     }
6986     else
6987     {
6988       int i;
6989
6990       for (i = 0; i < MAX_PLAYERS; i++)
6991       {
6992         struct PlayerInfo *player = &stored_player[i];
6993         int jx = player->jx, jy = player->jy;
6994
6995         if (!player->active)
6996           continue;
6997
6998         if (attr_x == -1 ||
6999             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7000         {
7001           attr_x = jx;
7002           attr_y = jy;
7003         }
7004       }
7005     }
7006
7007     if (element == EL_ROBOT &&
7008         game.robot_wheel_x >= 0 &&
7009         game.robot_wheel_y >= 0 &&
7010         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7011          game.engine_version < VERSION_IDENT(3,1,0,0)))
7012     {
7013       attr_x = game.robot_wheel_x;
7014       attr_y = game.robot_wheel_y;
7015     }
7016
7017     if (element == EL_PENGUIN)
7018     {
7019       int i;
7020       static int xy[4][2] =
7021       {
7022         { 0, -1 },
7023         { -1, 0 },
7024         { +1, 0 },
7025         { 0, +1 }
7026       };
7027
7028       for (i = 0; i < NUM_DIRECTIONS; i++)
7029       {
7030         int ex = x + xy[i][0];
7031         int ey = y + xy[i][1];
7032
7033         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7034                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7035                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7036                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7037         {
7038           attr_x = ex;
7039           attr_y = ey;
7040           break;
7041         }
7042       }
7043     }
7044
7045     MovDir[x][y] = MV_NONE;
7046     if (attr_x < x)
7047       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7048     else if (attr_x > x)
7049       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7050     if (attr_y < y)
7051       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7052     else if (attr_y > y)
7053       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7054
7055     if (element == EL_ROBOT)
7056     {
7057       int newx, newy;
7058
7059       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7060         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7061       Moving2Blocked(x, y, &newx, &newy);
7062
7063       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7064         MovDelay[x][y] = 8 + 8 * !RND(3);
7065       else
7066         MovDelay[x][y] = 16;
7067     }
7068     else if (element == EL_PENGUIN)
7069     {
7070       int newx, newy;
7071
7072       MovDelay[x][y] = 1;
7073
7074       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7075       {
7076         boolean first_horiz = RND(2);
7077         int new_move_dir = MovDir[x][y];
7078
7079         MovDir[x][y] =
7080           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7081         Moving2Blocked(x, y, &newx, &newy);
7082
7083         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7084           return;
7085
7086         MovDir[x][y] =
7087           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7088         Moving2Blocked(x, y, &newx, &newy);
7089
7090         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7091           return;
7092
7093         MovDir[x][y] = old_move_dir;
7094         return;
7095       }
7096     }
7097     else if (element == EL_SATELLITE)
7098     {
7099       int newx, newy;
7100
7101       MovDelay[x][y] = 1;
7102
7103       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7104       {
7105         boolean first_horiz = RND(2);
7106         int new_move_dir = MovDir[x][y];
7107
7108         MovDir[x][y] =
7109           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7110         Moving2Blocked(x, y, &newx, &newy);
7111
7112         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7113           return;
7114
7115         MovDir[x][y] =
7116           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7117         Moving2Blocked(x, y, &newx, &newy);
7118
7119         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7120           return;
7121
7122         MovDir[x][y] = old_move_dir;
7123         return;
7124       }
7125     }
7126     else if (element == EL_EMC_ANDROID)
7127     {
7128       static int check_pos[16] =
7129       {
7130         -1,             //  0 => (invalid)
7131         7,              //  1 => MV_LEFT
7132         3,              //  2 => MV_RIGHT
7133         -1,             //  3 => (invalid)
7134         1,              //  4 =>            MV_UP
7135         0,              //  5 => MV_LEFT  | MV_UP
7136         2,              //  6 => MV_RIGHT | MV_UP
7137         -1,             //  7 => (invalid)
7138         5,              //  8 =>            MV_DOWN
7139         6,              //  9 => MV_LEFT  | MV_DOWN
7140         4,              // 10 => MV_RIGHT | MV_DOWN
7141         -1,             // 11 => (invalid)
7142         -1,             // 12 => (invalid)
7143         -1,             // 13 => (invalid)
7144         -1,             // 14 => (invalid)
7145         -1,             // 15 => (invalid)
7146       };
7147       static struct
7148       {
7149         int dx, dy;
7150         int dir;
7151       } check_xy[8] =
7152       {
7153         { -1, -1,       MV_LEFT  | MV_UP   },
7154         {  0, -1,                  MV_UP   },
7155         { +1, -1,       MV_RIGHT | MV_UP   },
7156         { +1,  0,       MV_RIGHT           },
7157         { +1, +1,       MV_RIGHT | MV_DOWN },
7158         {  0, +1,                  MV_DOWN },
7159         { -1, +1,       MV_LEFT  | MV_DOWN },
7160         { -1,  0,       MV_LEFT            },
7161       };
7162       int start_pos, check_order;
7163       boolean can_clone = FALSE;
7164       int i;
7165
7166       // check if there is any free field around current position
7167       for (i = 0; i < 8; i++)
7168       {
7169         int newx = x + check_xy[i].dx;
7170         int newy = y + check_xy[i].dy;
7171
7172         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7173         {
7174           can_clone = TRUE;
7175
7176           break;
7177         }
7178       }
7179
7180       if (can_clone)            // randomly find an element to clone
7181       {
7182         can_clone = FALSE;
7183
7184         start_pos = check_pos[RND(8)];
7185         check_order = (RND(2) ? -1 : +1);
7186
7187         for (i = 0; i < 8; i++)
7188         {
7189           int pos_raw = start_pos + i * check_order;
7190           int pos = (pos_raw + 8) % 8;
7191           int newx = x + check_xy[pos].dx;
7192           int newy = y + check_xy[pos].dy;
7193
7194           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7195           {
7196             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7197             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7198
7199             Store[x][y] = Feld[newx][newy];
7200
7201             can_clone = TRUE;
7202
7203             break;
7204           }
7205         }
7206       }
7207
7208       if (can_clone)            // randomly find a direction to move
7209       {
7210         can_clone = FALSE;
7211
7212         start_pos = check_pos[RND(8)];
7213         check_order = (RND(2) ? -1 : +1);
7214
7215         for (i = 0; i < 8; i++)
7216         {
7217           int pos_raw = start_pos + i * check_order;
7218           int pos = (pos_raw + 8) % 8;
7219           int newx = x + check_xy[pos].dx;
7220           int newy = y + check_xy[pos].dy;
7221           int new_move_dir = check_xy[pos].dir;
7222
7223           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7224           {
7225             MovDir[x][y] = new_move_dir;
7226             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7227
7228             can_clone = TRUE;
7229
7230             break;
7231           }
7232         }
7233       }
7234
7235       if (can_clone)            // cloning and moving successful
7236         return;
7237
7238       // cannot clone -- try to move towards player
7239
7240       start_pos = check_pos[MovDir[x][y] & 0x0f];
7241       check_order = (RND(2) ? -1 : +1);
7242
7243       for (i = 0; i < 3; i++)
7244       {
7245         // first check start_pos, then previous/next or (next/previous) pos
7246         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7247         int pos = (pos_raw + 8) % 8;
7248         int newx = x + check_xy[pos].dx;
7249         int newy = y + check_xy[pos].dy;
7250         int new_move_dir = check_xy[pos].dir;
7251
7252         if (IS_PLAYER(newx, newy))
7253           break;
7254
7255         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7256         {
7257           MovDir[x][y] = new_move_dir;
7258           MovDelay[x][y] = level.android_move_time * 8 + 1;
7259
7260           break;
7261         }
7262       }
7263     }
7264   }
7265   else if (move_pattern == MV_TURNING_LEFT ||
7266            move_pattern == MV_TURNING_RIGHT ||
7267            move_pattern == MV_TURNING_LEFT_RIGHT ||
7268            move_pattern == MV_TURNING_RIGHT_LEFT ||
7269            move_pattern == MV_TURNING_RANDOM ||
7270            move_pattern == MV_ALL_DIRECTIONS)
7271   {
7272     boolean can_turn_left =
7273       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7274     boolean can_turn_right =
7275       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7276
7277     if (element_info[element].move_stepsize == 0)       // "not moving"
7278       return;
7279
7280     if (move_pattern == MV_TURNING_LEFT)
7281       MovDir[x][y] = left_dir;
7282     else if (move_pattern == MV_TURNING_RIGHT)
7283       MovDir[x][y] = right_dir;
7284     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7285       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7286     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7287       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7288     else if (move_pattern == MV_TURNING_RANDOM)
7289       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7290                       can_turn_right && !can_turn_left ? right_dir :
7291                       RND(2) ? left_dir : right_dir);
7292     else if (can_turn_left && can_turn_right)
7293       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7294     else if (can_turn_left)
7295       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7296     else if (can_turn_right)
7297       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7298     else
7299       MovDir[x][y] = back_dir;
7300
7301     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7302   }
7303   else if (move_pattern == MV_HORIZONTAL ||
7304            move_pattern == MV_VERTICAL)
7305   {
7306     if (move_pattern & old_move_dir)
7307       MovDir[x][y] = back_dir;
7308     else if (move_pattern == MV_HORIZONTAL)
7309       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7310     else if (move_pattern == MV_VERTICAL)
7311       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7312
7313     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7314   }
7315   else if (move_pattern & MV_ANY_DIRECTION)
7316   {
7317     MovDir[x][y] = move_pattern;
7318     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7319   }
7320   else if (move_pattern & MV_WIND_DIRECTION)
7321   {
7322     MovDir[x][y] = game.wind_direction;
7323     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7324   }
7325   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7326   {
7327     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7328       MovDir[x][y] = left_dir;
7329     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7330       MovDir[x][y] = right_dir;
7331
7332     if (MovDir[x][y] != old_move_dir)
7333       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7334   }
7335   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7336   {
7337     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7338       MovDir[x][y] = right_dir;
7339     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7340       MovDir[x][y] = left_dir;
7341
7342     if (MovDir[x][y] != old_move_dir)
7343       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7344   }
7345   else if (move_pattern == MV_TOWARDS_PLAYER ||
7346            move_pattern == MV_AWAY_FROM_PLAYER)
7347   {
7348     int attr_x = -1, attr_y = -1;
7349     int newx, newy;
7350     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7351
7352     if (game.all_players_gone)
7353     {
7354       attr_x = game.exit_x;
7355       attr_y = game.exit_y;
7356     }
7357     else
7358     {
7359       int i;
7360
7361       for (i = 0; i < MAX_PLAYERS; i++)
7362       {
7363         struct PlayerInfo *player = &stored_player[i];
7364         int jx = player->jx, jy = player->jy;
7365
7366         if (!player->active)
7367           continue;
7368
7369         if (attr_x == -1 ||
7370             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7371         {
7372           attr_x = jx;
7373           attr_y = jy;
7374         }
7375       }
7376     }
7377
7378     MovDir[x][y] = MV_NONE;
7379     if (attr_x < x)
7380       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7381     else if (attr_x > x)
7382       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7383     if (attr_y < y)
7384       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7385     else if (attr_y > y)
7386       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7387
7388     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7389
7390     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7391     {
7392       boolean first_horiz = RND(2);
7393       int new_move_dir = MovDir[x][y];
7394
7395       if (element_info[element].move_stepsize == 0)     // "not moving"
7396       {
7397         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7398         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7399
7400         return;
7401       }
7402
7403       MovDir[x][y] =
7404         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7405       Moving2Blocked(x, y, &newx, &newy);
7406
7407       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7408         return;
7409
7410       MovDir[x][y] =
7411         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7412       Moving2Blocked(x, y, &newx, &newy);
7413
7414       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7415         return;
7416
7417       MovDir[x][y] = old_move_dir;
7418     }
7419   }
7420   else if (move_pattern == MV_WHEN_PUSHED ||
7421            move_pattern == MV_WHEN_DROPPED)
7422   {
7423     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7424       MovDir[x][y] = MV_NONE;
7425
7426     MovDelay[x][y] = 0;
7427   }
7428   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7429   {
7430     static int test_xy[7][2] =
7431     {
7432       { 0, -1 },
7433       { -1, 0 },
7434       { +1, 0 },
7435       { 0, +1 },
7436       { 0, -1 },
7437       { -1, 0 },
7438       { +1, 0 },
7439     };
7440     static int test_dir[7] =
7441     {
7442       MV_UP,
7443       MV_LEFT,
7444       MV_RIGHT,
7445       MV_DOWN,
7446       MV_UP,
7447       MV_LEFT,
7448       MV_RIGHT,
7449     };
7450     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7451     int move_preference = -1000000;     // start with very low preference
7452     int new_move_dir = MV_NONE;
7453     int start_test = RND(4);
7454     int i;
7455
7456     for (i = 0; i < NUM_DIRECTIONS; i++)
7457     {
7458       int move_dir = test_dir[start_test + i];
7459       int move_dir_preference;
7460
7461       xx = x + test_xy[start_test + i][0];
7462       yy = y + test_xy[start_test + i][1];
7463
7464       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7465           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7466       {
7467         new_move_dir = move_dir;
7468
7469         break;
7470       }
7471
7472       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7473         continue;
7474
7475       move_dir_preference = -1 * RunnerVisit[xx][yy];
7476       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7477         move_dir_preference = PlayerVisit[xx][yy];
7478
7479       if (move_dir_preference > move_preference)
7480       {
7481         // prefer field that has not been visited for the longest time
7482         move_preference = move_dir_preference;
7483         new_move_dir = move_dir;
7484       }
7485       else if (move_dir_preference == move_preference &&
7486                move_dir == old_move_dir)
7487       {
7488         // prefer last direction when all directions are preferred equally
7489         move_preference = move_dir_preference;
7490         new_move_dir = move_dir;
7491       }
7492     }
7493
7494     MovDir[x][y] = new_move_dir;
7495     if (old_move_dir != new_move_dir)
7496       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7497   }
7498 }
7499
7500 static void TurnRound(int x, int y)
7501 {
7502   int direction = MovDir[x][y];
7503
7504   TurnRoundExt(x, y);
7505
7506   GfxDir[x][y] = MovDir[x][y];
7507
7508   if (direction != MovDir[x][y])
7509     GfxFrame[x][y] = 0;
7510
7511   if (MovDelay[x][y])
7512     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7513
7514   ResetGfxFrame(x, y);
7515 }
7516
7517 static boolean JustBeingPushed(int x, int y)
7518 {
7519   int i;
7520
7521   for (i = 0; i < MAX_PLAYERS; i++)
7522   {
7523     struct PlayerInfo *player = &stored_player[i];
7524
7525     if (player->active && player->is_pushing && player->MovPos)
7526     {
7527       int next_jx = player->jx + (player->jx - player->last_jx);
7528       int next_jy = player->jy + (player->jy - player->last_jy);
7529
7530       if (x == next_jx && y == next_jy)
7531         return TRUE;
7532     }
7533   }
7534
7535   return FALSE;
7536 }
7537
7538 static void StartMoving(int x, int y)
7539 {
7540   boolean started_moving = FALSE;       // some elements can fall _and_ move
7541   int element = Feld[x][y];
7542
7543   if (Stop[x][y])
7544     return;
7545
7546   if (MovDelay[x][y] == 0)
7547     GfxAction[x][y] = ACTION_DEFAULT;
7548
7549   if (CAN_FALL(element) && y < lev_fieldy - 1)
7550   {
7551     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7552         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7553       if (JustBeingPushed(x, y))
7554         return;
7555
7556     if (element == EL_QUICKSAND_FULL)
7557     {
7558       if (IS_FREE(x, y + 1))
7559       {
7560         InitMovingField(x, y, MV_DOWN);
7561         started_moving = TRUE;
7562
7563         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7564 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7565         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7566           Store[x][y] = EL_ROCK;
7567 #else
7568         Store[x][y] = EL_ROCK;
7569 #endif
7570
7571         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7572       }
7573       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7574       {
7575         if (!MovDelay[x][y])
7576         {
7577           MovDelay[x][y] = TILEY + 1;
7578
7579           ResetGfxAnimation(x, y);
7580           ResetGfxAnimation(x, y + 1);
7581         }
7582
7583         if (MovDelay[x][y])
7584         {
7585           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7586           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7587
7588           MovDelay[x][y]--;
7589           if (MovDelay[x][y])
7590             return;
7591         }
7592
7593         Feld[x][y] = EL_QUICKSAND_EMPTY;
7594         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7595         Store[x][y + 1] = Store[x][y];
7596         Store[x][y] = 0;
7597
7598         PlayLevelSoundAction(x, y, ACTION_FILLING);
7599       }
7600       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7601       {
7602         if (!MovDelay[x][y])
7603         {
7604           MovDelay[x][y] = TILEY + 1;
7605
7606           ResetGfxAnimation(x, y);
7607           ResetGfxAnimation(x, y + 1);
7608         }
7609
7610         if (MovDelay[x][y])
7611         {
7612           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7613           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7614
7615           MovDelay[x][y]--;
7616           if (MovDelay[x][y])
7617             return;
7618         }
7619
7620         Feld[x][y] = EL_QUICKSAND_EMPTY;
7621         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7622         Store[x][y + 1] = Store[x][y];
7623         Store[x][y] = 0;
7624
7625         PlayLevelSoundAction(x, y, ACTION_FILLING);
7626       }
7627     }
7628     else if (element == EL_QUICKSAND_FAST_FULL)
7629     {
7630       if (IS_FREE(x, y + 1))
7631       {
7632         InitMovingField(x, y, MV_DOWN);
7633         started_moving = TRUE;
7634
7635         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7636 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7637         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7638           Store[x][y] = EL_ROCK;
7639 #else
7640         Store[x][y] = EL_ROCK;
7641 #endif
7642
7643         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7644       }
7645       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7646       {
7647         if (!MovDelay[x][y])
7648         {
7649           MovDelay[x][y] = TILEY + 1;
7650
7651           ResetGfxAnimation(x, y);
7652           ResetGfxAnimation(x, y + 1);
7653         }
7654
7655         if (MovDelay[x][y])
7656         {
7657           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7658           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7659
7660           MovDelay[x][y]--;
7661           if (MovDelay[x][y])
7662             return;
7663         }
7664
7665         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7666         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7667         Store[x][y + 1] = Store[x][y];
7668         Store[x][y] = 0;
7669
7670         PlayLevelSoundAction(x, y, ACTION_FILLING);
7671       }
7672       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7673       {
7674         if (!MovDelay[x][y])
7675         {
7676           MovDelay[x][y] = TILEY + 1;
7677
7678           ResetGfxAnimation(x, y);
7679           ResetGfxAnimation(x, y + 1);
7680         }
7681
7682         if (MovDelay[x][y])
7683         {
7684           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7685           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7686
7687           MovDelay[x][y]--;
7688           if (MovDelay[x][y])
7689             return;
7690         }
7691
7692         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7693         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7694         Store[x][y + 1] = Store[x][y];
7695         Store[x][y] = 0;
7696
7697         PlayLevelSoundAction(x, y, ACTION_FILLING);
7698       }
7699     }
7700     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7701              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7702     {
7703       InitMovingField(x, y, MV_DOWN);
7704       started_moving = TRUE;
7705
7706       Feld[x][y] = EL_QUICKSAND_FILLING;
7707       Store[x][y] = element;
7708
7709       PlayLevelSoundAction(x, y, ACTION_FILLING);
7710     }
7711     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7712              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7713     {
7714       InitMovingField(x, y, MV_DOWN);
7715       started_moving = TRUE;
7716
7717       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7718       Store[x][y] = element;
7719
7720       PlayLevelSoundAction(x, y, ACTION_FILLING);
7721     }
7722     else if (element == EL_MAGIC_WALL_FULL)
7723     {
7724       if (IS_FREE(x, y + 1))
7725       {
7726         InitMovingField(x, y, MV_DOWN);
7727         started_moving = TRUE;
7728
7729         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7730         Store[x][y] = EL_CHANGED(Store[x][y]);
7731       }
7732       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7733       {
7734         if (!MovDelay[x][y])
7735           MovDelay[x][y] = TILEY / 4 + 1;
7736
7737         if (MovDelay[x][y])
7738         {
7739           MovDelay[x][y]--;
7740           if (MovDelay[x][y])
7741             return;
7742         }
7743
7744         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7745         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7746         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7747         Store[x][y] = 0;
7748       }
7749     }
7750     else if (element == EL_BD_MAGIC_WALL_FULL)
7751     {
7752       if (IS_FREE(x, y + 1))
7753       {
7754         InitMovingField(x, y, MV_DOWN);
7755         started_moving = TRUE;
7756
7757         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7758         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7759       }
7760       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7761       {
7762         if (!MovDelay[x][y])
7763           MovDelay[x][y] = TILEY / 4 + 1;
7764
7765         if (MovDelay[x][y])
7766         {
7767           MovDelay[x][y]--;
7768           if (MovDelay[x][y])
7769             return;
7770         }
7771
7772         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7773         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7774         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7775         Store[x][y] = 0;
7776       }
7777     }
7778     else if (element == EL_DC_MAGIC_WALL_FULL)
7779     {
7780       if (IS_FREE(x, y + 1))
7781       {
7782         InitMovingField(x, y, MV_DOWN);
7783         started_moving = TRUE;
7784
7785         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7786         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7787       }
7788       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7789       {
7790         if (!MovDelay[x][y])
7791           MovDelay[x][y] = TILEY / 4 + 1;
7792
7793         if (MovDelay[x][y])
7794         {
7795           MovDelay[x][y]--;
7796           if (MovDelay[x][y])
7797             return;
7798         }
7799
7800         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7801         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7802         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7803         Store[x][y] = 0;
7804       }
7805     }
7806     else if ((CAN_PASS_MAGIC_WALL(element) &&
7807               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7808                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7809              (CAN_PASS_DC_MAGIC_WALL(element) &&
7810               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7811
7812     {
7813       InitMovingField(x, y, MV_DOWN);
7814       started_moving = TRUE;
7815
7816       Feld[x][y] =
7817         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7818          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7819          EL_DC_MAGIC_WALL_FILLING);
7820       Store[x][y] = element;
7821     }
7822     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7823     {
7824       SplashAcid(x, y + 1);
7825
7826       InitMovingField(x, y, MV_DOWN);
7827       started_moving = TRUE;
7828
7829       Store[x][y] = EL_ACID;
7830     }
7831     else if (
7832              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7833               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7834              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7835               CAN_FALL(element) && WasJustFalling[x][y] &&
7836               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7837
7838              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7839               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7840               (Feld[x][y + 1] == EL_BLOCKED)))
7841     {
7842       /* this is needed for a special case not covered by calling "Impact()"
7843          from "ContinueMoving()": if an element moves to a tile directly below
7844          another element which was just falling on that tile (which was empty
7845          in the previous frame), the falling element above would just stop
7846          instead of smashing the element below (in previous version, the above
7847          element was just checked for "moving" instead of "falling", resulting
7848          in incorrect smashes caused by horizontal movement of the above
7849          element; also, the case of the player being the element to smash was
7850          simply not covered here... :-/ ) */
7851
7852       CheckCollision[x][y] = 0;
7853       CheckImpact[x][y] = 0;
7854
7855       Impact(x, y);
7856     }
7857     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7858     {
7859       if (MovDir[x][y] == MV_NONE)
7860       {
7861         InitMovingField(x, y, MV_DOWN);
7862         started_moving = TRUE;
7863       }
7864     }
7865     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7866     {
7867       if (WasJustFalling[x][y]) // prevent animation from being restarted
7868         MovDir[x][y] = MV_DOWN;
7869
7870       InitMovingField(x, y, MV_DOWN);
7871       started_moving = TRUE;
7872     }
7873     else if (element == EL_AMOEBA_DROP)
7874     {
7875       Feld[x][y] = EL_AMOEBA_GROWING;
7876       Store[x][y] = EL_AMOEBA_WET;
7877     }
7878     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7879               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7880              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7881              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7882     {
7883       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7884                                 (IS_FREE(x - 1, y + 1) ||
7885                                  Feld[x - 1][y + 1] == EL_ACID));
7886       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7887                                 (IS_FREE(x + 1, y + 1) ||
7888                                  Feld[x + 1][y + 1] == EL_ACID));
7889       boolean can_fall_any  = (can_fall_left || can_fall_right);
7890       boolean can_fall_both = (can_fall_left && can_fall_right);
7891       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7892
7893       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7894       {
7895         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7896           can_fall_right = FALSE;
7897         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7898           can_fall_left = FALSE;
7899         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7900           can_fall_right = FALSE;
7901         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7902           can_fall_left = FALSE;
7903
7904         can_fall_any  = (can_fall_left || can_fall_right);
7905         can_fall_both = FALSE;
7906       }
7907
7908       if (can_fall_both)
7909       {
7910         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7911           can_fall_right = FALSE;       // slip down on left side
7912         else
7913           can_fall_left = !(can_fall_right = RND(2));
7914
7915         can_fall_both = FALSE;
7916       }
7917
7918       if (can_fall_any)
7919       {
7920         // if not determined otherwise, prefer left side for slipping down
7921         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7922         started_moving = TRUE;
7923       }
7924     }
7925     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7926     {
7927       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7928       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7929       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7930       int belt_dir = game.belt_dir[belt_nr];
7931
7932       if ((belt_dir == MV_LEFT  && left_is_free) ||
7933           (belt_dir == MV_RIGHT && right_is_free))
7934       {
7935         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7936
7937         InitMovingField(x, y, belt_dir);
7938         started_moving = TRUE;
7939
7940         Pushed[x][y] = TRUE;
7941         Pushed[nextx][y] = TRUE;
7942
7943         GfxAction[x][y] = ACTION_DEFAULT;
7944       }
7945       else
7946       {
7947         MovDir[x][y] = 0;       // if element was moving, stop it
7948       }
7949     }
7950   }
7951
7952   // not "else if" because of elements that can fall and move (EL_SPRING)
7953   if (CAN_MOVE(element) && !started_moving)
7954   {
7955     int move_pattern = element_info[element].move_pattern;
7956     int newx, newy;
7957
7958     Moving2Blocked(x, y, &newx, &newy);
7959
7960     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7961       return;
7962
7963     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7964         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7965     {
7966       WasJustMoving[x][y] = 0;
7967       CheckCollision[x][y] = 0;
7968
7969       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7970
7971       if (Feld[x][y] != element)        // element has changed
7972         return;
7973     }
7974
7975     if (!MovDelay[x][y])        // start new movement phase
7976     {
7977       // all objects that can change their move direction after each step
7978       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7979
7980       if (element != EL_YAMYAM &&
7981           element != EL_DARK_YAMYAM &&
7982           element != EL_PACMAN &&
7983           !(move_pattern & MV_ANY_DIRECTION) &&
7984           move_pattern != MV_TURNING_LEFT &&
7985           move_pattern != MV_TURNING_RIGHT &&
7986           move_pattern != MV_TURNING_LEFT_RIGHT &&
7987           move_pattern != MV_TURNING_RIGHT_LEFT &&
7988           move_pattern != MV_TURNING_RANDOM)
7989       {
7990         TurnRound(x, y);
7991
7992         if (MovDelay[x][y] && (element == EL_BUG ||
7993                                element == EL_SPACESHIP ||
7994                                element == EL_SP_SNIKSNAK ||
7995                                element == EL_SP_ELECTRON ||
7996                                element == EL_MOLE))
7997           TEST_DrawLevelField(x, y);
7998       }
7999     }
8000
8001     if (MovDelay[x][y])         // wait some time before next movement
8002     {
8003       MovDelay[x][y]--;
8004
8005       if (element == EL_ROBOT ||
8006           element == EL_YAMYAM ||
8007           element == EL_DARK_YAMYAM)
8008       {
8009         DrawLevelElementAnimationIfNeeded(x, y, element);
8010         PlayLevelSoundAction(x, y, ACTION_WAITING);
8011       }
8012       else if (element == EL_SP_ELECTRON)
8013         DrawLevelElementAnimationIfNeeded(x, y, element);
8014       else if (element == EL_DRAGON)
8015       {
8016         int i;
8017         int dir = MovDir[x][y];
8018         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8019         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8020         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8021                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8022                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8023                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8024         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8025
8026         GfxAction[x][y] = ACTION_ATTACKING;
8027
8028         if (IS_PLAYER(x, y))
8029           DrawPlayerField(x, y);
8030         else
8031           TEST_DrawLevelField(x, y);
8032
8033         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8034
8035         for (i = 1; i <= 3; i++)
8036         {
8037           int xx = x + i * dx;
8038           int yy = y + i * dy;
8039           int sx = SCREENX(xx);
8040           int sy = SCREENY(yy);
8041           int flame_graphic = graphic + (i - 1);
8042
8043           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8044             break;
8045
8046           if (MovDelay[x][y])
8047           {
8048             int flamed = MovingOrBlocked2Element(xx, yy);
8049
8050             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8051               Bang(xx, yy);
8052             else
8053               RemoveMovingField(xx, yy);
8054
8055             ChangeDelay[xx][yy] = 0;
8056
8057             Feld[xx][yy] = EL_FLAMES;
8058
8059             if (IN_SCR_FIELD(sx, sy))
8060             {
8061               TEST_DrawLevelFieldCrumbled(xx, yy);
8062               DrawGraphic(sx, sy, flame_graphic, frame);
8063             }
8064           }
8065           else
8066           {
8067             if (Feld[xx][yy] == EL_FLAMES)
8068               Feld[xx][yy] = EL_EMPTY;
8069             TEST_DrawLevelField(xx, yy);
8070           }
8071         }
8072       }
8073
8074       if (MovDelay[x][y])       // element still has to wait some time
8075       {
8076         PlayLevelSoundAction(x, y, ACTION_WAITING);
8077
8078         return;
8079       }
8080     }
8081
8082     // now make next step
8083
8084     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8085
8086     if (DONT_COLLIDE_WITH(element) &&
8087         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8088         !PLAYER_ENEMY_PROTECTED(newx, newy))
8089     {
8090       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8091
8092       return;
8093     }
8094
8095     else if (CAN_MOVE_INTO_ACID(element) &&
8096              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8097              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8098              (MovDir[x][y] == MV_DOWN ||
8099               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8100     {
8101       SplashAcid(newx, newy);
8102       Store[x][y] = EL_ACID;
8103     }
8104     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8105     {
8106       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8107           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8108           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8109           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8110       {
8111         RemoveField(x, y);
8112         TEST_DrawLevelField(x, y);
8113
8114         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8115         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8116           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8117
8118         game.friends_still_needed--;
8119         if (!game.friends_still_needed &&
8120             !game.GameOver &&
8121             game.all_players_gone)
8122           LevelSolved();
8123
8124         return;
8125       }
8126       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8127       {
8128         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8129           TEST_DrawLevelField(newx, newy);
8130         else
8131           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8132       }
8133       else if (!IS_FREE(newx, newy))
8134       {
8135         GfxAction[x][y] = ACTION_WAITING;
8136
8137         if (IS_PLAYER(x, y))
8138           DrawPlayerField(x, y);
8139         else
8140           TEST_DrawLevelField(x, y);
8141
8142         return;
8143       }
8144     }
8145     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8146     {
8147       if (IS_FOOD_PIG(Feld[newx][newy]))
8148       {
8149         if (IS_MOVING(newx, newy))
8150           RemoveMovingField(newx, newy);
8151         else
8152         {
8153           Feld[newx][newy] = EL_EMPTY;
8154           TEST_DrawLevelField(newx, newy);
8155         }
8156
8157         PlayLevelSound(x, y, SND_PIG_DIGGING);
8158       }
8159       else if (!IS_FREE(newx, newy))
8160       {
8161         if (IS_PLAYER(x, y))
8162           DrawPlayerField(x, y);
8163         else
8164           TEST_DrawLevelField(x, y);
8165
8166         return;
8167       }
8168     }
8169     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8170     {
8171       if (Store[x][y] != EL_EMPTY)
8172       {
8173         boolean can_clone = FALSE;
8174         int xx, yy;
8175
8176         // check if element to clone is still there
8177         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8178         {
8179           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8180           {
8181             can_clone = TRUE;
8182
8183             break;
8184           }
8185         }
8186
8187         // cannot clone or target field not free anymore -- do not clone
8188         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8189           Store[x][y] = EL_EMPTY;
8190       }
8191
8192       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8193       {
8194         if (IS_MV_DIAGONAL(MovDir[x][y]))
8195         {
8196           int diagonal_move_dir = MovDir[x][y];
8197           int stored = Store[x][y];
8198           int change_delay = 8;
8199           int graphic;
8200
8201           // android is moving diagonally
8202
8203           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8204
8205           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8206           GfxElement[x][y] = EL_EMC_ANDROID;
8207           GfxAction[x][y] = ACTION_SHRINKING;
8208           GfxDir[x][y] = diagonal_move_dir;
8209           ChangeDelay[x][y] = change_delay;
8210
8211           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8212                                    GfxDir[x][y]);
8213
8214           DrawLevelGraphicAnimation(x, y, graphic);
8215           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8216
8217           if (Feld[newx][newy] == EL_ACID)
8218           {
8219             SplashAcid(newx, newy);
8220
8221             return;
8222           }
8223
8224           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8225
8226           Store[newx][newy] = EL_EMC_ANDROID;
8227           GfxElement[newx][newy] = EL_EMC_ANDROID;
8228           GfxAction[newx][newy] = ACTION_GROWING;
8229           GfxDir[newx][newy] = diagonal_move_dir;
8230           ChangeDelay[newx][newy] = change_delay;
8231
8232           graphic = el_act_dir2img(GfxElement[newx][newy],
8233                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8234
8235           DrawLevelGraphicAnimation(newx, newy, graphic);
8236           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8237
8238           return;
8239         }
8240         else
8241         {
8242           Feld[newx][newy] = EL_EMPTY;
8243           TEST_DrawLevelField(newx, newy);
8244
8245           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8246         }
8247       }
8248       else if (!IS_FREE(newx, newy))
8249       {
8250         return;
8251       }
8252     }
8253     else if (IS_CUSTOM_ELEMENT(element) &&
8254              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8255     {
8256       if (!DigFieldByCE(newx, newy, element))
8257         return;
8258
8259       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8260       {
8261         RunnerVisit[x][y] = FrameCounter;
8262         PlayerVisit[x][y] /= 8;         // expire player visit path
8263       }
8264     }
8265     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8266     {
8267       if (!IS_FREE(newx, newy))
8268       {
8269         if (IS_PLAYER(x, y))
8270           DrawPlayerField(x, y);
8271         else
8272           TEST_DrawLevelField(x, y);
8273
8274         return;
8275       }
8276       else
8277       {
8278         boolean wanna_flame = !RND(10);
8279         int dx = newx - x, dy = newy - y;
8280         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8281         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8282         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8283                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8284         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8285                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8286
8287         if ((wanna_flame ||
8288              IS_CLASSIC_ENEMY(element1) ||
8289              IS_CLASSIC_ENEMY(element2)) &&
8290             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8291             element1 != EL_FLAMES && element2 != EL_FLAMES)
8292         {
8293           ResetGfxAnimation(x, y);
8294           GfxAction[x][y] = ACTION_ATTACKING;
8295
8296           if (IS_PLAYER(x, y))
8297             DrawPlayerField(x, y);
8298           else
8299             TEST_DrawLevelField(x, y);
8300
8301           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8302
8303           MovDelay[x][y] = 50;
8304
8305           Feld[newx][newy] = EL_FLAMES;
8306           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8307             Feld[newx1][newy1] = EL_FLAMES;
8308           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8309             Feld[newx2][newy2] = EL_FLAMES;
8310
8311           return;
8312         }
8313       }
8314     }
8315     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8316              Feld[newx][newy] == EL_DIAMOND)
8317     {
8318       if (IS_MOVING(newx, newy))
8319         RemoveMovingField(newx, newy);
8320       else
8321       {
8322         Feld[newx][newy] = EL_EMPTY;
8323         TEST_DrawLevelField(newx, newy);
8324       }
8325
8326       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8327     }
8328     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8329              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8330     {
8331       if (AmoebaNr[newx][newy])
8332       {
8333         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8334         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8335             Feld[newx][newy] == EL_BD_AMOEBA)
8336           AmoebaCnt[AmoebaNr[newx][newy]]--;
8337       }
8338
8339       if (IS_MOVING(newx, newy))
8340       {
8341         RemoveMovingField(newx, newy);
8342       }
8343       else
8344       {
8345         Feld[newx][newy] = EL_EMPTY;
8346         TEST_DrawLevelField(newx, newy);
8347       }
8348
8349       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8350     }
8351     else if ((element == EL_PACMAN || element == EL_MOLE)
8352              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8353     {
8354       if (AmoebaNr[newx][newy])
8355       {
8356         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8357         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8358             Feld[newx][newy] == EL_BD_AMOEBA)
8359           AmoebaCnt[AmoebaNr[newx][newy]]--;
8360       }
8361
8362       if (element == EL_MOLE)
8363       {
8364         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8365         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8366
8367         ResetGfxAnimation(x, y);
8368         GfxAction[x][y] = ACTION_DIGGING;
8369         TEST_DrawLevelField(x, y);
8370
8371         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8372
8373         return;                         // wait for shrinking amoeba
8374       }
8375       else      // element == EL_PACMAN
8376       {
8377         Feld[newx][newy] = EL_EMPTY;
8378         TEST_DrawLevelField(newx, newy);
8379         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8380       }
8381     }
8382     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8383              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8384               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8385     {
8386       // wait for shrinking amoeba to completely disappear
8387       return;
8388     }
8389     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8390     {
8391       // object was running against a wall
8392
8393       TurnRound(x, y);
8394
8395       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8396         DrawLevelElementAnimation(x, y, element);
8397
8398       if (DONT_TOUCH(element))
8399         TestIfBadThingTouchesPlayer(x, y);
8400
8401       return;
8402     }
8403
8404     InitMovingField(x, y, MovDir[x][y]);
8405
8406     PlayLevelSoundAction(x, y, ACTION_MOVING);
8407   }
8408
8409   if (MovDir[x][y])
8410     ContinueMoving(x, y);
8411 }
8412
8413 void ContinueMoving(int x, int y)
8414 {
8415   int element = Feld[x][y];
8416   struct ElementInfo *ei = &element_info[element];
8417   int direction = MovDir[x][y];
8418   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8419   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8420   int newx = x + dx, newy = y + dy;
8421   int stored = Store[x][y];
8422   int stored_new = Store[newx][newy];
8423   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8424   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8425   boolean last_line = (newy == lev_fieldy - 1);
8426
8427   MovPos[x][y] += getElementMoveStepsize(x, y);
8428
8429   if (pushed_by_player) // special case: moving object pushed by player
8430     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8431
8432   if (ABS(MovPos[x][y]) < TILEX)
8433   {
8434     TEST_DrawLevelField(x, y);
8435
8436     return;     // element is still moving
8437   }
8438
8439   // element reached destination field
8440
8441   Feld[x][y] = EL_EMPTY;
8442   Feld[newx][newy] = element;
8443   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8444
8445   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8446   {
8447     element = Feld[newx][newy] = EL_ACID;
8448   }
8449   else if (element == EL_MOLE)
8450   {
8451     Feld[x][y] = EL_SAND;
8452
8453     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8454   }
8455   else if (element == EL_QUICKSAND_FILLING)
8456   {
8457     element = Feld[newx][newy] = get_next_element(element);
8458     Store[newx][newy] = Store[x][y];
8459   }
8460   else if (element == EL_QUICKSAND_EMPTYING)
8461   {
8462     Feld[x][y] = get_next_element(element);
8463     element = Feld[newx][newy] = Store[x][y];
8464   }
8465   else if (element == EL_QUICKSAND_FAST_FILLING)
8466   {
8467     element = Feld[newx][newy] = get_next_element(element);
8468     Store[newx][newy] = Store[x][y];
8469   }
8470   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8471   {
8472     Feld[x][y] = get_next_element(element);
8473     element = Feld[newx][newy] = Store[x][y];
8474   }
8475   else if (element == EL_MAGIC_WALL_FILLING)
8476   {
8477     element = Feld[newx][newy] = get_next_element(element);
8478     if (!game.magic_wall_active)
8479       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8480     Store[newx][newy] = Store[x][y];
8481   }
8482   else if (element == EL_MAGIC_WALL_EMPTYING)
8483   {
8484     Feld[x][y] = get_next_element(element);
8485     if (!game.magic_wall_active)
8486       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8487     element = Feld[newx][newy] = Store[x][y];
8488
8489     InitField(newx, newy, FALSE);
8490   }
8491   else if (element == EL_BD_MAGIC_WALL_FILLING)
8492   {
8493     element = Feld[newx][newy] = get_next_element(element);
8494     if (!game.magic_wall_active)
8495       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8496     Store[newx][newy] = Store[x][y];
8497   }
8498   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8499   {
8500     Feld[x][y] = get_next_element(element);
8501     if (!game.magic_wall_active)
8502       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8503     element = Feld[newx][newy] = Store[x][y];
8504
8505     InitField(newx, newy, FALSE);
8506   }
8507   else if (element == EL_DC_MAGIC_WALL_FILLING)
8508   {
8509     element = Feld[newx][newy] = get_next_element(element);
8510     if (!game.magic_wall_active)
8511       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8512     Store[newx][newy] = Store[x][y];
8513   }
8514   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8515   {
8516     Feld[x][y] = get_next_element(element);
8517     if (!game.magic_wall_active)
8518       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8519     element = Feld[newx][newy] = Store[x][y];
8520
8521     InitField(newx, newy, FALSE);
8522   }
8523   else if (element == EL_AMOEBA_DROPPING)
8524   {
8525     Feld[x][y] = get_next_element(element);
8526     element = Feld[newx][newy] = Store[x][y];
8527   }
8528   else if (element == EL_SOKOBAN_OBJECT)
8529   {
8530     if (Back[x][y])
8531       Feld[x][y] = Back[x][y];
8532
8533     if (Back[newx][newy])
8534       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8535
8536     Back[x][y] = Back[newx][newy] = 0;
8537   }
8538
8539   Store[x][y] = EL_EMPTY;
8540   MovPos[x][y] = 0;
8541   MovDir[x][y] = 0;
8542   MovDelay[x][y] = 0;
8543
8544   MovDelay[newx][newy] = 0;
8545
8546   if (CAN_CHANGE_OR_HAS_ACTION(element))
8547   {
8548     // copy element change control values to new field
8549     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8550     ChangePage[newx][newy]  = ChangePage[x][y];
8551     ChangeCount[newx][newy] = ChangeCount[x][y];
8552     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8553   }
8554
8555   CustomValue[newx][newy] = CustomValue[x][y];
8556
8557   ChangeDelay[x][y] = 0;
8558   ChangePage[x][y] = -1;
8559   ChangeCount[x][y] = 0;
8560   ChangeEvent[x][y] = -1;
8561
8562   CustomValue[x][y] = 0;
8563
8564   // copy animation control values to new field
8565   GfxFrame[newx][newy]  = GfxFrame[x][y];
8566   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8567   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8568   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8569
8570   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8571
8572   // some elements can leave other elements behind after moving
8573   if (ei->move_leave_element != EL_EMPTY &&
8574       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8575       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8576   {
8577     int move_leave_element = ei->move_leave_element;
8578
8579     // this makes it possible to leave the removed element again
8580     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8581       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8582
8583     Feld[x][y] = move_leave_element;
8584
8585     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8586       MovDir[x][y] = direction;
8587
8588     InitField(x, y, FALSE);
8589
8590     if (GFX_CRUMBLED(Feld[x][y]))
8591       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8592
8593     if (ELEM_IS_PLAYER(move_leave_element))
8594       RelocatePlayer(x, y, move_leave_element);
8595   }
8596
8597   // do this after checking for left-behind element
8598   ResetGfxAnimation(x, y);      // reset animation values for old field
8599
8600   if (!CAN_MOVE(element) ||
8601       (CAN_FALL(element) && direction == MV_DOWN &&
8602        (element == EL_SPRING ||
8603         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8604         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8605     GfxDir[x][y] = MovDir[newx][newy] = 0;
8606
8607   TEST_DrawLevelField(x, y);
8608   TEST_DrawLevelField(newx, newy);
8609
8610   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8611
8612   // prevent pushed element from moving on in pushed direction
8613   if (pushed_by_player && CAN_MOVE(element) &&
8614       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8615       !(element_info[element].move_pattern & direction))
8616     TurnRound(newx, newy);
8617
8618   // prevent elements on conveyor belt from moving on in last direction
8619   if (pushed_by_conveyor && CAN_FALL(element) &&
8620       direction & MV_HORIZONTAL)
8621     MovDir[newx][newy] = 0;
8622
8623   if (!pushed_by_player)
8624   {
8625     int nextx = newx + dx, nexty = newy + dy;
8626     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8627
8628     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8629
8630     if (CAN_FALL(element) && direction == MV_DOWN)
8631       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8632
8633     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8634       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8635
8636     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8637       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8638   }
8639
8640   if (DONT_TOUCH(element))      // object may be nasty to player or others
8641   {
8642     TestIfBadThingTouchesPlayer(newx, newy);
8643     TestIfBadThingTouchesFriend(newx, newy);
8644
8645     if (!IS_CUSTOM_ELEMENT(element))
8646       TestIfBadThingTouchesOtherBadThing(newx, newy);
8647   }
8648   else if (element == EL_PENGUIN)
8649     TestIfFriendTouchesBadThing(newx, newy);
8650
8651   if (DONT_GET_HIT_BY(element))
8652   {
8653     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8654   }
8655
8656   // give the player one last chance (one more frame) to move away
8657   if (CAN_FALL(element) && direction == MV_DOWN &&
8658       (last_line || (!IS_FREE(x, newy + 1) &&
8659                      (!IS_PLAYER(x, newy + 1) ||
8660                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8661     Impact(x, newy);
8662
8663   if (pushed_by_player && !game.use_change_when_pushing_bug)
8664   {
8665     int push_side = MV_DIR_OPPOSITE(direction);
8666     struct PlayerInfo *player = PLAYERINFO(x, y);
8667
8668     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8669                                player->index_bit, push_side);
8670     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8671                                         player->index_bit, push_side);
8672   }
8673
8674   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8675     MovDelay[newx][newy] = 1;
8676
8677   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8678
8679   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8680   TestIfElementHitsCustomElement(newx, newy, direction);
8681   TestIfPlayerTouchesCustomElement(newx, newy);
8682   TestIfElementTouchesCustomElement(newx, newy);
8683
8684   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8685       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8686     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8687                              MV_DIR_OPPOSITE(direction));
8688 }
8689
8690 int AmoebeNachbarNr(int ax, int ay)
8691 {
8692   int i;
8693   int element = Feld[ax][ay];
8694   int group_nr = 0;
8695   static int xy[4][2] =
8696   {
8697     { 0, -1 },
8698     { -1, 0 },
8699     { +1, 0 },
8700     { 0, +1 }
8701   };
8702
8703   for (i = 0; i < NUM_DIRECTIONS; i++)
8704   {
8705     int x = ax + xy[i][0];
8706     int y = ay + xy[i][1];
8707
8708     if (!IN_LEV_FIELD(x, y))
8709       continue;
8710
8711     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8712       group_nr = AmoebaNr[x][y];
8713   }
8714
8715   return group_nr;
8716 }
8717
8718 static void AmoebenVereinigen(int ax, int ay)
8719 {
8720   int i, x, y, xx, yy;
8721   int new_group_nr = AmoebaNr[ax][ay];
8722   static int xy[4][2] =
8723   {
8724     { 0, -1 },
8725     { -1, 0 },
8726     { +1, 0 },
8727     { 0, +1 }
8728   };
8729
8730   if (new_group_nr == 0)
8731     return;
8732
8733   for (i = 0; i < NUM_DIRECTIONS; i++)
8734   {
8735     x = ax + xy[i][0];
8736     y = ay + xy[i][1];
8737
8738     if (!IN_LEV_FIELD(x, y))
8739       continue;
8740
8741     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8742          Feld[x][y] == EL_BD_AMOEBA ||
8743          Feld[x][y] == EL_AMOEBA_DEAD) &&
8744         AmoebaNr[x][y] != new_group_nr)
8745     {
8746       int old_group_nr = AmoebaNr[x][y];
8747
8748       if (old_group_nr == 0)
8749         return;
8750
8751       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8752       AmoebaCnt[old_group_nr] = 0;
8753       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8754       AmoebaCnt2[old_group_nr] = 0;
8755
8756       SCAN_PLAYFIELD(xx, yy)
8757       {
8758         if (AmoebaNr[xx][yy] == old_group_nr)
8759           AmoebaNr[xx][yy] = new_group_nr;
8760       }
8761     }
8762   }
8763 }
8764
8765 void AmoebeUmwandeln(int ax, int ay)
8766 {
8767   int i, x, y;
8768
8769   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8770   {
8771     int group_nr = AmoebaNr[ax][ay];
8772
8773 #ifdef DEBUG
8774     if (group_nr == 0)
8775     {
8776       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8777       printf("AmoebeUmwandeln(): This should never happen!\n");
8778       return;
8779     }
8780 #endif
8781
8782     SCAN_PLAYFIELD(x, y)
8783     {
8784       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8785       {
8786         AmoebaNr[x][y] = 0;
8787         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8788       }
8789     }
8790
8791     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8792                             SND_AMOEBA_TURNING_TO_GEM :
8793                             SND_AMOEBA_TURNING_TO_ROCK));
8794     Bang(ax, ay);
8795   }
8796   else
8797   {
8798     static int xy[4][2] =
8799     {
8800       { 0, -1 },
8801       { -1, 0 },
8802       { +1, 0 },
8803       { 0, +1 }
8804     };
8805
8806     for (i = 0; i < NUM_DIRECTIONS; i++)
8807     {
8808       x = ax + xy[i][0];
8809       y = ay + xy[i][1];
8810
8811       if (!IN_LEV_FIELD(x, y))
8812         continue;
8813
8814       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8815       {
8816         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8817                               SND_AMOEBA_TURNING_TO_GEM :
8818                               SND_AMOEBA_TURNING_TO_ROCK));
8819         Bang(x, y);
8820       }
8821     }
8822   }
8823 }
8824
8825 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8826 {
8827   int x, y;
8828   int group_nr = AmoebaNr[ax][ay];
8829   boolean done = FALSE;
8830
8831 #ifdef DEBUG
8832   if (group_nr == 0)
8833   {
8834     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8835     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8836     return;
8837   }
8838 #endif
8839
8840   SCAN_PLAYFIELD(x, y)
8841   {
8842     if (AmoebaNr[x][y] == group_nr &&
8843         (Feld[x][y] == EL_AMOEBA_DEAD ||
8844          Feld[x][y] == EL_BD_AMOEBA ||
8845          Feld[x][y] == EL_AMOEBA_GROWING))
8846     {
8847       AmoebaNr[x][y] = 0;
8848       Feld[x][y] = new_element;
8849       InitField(x, y, FALSE);
8850       TEST_DrawLevelField(x, y);
8851       done = TRUE;
8852     }
8853   }
8854
8855   if (done)
8856     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8857                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8858                             SND_BD_AMOEBA_TURNING_TO_GEM));
8859 }
8860
8861 static void AmoebeWaechst(int x, int y)
8862 {
8863   static unsigned int sound_delay = 0;
8864   static unsigned int sound_delay_value = 0;
8865
8866   if (!MovDelay[x][y])          // start new growing cycle
8867   {
8868     MovDelay[x][y] = 7;
8869
8870     if (DelayReached(&sound_delay, sound_delay_value))
8871     {
8872       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8873       sound_delay_value = 30;
8874     }
8875   }
8876
8877   if (MovDelay[x][y])           // wait some time before growing bigger
8878   {
8879     MovDelay[x][y]--;
8880     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8881     {
8882       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8883                                            6 - MovDelay[x][y]);
8884
8885       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8886     }
8887
8888     if (!MovDelay[x][y])
8889     {
8890       Feld[x][y] = Store[x][y];
8891       Store[x][y] = 0;
8892       TEST_DrawLevelField(x, y);
8893     }
8894   }
8895 }
8896
8897 static void AmoebaDisappearing(int x, int y)
8898 {
8899   static unsigned int sound_delay = 0;
8900   static unsigned int sound_delay_value = 0;
8901
8902   if (!MovDelay[x][y])          // start new shrinking cycle
8903   {
8904     MovDelay[x][y] = 7;
8905
8906     if (DelayReached(&sound_delay, sound_delay_value))
8907       sound_delay_value = 30;
8908   }
8909
8910   if (MovDelay[x][y])           // wait some time before shrinking
8911   {
8912     MovDelay[x][y]--;
8913     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8914     {
8915       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8916                                            6 - MovDelay[x][y]);
8917
8918       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8919     }
8920
8921     if (!MovDelay[x][y])
8922     {
8923       Feld[x][y] = EL_EMPTY;
8924       TEST_DrawLevelField(x, y);
8925
8926       // don't let mole enter this field in this cycle;
8927       // (give priority to objects falling to this field from above)
8928       Stop[x][y] = TRUE;
8929     }
8930   }
8931 }
8932
8933 static void AmoebeAbleger(int ax, int ay)
8934 {
8935   int i;
8936   int element = Feld[ax][ay];
8937   int graphic = el2img(element);
8938   int newax = ax, neway = ay;
8939   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8940   static int xy[4][2] =
8941   {
8942     { 0, -1 },
8943     { -1, 0 },
8944     { +1, 0 },
8945     { 0, +1 }
8946   };
8947
8948   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8949   {
8950     Feld[ax][ay] = EL_AMOEBA_DEAD;
8951     TEST_DrawLevelField(ax, ay);
8952     return;
8953   }
8954
8955   if (IS_ANIMATED(graphic))
8956     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8957
8958   if (!MovDelay[ax][ay])        // start making new amoeba field
8959     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8960
8961   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8962   {
8963     MovDelay[ax][ay]--;
8964     if (MovDelay[ax][ay])
8965       return;
8966   }
8967
8968   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8969   {
8970     int start = RND(4);
8971     int x = ax + xy[start][0];
8972     int y = ay + xy[start][1];
8973
8974     if (!IN_LEV_FIELD(x, y))
8975       return;
8976
8977     if (IS_FREE(x, y) ||
8978         CAN_GROW_INTO(Feld[x][y]) ||
8979         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8980         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8981     {
8982       newax = x;
8983       neway = y;
8984     }
8985
8986     if (newax == ax && neway == ay)
8987       return;
8988   }
8989   else                          // normal or "filled" (BD style) amoeba
8990   {
8991     int start = RND(4);
8992     boolean waiting_for_player = FALSE;
8993
8994     for (i = 0; i < NUM_DIRECTIONS; i++)
8995     {
8996       int j = (start + i) % 4;
8997       int x = ax + xy[j][0];
8998       int y = ay + xy[j][1];
8999
9000       if (!IN_LEV_FIELD(x, y))
9001         continue;
9002
9003       if (IS_FREE(x, y) ||
9004           CAN_GROW_INTO(Feld[x][y]) ||
9005           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9006           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9007       {
9008         newax = x;
9009         neway = y;
9010         break;
9011       }
9012       else if (IS_PLAYER(x, y))
9013         waiting_for_player = TRUE;
9014     }
9015
9016     if (newax == ax && neway == ay)             // amoeba cannot grow
9017     {
9018       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9019       {
9020         Feld[ax][ay] = EL_AMOEBA_DEAD;
9021         TEST_DrawLevelField(ax, ay);
9022         AmoebaCnt[AmoebaNr[ax][ay]]--;
9023
9024         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9025         {
9026           if (element == EL_AMOEBA_FULL)
9027             AmoebeUmwandeln(ax, ay);
9028           else if (element == EL_BD_AMOEBA)
9029             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9030         }
9031       }
9032       return;
9033     }
9034     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9035     {
9036       // amoeba gets larger by growing in some direction
9037
9038       int new_group_nr = AmoebaNr[ax][ay];
9039
9040 #ifdef DEBUG
9041   if (new_group_nr == 0)
9042   {
9043     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9044     printf("AmoebeAbleger(): This should never happen!\n");
9045     return;
9046   }
9047 #endif
9048
9049       AmoebaNr[newax][neway] = new_group_nr;
9050       AmoebaCnt[new_group_nr]++;
9051       AmoebaCnt2[new_group_nr]++;
9052
9053       // if amoeba touches other amoeba(s) after growing, unify them
9054       AmoebenVereinigen(newax, neway);
9055
9056       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9057       {
9058         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9059         return;
9060       }
9061     }
9062   }
9063
9064   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9065       (neway == lev_fieldy - 1 && newax != ax))
9066   {
9067     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9068     Store[newax][neway] = element;
9069   }
9070   else if (neway == ay || element == EL_EMC_DRIPPER)
9071   {
9072     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9073
9074     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9075   }
9076   else
9077   {
9078     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9079     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9080     Store[ax][ay] = EL_AMOEBA_DROP;
9081     ContinueMoving(ax, ay);
9082     return;
9083   }
9084
9085   TEST_DrawLevelField(newax, neway);
9086 }
9087
9088 static void Life(int ax, int ay)
9089 {
9090   int x1, y1, x2, y2;
9091   int life_time = 40;
9092   int element = Feld[ax][ay];
9093   int graphic = el2img(element);
9094   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9095                          level.biomaze);
9096   boolean changed = FALSE;
9097
9098   if (IS_ANIMATED(graphic))
9099     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9100
9101   if (Stop[ax][ay])
9102     return;
9103
9104   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9105     MovDelay[ax][ay] = life_time;
9106
9107   if (MovDelay[ax][ay])         // wait some time before next cycle
9108   {
9109     MovDelay[ax][ay]--;
9110     if (MovDelay[ax][ay])
9111       return;
9112   }
9113
9114   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9115   {
9116     int xx = ax+x1, yy = ay+y1;
9117     int old_element = Feld[xx][yy];
9118     int num_neighbours = 0;
9119
9120     if (!IN_LEV_FIELD(xx, yy))
9121       continue;
9122
9123     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9124     {
9125       int x = xx+x2, y = yy+y2;
9126
9127       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9128         continue;
9129
9130       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9131       boolean is_neighbour = FALSE;
9132
9133       if (level.use_life_bugs)
9134         is_neighbour =
9135           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9136            (IS_FREE(x, y)                             &&  Stop[x][y]));
9137       else
9138         is_neighbour =
9139           (Last[x][y] == element || is_player_cell);
9140
9141       if (is_neighbour)
9142         num_neighbours++;
9143     }
9144
9145     boolean is_free = FALSE;
9146
9147     if (level.use_life_bugs)
9148       is_free = (IS_FREE(xx, yy));
9149     else
9150       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9151
9152     if (xx == ax && yy == ay)           // field in the middle
9153     {
9154       if (num_neighbours < life_parameter[0] ||
9155           num_neighbours > life_parameter[1])
9156       {
9157         Feld[xx][yy] = EL_EMPTY;
9158         if (Feld[xx][yy] != old_element)
9159           TEST_DrawLevelField(xx, yy);
9160         Stop[xx][yy] = TRUE;
9161         changed = TRUE;
9162       }
9163     }
9164     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9165     {                                   // free border field
9166       if (num_neighbours >= life_parameter[2] &&
9167           num_neighbours <= life_parameter[3])
9168       {
9169         Feld[xx][yy] = element;
9170         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9171         if (Feld[xx][yy] != old_element)
9172           TEST_DrawLevelField(xx, yy);
9173         Stop[xx][yy] = TRUE;
9174         changed = TRUE;
9175       }
9176     }
9177   }
9178
9179   if (changed)
9180     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9181                    SND_GAME_OF_LIFE_GROWING);
9182 }
9183
9184 static void InitRobotWheel(int x, int y)
9185 {
9186   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9187 }
9188
9189 static void RunRobotWheel(int x, int y)
9190 {
9191   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9192 }
9193
9194 static void StopRobotWheel(int x, int y)
9195 {
9196   if (game.robot_wheel_x == x &&
9197       game.robot_wheel_y == y)
9198   {
9199     game.robot_wheel_x = -1;
9200     game.robot_wheel_y = -1;
9201     game.robot_wheel_active = FALSE;
9202   }
9203 }
9204
9205 static void InitTimegateWheel(int x, int y)
9206 {
9207   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9208 }
9209
9210 static void RunTimegateWheel(int x, int y)
9211 {
9212   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9213 }
9214
9215 static void InitMagicBallDelay(int x, int y)
9216 {
9217   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9218 }
9219
9220 static void ActivateMagicBall(int bx, int by)
9221 {
9222   int x, y;
9223
9224   if (level.ball_random)
9225   {
9226     int pos_border = RND(8);    // select one of the eight border elements
9227     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9228     int xx = pos_content % 3;
9229     int yy = pos_content / 3;
9230
9231     x = bx - 1 + xx;
9232     y = by - 1 + yy;
9233
9234     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9235       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9236   }
9237   else
9238   {
9239     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9240     {
9241       int xx = x - bx + 1;
9242       int yy = y - by + 1;
9243
9244       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9245         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9246     }
9247   }
9248
9249   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9250 }
9251
9252 static void CheckExit(int x, int y)
9253 {
9254   if (game.gems_still_needed > 0 ||
9255       game.sokoban_fields_still_needed > 0 ||
9256       game.sokoban_objects_still_needed > 0 ||
9257       game.lights_still_needed > 0)
9258   {
9259     int element = Feld[x][y];
9260     int graphic = el2img(element);
9261
9262     if (IS_ANIMATED(graphic))
9263       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9264
9265     return;
9266   }
9267
9268   // do not re-open exit door closed after last player
9269   if (game.all_players_gone)
9270     return;
9271
9272   Feld[x][y] = EL_EXIT_OPENING;
9273
9274   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9275 }
9276
9277 static void CheckExitEM(int x, int y)
9278 {
9279   if (game.gems_still_needed > 0 ||
9280       game.sokoban_fields_still_needed > 0 ||
9281       game.sokoban_objects_still_needed > 0 ||
9282       game.lights_still_needed > 0)
9283   {
9284     int element = Feld[x][y];
9285     int graphic = el2img(element);
9286
9287     if (IS_ANIMATED(graphic))
9288       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9289
9290     return;
9291   }
9292
9293   // do not re-open exit door closed after last player
9294   if (game.all_players_gone)
9295     return;
9296
9297   Feld[x][y] = EL_EM_EXIT_OPENING;
9298
9299   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9300 }
9301
9302 static void CheckExitSteel(int x, int y)
9303 {
9304   if (game.gems_still_needed > 0 ||
9305       game.sokoban_fields_still_needed > 0 ||
9306       game.sokoban_objects_still_needed > 0 ||
9307       game.lights_still_needed > 0)
9308   {
9309     int element = Feld[x][y];
9310     int graphic = el2img(element);
9311
9312     if (IS_ANIMATED(graphic))
9313       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9314
9315     return;
9316   }
9317
9318   // do not re-open exit door closed after last player
9319   if (game.all_players_gone)
9320     return;
9321
9322   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9323
9324   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9325 }
9326
9327 static void CheckExitSteelEM(int x, int y)
9328 {
9329   if (game.gems_still_needed > 0 ||
9330       game.sokoban_fields_still_needed > 0 ||
9331       game.sokoban_objects_still_needed > 0 ||
9332       game.lights_still_needed > 0)
9333   {
9334     int element = Feld[x][y];
9335     int graphic = el2img(element);
9336
9337     if (IS_ANIMATED(graphic))
9338       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9339
9340     return;
9341   }
9342
9343   // do not re-open exit door closed after last player
9344   if (game.all_players_gone)
9345     return;
9346
9347   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9348
9349   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9350 }
9351
9352 static void CheckExitSP(int x, int y)
9353 {
9354   if (game.gems_still_needed > 0)
9355   {
9356     int element = Feld[x][y];
9357     int graphic = el2img(element);
9358
9359     if (IS_ANIMATED(graphic))
9360       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9361
9362     return;
9363   }
9364
9365   // do not re-open exit door closed after last player
9366   if (game.all_players_gone)
9367     return;
9368
9369   Feld[x][y] = EL_SP_EXIT_OPENING;
9370
9371   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9372 }
9373
9374 static void CloseAllOpenTimegates(void)
9375 {
9376   int x, y;
9377
9378   SCAN_PLAYFIELD(x, y)
9379   {
9380     int element = Feld[x][y];
9381
9382     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9383     {
9384       Feld[x][y] = EL_TIMEGATE_CLOSING;
9385
9386       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9387     }
9388   }
9389 }
9390
9391 static void DrawTwinkleOnField(int x, int y)
9392 {
9393   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9394     return;
9395
9396   if (Feld[x][y] == EL_BD_DIAMOND)
9397     return;
9398
9399   if (MovDelay[x][y] == 0)      // next animation frame
9400     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9401
9402   if (MovDelay[x][y] != 0)      // wait some time before next frame
9403   {
9404     MovDelay[x][y]--;
9405
9406     DrawLevelElementAnimation(x, y, Feld[x][y]);
9407
9408     if (MovDelay[x][y] != 0)
9409     {
9410       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9411                                            10 - MovDelay[x][y]);
9412
9413       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9414     }
9415   }
9416 }
9417
9418 static void MauerWaechst(int x, int y)
9419 {
9420   int delay = 6;
9421
9422   if (!MovDelay[x][y])          // next animation frame
9423     MovDelay[x][y] = 3 * delay;
9424
9425   if (MovDelay[x][y])           // wait some time before next frame
9426   {
9427     MovDelay[x][y]--;
9428
9429     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9430     {
9431       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9432       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9433
9434       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9435     }
9436
9437     if (!MovDelay[x][y])
9438     {
9439       if (MovDir[x][y] == MV_LEFT)
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_RIGHT)
9445       {
9446         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9447           TEST_DrawLevelField(x + 1, y);
9448       }
9449       else if (MovDir[x][y] == MV_UP)
9450       {
9451         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9452           TEST_DrawLevelField(x, y - 1);
9453       }
9454       else
9455       {
9456         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9457           TEST_DrawLevelField(x, y + 1);
9458       }
9459
9460       Feld[x][y] = Store[x][y];
9461       Store[x][y] = 0;
9462       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9463       TEST_DrawLevelField(x, y);
9464     }
9465   }
9466 }
9467
9468 static void MauerAbleger(int ax, int ay)
9469 {
9470   int element = Feld[ax][ay];
9471   int graphic = el2img(element);
9472   boolean oben_frei = FALSE, unten_frei = FALSE;
9473   boolean links_frei = FALSE, rechts_frei = FALSE;
9474   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9475   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9476   boolean new_wall = FALSE;
9477
9478   if (IS_ANIMATED(graphic))
9479     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9480
9481   if (!MovDelay[ax][ay])        // start building new wall
9482     MovDelay[ax][ay] = 6;
9483
9484   if (MovDelay[ax][ay])         // wait some time before building new wall
9485   {
9486     MovDelay[ax][ay]--;
9487     if (MovDelay[ax][ay])
9488       return;
9489   }
9490
9491   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9492     oben_frei = TRUE;
9493   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9494     unten_frei = TRUE;
9495   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9496     links_frei = TRUE;
9497   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9498     rechts_frei = TRUE;
9499
9500   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9501       element == EL_EXPANDABLE_WALL_ANY)
9502   {
9503     if (oben_frei)
9504     {
9505       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9506       Store[ax][ay-1] = element;
9507       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9508       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9509         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9510                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9511       new_wall = TRUE;
9512     }
9513     if (unten_frei)
9514     {
9515       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9516       Store[ax][ay+1] = element;
9517       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9518       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9519         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9520                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9521       new_wall = TRUE;
9522     }
9523   }
9524
9525   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9526       element == EL_EXPANDABLE_WALL_ANY ||
9527       element == EL_EXPANDABLE_WALL ||
9528       element == EL_BD_EXPANDABLE_WALL)
9529   {
9530     if (links_frei)
9531     {
9532       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9533       Store[ax-1][ay] = element;
9534       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9535       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9536         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9537                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9538       new_wall = TRUE;
9539     }
9540
9541     if (rechts_frei)
9542     {
9543       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9544       Store[ax+1][ay] = element;
9545       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9546       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9547         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9548                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9549       new_wall = TRUE;
9550     }
9551   }
9552
9553   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9554     TEST_DrawLevelField(ax, ay);
9555
9556   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9557     oben_massiv = TRUE;
9558   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9559     unten_massiv = TRUE;
9560   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9561     links_massiv = TRUE;
9562   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9563     rechts_massiv = TRUE;
9564
9565   if (((oben_massiv && unten_massiv) ||
9566        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9567        element == EL_EXPANDABLE_WALL) &&
9568       ((links_massiv && rechts_massiv) ||
9569        element == EL_EXPANDABLE_WALL_VERTICAL))
9570     Feld[ax][ay] = EL_WALL;
9571
9572   if (new_wall)
9573     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9574 }
9575
9576 static void MauerAblegerStahl(int ax, int ay)
9577 {
9578   int element = Feld[ax][ay];
9579   int graphic = el2img(element);
9580   boolean oben_frei = FALSE, unten_frei = FALSE;
9581   boolean links_frei = FALSE, rechts_frei = FALSE;
9582   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9583   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9584   boolean new_wall = FALSE;
9585
9586   if (IS_ANIMATED(graphic))
9587     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9588
9589   if (!MovDelay[ax][ay])        // start building new wall
9590     MovDelay[ax][ay] = 6;
9591
9592   if (MovDelay[ax][ay])         // wait some time before building new wall
9593   {
9594     MovDelay[ax][ay]--;
9595     if (MovDelay[ax][ay])
9596       return;
9597   }
9598
9599   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9600     oben_frei = TRUE;
9601   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9602     unten_frei = TRUE;
9603   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9604     links_frei = TRUE;
9605   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9606     rechts_frei = TRUE;
9607
9608   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9609       element == EL_EXPANDABLE_STEELWALL_ANY)
9610   {
9611     if (oben_frei)
9612     {
9613       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9614       Store[ax][ay-1] = element;
9615       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9616       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9617         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9618                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9619       new_wall = TRUE;
9620     }
9621     if (unten_frei)
9622     {
9623       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9624       Store[ax][ay+1] = element;
9625       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9626       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9627         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9628                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9629       new_wall = TRUE;
9630     }
9631   }
9632
9633   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9634       element == EL_EXPANDABLE_STEELWALL_ANY)
9635   {
9636     if (links_frei)
9637     {
9638       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9639       Store[ax-1][ay] = element;
9640       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9641       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9642         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9643                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9644       new_wall = TRUE;
9645     }
9646
9647     if (rechts_frei)
9648     {
9649       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9650       Store[ax+1][ay] = element;
9651       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9652       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9653         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9654                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9655       new_wall = TRUE;
9656     }
9657   }
9658
9659   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9660     oben_massiv = TRUE;
9661   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9662     unten_massiv = TRUE;
9663   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9664     links_massiv = TRUE;
9665   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9666     rechts_massiv = TRUE;
9667
9668   if (((oben_massiv && unten_massiv) ||
9669        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9670       ((links_massiv && rechts_massiv) ||
9671        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9672     Feld[ax][ay] = EL_STEELWALL;
9673
9674   if (new_wall)
9675     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9676 }
9677
9678 static void CheckForDragon(int x, int y)
9679 {
9680   int i, j;
9681   boolean dragon_found = FALSE;
9682   static int xy[4][2] =
9683   {
9684     { 0, -1 },
9685     { -1, 0 },
9686     { +1, 0 },
9687     { 0, +1 }
9688   };
9689
9690   for (i = 0; i < NUM_DIRECTIONS; i++)
9691   {
9692     for (j = 0; j < 4; j++)
9693     {
9694       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9695
9696       if (IN_LEV_FIELD(xx, yy) &&
9697           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9698       {
9699         if (Feld[xx][yy] == EL_DRAGON)
9700           dragon_found = TRUE;
9701       }
9702       else
9703         break;
9704     }
9705   }
9706
9707   if (!dragon_found)
9708   {
9709     for (i = 0; i < NUM_DIRECTIONS; i++)
9710     {
9711       for (j = 0; j < 3; j++)
9712       {
9713         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9714   
9715         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9716         {
9717           Feld[xx][yy] = EL_EMPTY;
9718           TEST_DrawLevelField(xx, yy);
9719         }
9720         else
9721           break;
9722       }
9723     }
9724   }
9725 }
9726
9727 static void InitBuggyBase(int x, int y)
9728 {
9729   int element = Feld[x][y];
9730   int activating_delay = FRAMES_PER_SECOND / 4;
9731
9732   ChangeDelay[x][y] =
9733     (element == EL_SP_BUGGY_BASE ?
9734      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9735      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9736      activating_delay :
9737      element == EL_SP_BUGGY_BASE_ACTIVE ?
9738      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9739 }
9740
9741 static void WarnBuggyBase(int x, int y)
9742 {
9743   int i;
9744   static int xy[4][2] =
9745   {
9746     { 0, -1 },
9747     { -1, 0 },
9748     { +1, 0 },
9749     { 0, +1 }
9750   };
9751
9752   for (i = 0; i < NUM_DIRECTIONS; i++)
9753   {
9754     int xx = x + xy[i][0];
9755     int yy = y + xy[i][1];
9756
9757     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9758     {
9759       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9760
9761       break;
9762     }
9763   }
9764 }
9765
9766 static void InitTrap(int x, int y)
9767 {
9768   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9769 }
9770
9771 static void ActivateTrap(int x, int y)
9772 {
9773   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9774 }
9775
9776 static void ChangeActiveTrap(int x, int y)
9777 {
9778   int graphic = IMG_TRAP_ACTIVE;
9779
9780   // if new animation frame was drawn, correct crumbled sand border
9781   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9782     TEST_DrawLevelFieldCrumbled(x, y);
9783 }
9784
9785 static int getSpecialActionElement(int element, int number, int base_element)
9786 {
9787   return (element != EL_EMPTY ? element :
9788           number != -1 ? base_element + number - 1 :
9789           EL_EMPTY);
9790 }
9791
9792 static int getModifiedActionNumber(int value_old, int operator, int operand,
9793                                    int value_min, int value_max)
9794 {
9795   int value_new = (operator == CA_MODE_SET      ? operand :
9796                    operator == CA_MODE_ADD      ? value_old + operand :
9797                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9798                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9799                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9800                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9801                    value_old);
9802
9803   return (value_new < value_min ? value_min :
9804           value_new > value_max ? value_max :
9805           value_new);
9806 }
9807
9808 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9809 {
9810   struct ElementInfo *ei = &element_info[element];
9811   struct ElementChangeInfo *change = &ei->change_page[page];
9812   int target_element = change->target_element;
9813   int action_type = change->action_type;
9814   int action_mode = change->action_mode;
9815   int action_arg = change->action_arg;
9816   int action_element = change->action_element;
9817   int i;
9818
9819   if (!change->has_action)
9820     return;
9821
9822   // ---------- determine action paramater values -----------------------------
9823
9824   int level_time_value =
9825     (level.time > 0 ? TimeLeft :
9826      TimePlayed);
9827
9828   int action_arg_element_raw =
9829     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9830      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9831      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9832      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9833      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9834      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9835      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9836      EL_EMPTY);
9837   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9838
9839   int action_arg_direction =
9840     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9841      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9842      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9843      change->actual_trigger_side :
9844      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9845      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9846      MV_NONE);
9847
9848   int action_arg_number_min =
9849     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9850      CA_ARG_MIN);
9851
9852   int action_arg_number_max =
9853     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9854      action_type == CA_SET_LEVEL_GEMS ? 999 :
9855      action_type == CA_SET_LEVEL_TIME ? 9999 :
9856      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9857      action_type == CA_SET_CE_VALUE ? 9999 :
9858      action_type == CA_SET_CE_SCORE ? 9999 :
9859      CA_ARG_MAX);
9860
9861   int action_arg_number_reset =
9862     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9863      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9864      action_type == CA_SET_LEVEL_TIME ? level.time :
9865      action_type == CA_SET_LEVEL_SCORE ? 0 :
9866      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9867      action_type == CA_SET_CE_SCORE ? 0 :
9868      0);
9869
9870   int action_arg_number =
9871     (action_arg <= CA_ARG_MAX ? action_arg :
9872      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9873      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9874      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9875      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9876      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9877      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9878      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9879      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9880      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9881      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9882      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9883      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9884      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9885      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9886      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9887      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9888      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9889      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9890      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9891      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9892      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9893      -1);
9894
9895   int action_arg_number_old =
9896     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9897      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9898      action_type == CA_SET_LEVEL_SCORE ? game.score :
9899      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9900      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9901      0);
9902
9903   int action_arg_number_new =
9904     getModifiedActionNumber(action_arg_number_old,
9905                             action_mode, action_arg_number,
9906                             action_arg_number_min, action_arg_number_max);
9907
9908   int trigger_player_bits =
9909     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9910      change->actual_trigger_player_bits : change->trigger_player);
9911
9912   int action_arg_player_bits =
9913     (action_arg >= CA_ARG_PLAYER_1 &&
9914      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9915      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9916      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9917      PLAYER_BITS_ANY);
9918
9919   // ---------- execute action  -----------------------------------------------
9920
9921   switch (action_type)
9922   {
9923     case CA_NO_ACTION:
9924     {
9925       return;
9926     }
9927
9928     // ---------- level actions  ----------------------------------------------
9929
9930     case CA_RESTART_LEVEL:
9931     {
9932       game.restart_level = TRUE;
9933
9934       break;
9935     }
9936
9937     case CA_SHOW_ENVELOPE:
9938     {
9939       int element = getSpecialActionElement(action_arg_element,
9940                                             action_arg_number, EL_ENVELOPE_1);
9941
9942       if (IS_ENVELOPE(element))
9943         local_player->show_envelope = element;
9944
9945       break;
9946     }
9947
9948     case CA_SET_LEVEL_TIME:
9949     {
9950       if (level.time > 0)       // only modify limited time value
9951       {
9952         TimeLeft = action_arg_number_new;
9953
9954         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9955
9956         DisplayGameControlValues();
9957
9958         if (!TimeLeft && setup.time_limit)
9959           for (i = 0; i < MAX_PLAYERS; i++)
9960             KillPlayer(&stored_player[i]);
9961       }
9962
9963       break;
9964     }
9965
9966     case CA_SET_LEVEL_SCORE:
9967     {
9968       game.score = action_arg_number_new;
9969
9970       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9971
9972       DisplayGameControlValues();
9973
9974       break;
9975     }
9976
9977     case CA_SET_LEVEL_GEMS:
9978     {
9979       game.gems_still_needed = action_arg_number_new;
9980
9981       game.snapshot.collected_item = TRUE;
9982
9983       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9984
9985       DisplayGameControlValues();
9986
9987       break;
9988     }
9989
9990     case CA_SET_LEVEL_WIND:
9991     {
9992       game.wind_direction = action_arg_direction;
9993
9994       break;
9995     }
9996
9997     case CA_SET_LEVEL_RANDOM_SEED:
9998     {
9999       // ensure that setting a new random seed while playing is predictable
10000       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10001
10002       break;
10003     }
10004
10005     // ---------- player actions  ---------------------------------------------
10006
10007     case CA_MOVE_PLAYER:
10008     case CA_MOVE_PLAYER_NEW:
10009     {
10010       // automatically move to the next field in specified direction
10011       for (i = 0; i < MAX_PLAYERS; i++)
10012         if (trigger_player_bits & (1 << i))
10013           if (action_type == CA_MOVE_PLAYER ||
10014               stored_player[i].MovPos == 0)
10015             stored_player[i].programmed_action = action_arg_direction;
10016
10017       break;
10018     }
10019
10020     case CA_EXIT_PLAYER:
10021     {
10022       for (i = 0; i < MAX_PLAYERS; i++)
10023         if (action_arg_player_bits & (1 << i))
10024           ExitPlayer(&stored_player[i]);
10025
10026       if (game.players_still_needed == 0)
10027         LevelSolved();
10028
10029       break;
10030     }
10031
10032     case CA_KILL_PLAYER:
10033     {
10034       for (i = 0; i < MAX_PLAYERS; i++)
10035         if (action_arg_player_bits & (1 << i))
10036           KillPlayer(&stored_player[i]);
10037
10038       break;
10039     }
10040
10041     case CA_SET_PLAYER_KEYS:
10042     {
10043       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10044       int element = getSpecialActionElement(action_arg_element,
10045                                             action_arg_number, EL_KEY_1);
10046
10047       if (IS_KEY(element))
10048       {
10049         for (i = 0; i < MAX_PLAYERS; i++)
10050         {
10051           if (trigger_player_bits & (1 << i))
10052           {
10053             stored_player[i].key[KEY_NR(element)] = key_state;
10054
10055             DrawGameDoorValues();
10056           }
10057         }
10058       }
10059
10060       break;
10061     }
10062
10063     case CA_SET_PLAYER_SPEED:
10064     {
10065       for (i = 0; i < MAX_PLAYERS; i++)
10066       {
10067         if (trigger_player_bits & (1 << i))
10068         {
10069           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10070
10071           if (action_arg == CA_ARG_SPEED_FASTER &&
10072               stored_player[i].cannot_move)
10073           {
10074             action_arg_number = STEPSIZE_VERY_SLOW;
10075           }
10076           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10077                    action_arg == CA_ARG_SPEED_FASTER)
10078           {
10079             action_arg_number = 2;
10080             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10081                            CA_MODE_MULTIPLY);
10082           }
10083           else if (action_arg == CA_ARG_NUMBER_RESET)
10084           {
10085             action_arg_number = level.initial_player_stepsize[i];
10086           }
10087
10088           move_stepsize =
10089             getModifiedActionNumber(move_stepsize,
10090                                     action_mode,
10091                                     action_arg_number,
10092                                     action_arg_number_min,
10093                                     action_arg_number_max);
10094
10095           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10096         }
10097       }
10098
10099       break;
10100     }
10101
10102     case CA_SET_PLAYER_SHIELD:
10103     {
10104       for (i = 0; i < MAX_PLAYERS; i++)
10105       {
10106         if (trigger_player_bits & (1 << i))
10107         {
10108           if (action_arg == CA_ARG_SHIELD_OFF)
10109           {
10110             stored_player[i].shield_normal_time_left = 0;
10111             stored_player[i].shield_deadly_time_left = 0;
10112           }
10113           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10114           {
10115             stored_player[i].shield_normal_time_left = 999999;
10116           }
10117           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10118           {
10119             stored_player[i].shield_normal_time_left = 999999;
10120             stored_player[i].shield_deadly_time_left = 999999;
10121           }
10122         }
10123       }
10124
10125       break;
10126     }
10127
10128     case CA_SET_PLAYER_GRAVITY:
10129     {
10130       for (i = 0; i < MAX_PLAYERS; i++)
10131       {
10132         if (trigger_player_bits & (1 << i))
10133         {
10134           stored_player[i].gravity =
10135             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10136              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10137              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10138              stored_player[i].gravity);
10139         }
10140       }
10141
10142       break;
10143     }
10144
10145     case CA_SET_PLAYER_ARTWORK:
10146     {
10147       for (i = 0; i < MAX_PLAYERS; i++)
10148       {
10149         if (trigger_player_bits & (1 << i))
10150         {
10151           int artwork_element = action_arg_element;
10152
10153           if (action_arg == CA_ARG_ELEMENT_RESET)
10154             artwork_element =
10155               (level.use_artwork_element[i] ? level.artwork_element[i] :
10156                stored_player[i].element_nr);
10157
10158           if (stored_player[i].artwork_element != artwork_element)
10159             stored_player[i].Frame = 0;
10160
10161           stored_player[i].artwork_element = artwork_element;
10162
10163           SetPlayerWaiting(&stored_player[i], FALSE);
10164
10165           // set number of special actions for bored and sleeping animation
10166           stored_player[i].num_special_action_bored =
10167             get_num_special_action(artwork_element,
10168                                    ACTION_BORING_1, ACTION_BORING_LAST);
10169           stored_player[i].num_special_action_sleeping =
10170             get_num_special_action(artwork_element,
10171                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10172         }
10173       }
10174
10175       break;
10176     }
10177
10178     case CA_SET_PLAYER_INVENTORY:
10179     {
10180       for (i = 0; i < MAX_PLAYERS; i++)
10181       {
10182         struct PlayerInfo *player = &stored_player[i];
10183         int j, k;
10184
10185         if (trigger_player_bits & (1 << i))
10186         {
10187           int inventory_element = action_arg_element;
10188
10189           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10190               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10191               action_arg == CA_ARG_ELEMENT_ACTION)
10192           {
10193             int element = inventory_element;
10194             int collect_count = element_info[element].collect_count_initial;
10195
10196             if (!IS_CUSTOM_ELEMENT(element))
10197               collect_count = 1;
10198
10199             if (collect_count == 0)
10200               player->inventory_infinite_element = element;
10201             else
10202               for (k = 0; k < collect_count; k++)
10203                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10204                   player->inventory_element[player->inventory_size++] =
10205                     element;
10206           }
10207           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10208                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10209                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10210           {
10211             if (player->inventory_infinite_element != EL_UNDEFINED &&
10212                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10213                                      action_arg_element_raw))
10214               player->inventory_infinite_element = EL_UNDEFINED;
10215
10216             for (k = 0, j = 0; j < player->inventory_size; j++)
10217             {
10218               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10219                                         action_arg_element_raw))
10220                 player->inventory_element[k++] = player->inventory_element[j];
10221             }
10222
10223             player->inventory_size = k;
10224           }
10225           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10226           {
10227             if (player->inventory_size > 0)
10228             {
10229               for (j = 0; j < player->inventory_size - 1; j++)
10230                 player->inventory_element[j] = player->inventory_element[j + 1];
10231
10232               player->inventory_size--;
10233             }
10234           }
10235           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10236           {
10237             if (player->inventory_size > 0)
10238               player->inventory_size--;
10239           }
10240           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10241           {
10242             player->inventory_infinite_element = EL_UNDEFINED;
10243             player->inventory_size = 0;
10244           }
10245           else if (action_arg == CA_ARG_INVENTORY_RESET)
10246           {
10247             player->inventory_infinite_element = EL_UNDEFINED;
10248             player->inventory_size = 0;
10249
10250             if (level.use_initial_inventory[i])
10251             {
10252               for (j = 0; j < level.initial_inventory_size[i]; j++)
10253               {
10254                 int element = level.initial_inventory_content[i][j];
10255                 int collect_count = element_info[element].collect_count_initial;
10256
10257                 if (!IS_CUSTOM_ELEMENT(element))
10258                   collect_count = 1;
10259
10260                 if (collect_count == 0)
10261                   player->inventory_infinite_element = element;
10262                 else
10263                   for (k = 0; k < collect_count; k++)
10264                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10265                       player->inventory_element[player->inventory_size++] =
10266                         element;
10267               }
10268             }
10269           }
10270         }
10271       }
10272
10273       break;
10274     }
10275
10276     // ---------- CE actions  -------------------------------------------------
10277
10278     case CA_SET_CE_VALUE:
10279     {
10280       int last_ce_value = CustomValue[x][y];
10281
10282       CustomValue[x][y] = action_arg_number_new;
10283
10284       if (CustomValue[x][y] != last_ce_value)
10285       {
10286         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10287         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10288
10289         if (CustomValue[x][y] == 0)
10290         {
10291           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10292           ChangeCount[x][y] = 0;        // allow at least one more change
10293
10294           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10295           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10296         }
10297       }
10298
10299       break;
10300     }
10301
10302     case CA_SET_CE_SCORE:
10303     {
10304       int last_ce_score = ei->collect_score;
10305
10306       ei->collect_score = action_arg_number_new;
10307
10308       if (ei->collect_score != last_ce_score)
10309       {
10310         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10311         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10312
10313         if (ei->collect_score == 0)
10314         {
10315           int xx, yy;
10316
10317           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10318           ChangeCount[x][y] = 0;        // allow at least one more change
10319
10320           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10321           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10322
10323           /*
10324             This is a very special case that seems to be a mixture between
10325             CheckElementChange() and CheckTriggeredElementChange(): while
10326             the first one only affects single elements that are triggered
10327             directly, the second one affects multiple elements in the playfield
10328             that are triggered indirectly by another element. This is a third
10329             case: Changing the CE score always affects multiple identical CEs,
10330             so every affected CE must be checked, not only the single CE for
10331             which the CE score was changed in the first place (as every instance
10332             of that CE shares the same CE score, and therefore also can change)!
10333           */
10334           SCAN_PLAYFIELD(xx, yy)
10335           {
10336             if (Feld[xx][yy] == element)
10337               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10338                                  CE_SCORE_GETS_ZERO);
10339           }
10340         }
10341       }
10342
10343       break;
10344     }
10345
10346     case CA_SET_CE_ARTWORK:
10347     {
10348       int artwork_element = action_arg_element;
10349       boolean reset_frame = FALSE;
10350       int xx, yy;
10351
10352       if (action_arg == CA_ARG_ELEMENT_RESET)
10353         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10354                            element);
10355
10356       if (ei->gfx_element != artwork_element)
10357         reset_frame = TRUE;
10358
10359       ei->gfx_element = artwork_element;
10360
10361       SCAN_PLAYFIELD(xx, yy)
10362       {
10363         if (Feld[xx][yy] == element)
10364         {
10365           if (reset_frame)
10366           {
10367             ResetGfxAnimation(xx, yy);
10368             ResetRandomAnimationValue(xx, yy);
10369           }
10370
10371           TEST_DrawLevelField(xx, yy);
10372         }
10373       }
10374
10375       break;
10376     }
10377
10378     // ---------- engine actions  ---------------------------------------------
10379
10380     case CA_SET_ENGINE_SCAN_MODE:
10381     {
10382       InitPlayfieldScanMode(action_arg);
10383
10384       break;
10385     }
10386
10387     default:
10388       break;
10389   }
10390 }
10391
10392 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10393 {
10394   int old_element = Feld[x][y];
10395   int new_element = GetElementFromGroupElement(element);
10396   int previous_move_direction = MovDir[x][y];
10397   int last_ce_value = CustomValue[x][y];
10398   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10399   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10400   boolean add_player_onto_element = (new_element_is_player &&
10401                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10402                                      IS_WALKABLE(old_element));
10403
10404   if (!add_player_onto_element)
10405   {
10406     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10407       RemoveMovingField(x, y);
10408     else
10409       RemoveField(x, y);
10410
10411     Feld[x][y] = new_element;
10412
10413     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10414       MovDir[x][y] = previous_move_direction;
10415
10416     if (element_info[new_element].use_last_ce_value)
10417       CustomValue[x][y] = last_ce_value;
10418
10419     InitField_WithBug1(x, y, FALSE);
10420
10421     new_element = Feld[x][y];   // element may have changed
10422
10423     ResetGfxAnimation(x, y);
10424     ResetRandomAnimationValue(x, y);
10425
10426     TEST_DrawLevelField(x, y);
10427
10428     if (GFX_CRUMBLED(new_element))
10429       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10430   }
10431
10432   // check if element under the player changes from accessible to unaccessible
10433   // (needed for special case of dropping element which then changes)
10434   // (must be checked after creating new element for walkable group elements)
10435   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10436       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10437   {
10438     Bang(x, y);
10439
10440     return;
10441   }
10442
10443   // "ChangeCount" not set yet to allow "entered by player" change one time
10444   if (new_element_is_player)
10445     RelocatePlayer(x, y, new_element);
10446
10447   if (is_change)
10448     ChangeCount[x][y]++;        // count number of changes in the same frame
10449
10450   TestIfBadThingTouchesPlayer(x, y);
10451   TestIfPlayerTouchesCustomElement(x, y);
10452   TestIfElementTouchesCustomElement(x, y);
10453 }
10454
10455 static void CreateField(int x, int y, int element)
10456 {
10457   CreateFieldExt(x, y, element, FALSE);
10458 }
10459
10460 static void CreateElementFromChange(int x, int y, int element)
10461 {
10462   element = GET_VALID_RUNTIME_ELEMENT(element);
10463
10464   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10465   {
10466     int old_element = Feld[x][y];
10467
10468     // prevent changed element from moving in same engine frame
10469     // unless both old and new element can either fall or move
10470     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10471         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10472       Stop[x][y] = TRUE;
10473   }
10474
10475   CreateFieldExt(x, y, element, TRUE);
10476 }
10477
10478 static boolean ChangeElement(int x, int y, int element, int page)
10479 {
10480   struct ElementInfo *ei = &element_info[element];
10481   struct ElementChangeInfo *change = &ei->change_page[page];
10482   int ce_value = CustomValue[x][y];
10483   int ce_score = ei->collect_score;
10484   int target_element;
10485   int old_element = Feld[x][y];
10486
10487   // always use default change event to prevent running into a loop
10488   if (ChangeEvent[x][y] == -1)
10489     ChangeEvent[x][y] = CE_DELAY;
10490
10491   if (ChangeEvent[x][y] == CE_DELAY)
10492   {
10493     // reset actual trigger element, trigger player and action element
10494     change->actual_trigger_element = EL_EMPTY;
10495     change->actual_trigger_player = EL_EMPTY;
10496     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10497     change->actual_trigger_side = CH_SIDE_NONE;
10498     change->actual_trigger_ce_value = 0;
10499     change->actual_trigger_ce_score = 0;
10500   }
10501
10502   // do not change elements more than a specified maximum number of changes
10503   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10504     return FALSE;
10505
10506   ChangeCount[x][y]++;          // count number of changes in the same frame
10507
10508   if (change->explode)
10509   {
10510     Bang(x, y);
10511
10512     return TRUE;
10513   }
10514
10515   if (change->use_target_content)
10516   {
10517     boolean complete_replace = TRUE;
10518     boolean can_replace[3][3];
10519     int xx, yy;
10520
10521     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10522     {
10523       boolean is_empty;
10524       boolean is_walkable;
10525       boolean is_diggable;
10526       boolean is_collectible;
10527       boolean is_removable;
10528       boolean is_destructible;
10529       int ex = x + xx - 1;
10530       int ey = y + yy - 1;
10531       int content_element = change->target_content.e[xx][yy];
10532       int e;
10533
10534       can_replace[xx][yy] = TRUE;
10535
10536       if (ex == x && ey == y)   // do not check changing element itself
10537         continue;
10538
10539       if (content_element == EL_EMPTY_SPACE)
10540       {
10541         can_replace[xx][yy] = FALSE;    // do not replace border with space
10542
10543         continue;
10544       }
10545
10546       if (!IN_LEV_FIELD(ex, ey))
10547       {
10548         can_replace[xx][yy] = FALSE;
10549         complete_replace = FALSE;
10550
10551         continue;
10552       }
10553
10554       e = Feld[ex][ey];
10555
10556       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10557         e = MovingOrBlocked2Element(ex, ey);
10558
10559       is_empty = (IS_FREE(ex, ey) ||
10560                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10561
10562       is_walkable     = (is_empty || IS_WALKABLE(e));
10563       is_diggable     = (is_empty || IS_DIGGABLE(e));
10564       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10565       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10566       is_removable    = (is_diggable || is_collectible);
10567
10568       can_replace[xx][yy] =
10569         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10570           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10571           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10572           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10573           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10574           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10575          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10576
10577       if (!can_replace[xx][yy])
10578         complete_replace = FALSE;
10579     }
10580
10581     if (!change->only_if_complete || complete_replace)
10582     {
10583       boolean something_has_changed = FALSE;
10584
10585       if (change->only_if_complete && change->use_random_replace &&
10586           RND(100) < change->random_percentage)
10587         return FALSE;
10588
10589       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10590       {
10591         int ex = x + xx - 1;
10592         int ey = y + yy - 1;
10593         int content_element;
10594
10595         if (can_replace[xx][yy] && (!change->use_random_replace ||
10596                                     RND(100) < change->random_percentage))
10597         {
10598           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10599             RemoveMovingField(ex, ey);
10600
10601           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10602
10603           content_element = change->target_content.e[xx][yy];
10604           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10605                                               ce_value, ce_score);
10606
10607           CreateElementFromChange(ex, ey, target_element);
10608
10609           something_has_changed = TRUE;
10610
10611           // for symmetry reasons, freeze newly created border elements
10612           if (ex != x || ey != y)
10613             Stop[ex][ey] = TRUE;        // no more moving in this frame
10614         }
10615       }
10616
10617       if (something_has_changed)
10618       {
10619         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10620         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10621       }
10622     }
10623   }
10624   else
10625   {
10626     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10627                                         ce_value, ce_score);
10628
10629     if (element == EL_DIAGONAL_GROWING ||
10630         element == EL_DIAGONAL_SHRINKING)
10631     {
10632       target_element = Store[x][y];
10633
10634       Store[x][y] = EL_EMPTY;
10635     }
10636
10637     CreateElementFromChange(x, y, target_element);
10638
10639     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10640     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10641   }
10642
10643   // this uses direct change before indirect change
10644   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10645
10646   return TRUE;
10647 }
10648
10649 static void HandleElementChange(int x, int y, int page)
10650 {
10651   int element = MovingOrBlocked2Element(x, y);
10652   struct ElementInfo *ei = &element_info[element];
10653   struct ElementChangeInfo *change = &ei->change_page[page];
10654   boolean handle_action_before_change = FALSE;
10655
10656 #ifdef DEBUG
10657   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10658       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10659   {
10660     printf("\n\n");
10661     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10662            x, y, element, element_info[element].token_name);
10663     printf("HandleElementChange(): This should never happen!\n");
10664     printf("\n\n");
10665   }
10666 #endif
10667
10668   // this can happen with classic bombs on walkable, changing elements
10669   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10670   {
10671     return;
10672   }
10673
10674   if (ChangeDelay[x][y] == 0)           // initialize element change
10675   {
10676     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10677
10678     if (change->can_change)
10679     {
10680       // !!! not clear why graphic animation should be reset at all here !!!
10681       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10682       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10683
10684       /*
10685         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10686
10687         When using an animation frame delay of 1 (this only happens with
10688         "sp_zonk.moving.left/right" in the classic graphics), the default
10689         (non-moving) animation shows wrong animation frames (while the
10690         moving animation, like "sp_zonk.moving.left/right", is correct,
10691         so this graphical bug never shows up with the classic graphics).
10692         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10693         be drawn instead of the correct frames 0,1,2,3. This is caused by
10694         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10695         an element change: First when the change delay ("ChangeDelay[][]")
10696         counter has reached zero after decrementing, then a second time in
10697         the next frame (after "GfxFrame[][]" was already incremented) when
10698         "ChangeDelay[][]" is reset to the initial delay value again.
10699
10700         This causes frame 0 to be drawn twice, while the last frame won't
10701         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10702
10703         As some animations may already be cleverly designed around this bug
10704         (at least the "Snake Bite" snake tail animation does this), it cannot
10705         simply be fixed here without breaking such existing animations.
10706         Unfortunately, it cannot easily be detected if a graphics set was
10707         designed "before" or "after" the bug was fixed. As a workaround,
10708         a new graphics set option "game.graphics_engine_version" was added
10709         to be able to specify the game's major release version for which the
10710         graphics set was designed, which can then be used to decide if the
10711         bugfix should be used (version 4 and above) or not (version 3 or
10712         below, or if no version was specified at all, as with old sets).
10713
10714         (The wrong/fixed animation frames can be tested with the test level set
10715         "test_gfxframe" and level "000", which contains a specially prepared
10716         custom element at level position (x/y) == (11/9) which uses the zonk
10717         animation mentioned above. Using "game.graphics_engine_version: 4"
10718         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10719         This can also be seen from the debug output for this test element.)
10720       */
10721
10722       // when a custom element is about to change (for example by change delay),
10723       // do not reset graphic animation when the custom element is moving
10724       if (game.graphics_engine_version < 4 &&
10725           !IS_MOVING(x, y))
10726       {
10727         ResetGfxAnimation(x, y);
10728         ResetRandomAnimationValue(x, y);
10729       }
10730
10731       if (change->pre_change_function)
10732         change->pre_change_function(x, y);
10733     }
10734   }
10735
10736   ChangeDelay[x][y]--;
10737
10738   if (ChangeDelay[x][y] != 0)           // continue element change
10739   {
10740     if (change->can_change)
10741     {
10742       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10743
10744       if (IS_ANIMATED(graphic))
10745         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10746
10747       if (change->change_function)
10748         change->change_function(x, y);
10749     }
10750   }
10751   else                                  // finish element change
10752   {
10753     if (ChangePage[x][y] != -1)         // remember page from delayed change
10754     {
10755       page = ChangePage[x][y];
10756       ChangePage[x][y] = -1;
10757
10758       change = &ei->change_page[page];
10759     }
10760
10761     if (IS_MOVING(x, y))                // never change a running system ;-)
10762     {
10763       ChangeDelay[x][y] = 1;            // try change after next move step
10764       ChangePage[x][y] = page;          // remember page to use for change
10765
10766       return;
10767     }
10768
10769     // special case: set new level random seed before changing element
10770     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10771       handle_action_before_change = TRUE;
10772
10773     if (change->has_action && handle_action_before_change)
10774       ExecuteCustomElementAction(x, y, element, page);
10775
10776     if (change->can_change)
10777     {
10778       if (ChangeElement(x, y, element, page))
10779       {
10780         if (change->post_change_function)
10781           change->post_change_function(x, y);
10782       }
10783     }
10784
10785     if (change->has_action && !handle_action_before_change)
10786       ExecuteCustomElementAction(x, y, element, page);
10787   }
10788 }
10789
10790 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10791                                               int trigger_element,
10792                                               int trigger_event,
10793                                               int trigger_player,
10794                                               int trigger_side,
10795                                               int trigger_page)
10796 {
10797   boolean change_done_any = FALSE;
10798   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10799   int i;
10800
10801   if (!(trigger_events[trigger_element][trigger_event]))
10802     return FALSE;
10803
10804   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10805
10806   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10807   {
10808     int element = EL_CUSTOM_START + i;
10809     boolean change_done = FALSE;
10810     int p;
10811
10812     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10813         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10814       continue;
10815
10816     for (p = 0; p < element_info[element].num_change_pages; p++)
10817     {
10818       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10819
10820       if (change->can_change_or_has_action &&
10821           change->has_event[trigger_event] &&
10822           change->trigger_side & trigger_side &&
10823           change->trigger_player & trigger_player &&
10824           change->trigger_page & trigger_page_bits &&
10825           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10826       {
10827         change->actual_trigger_element = trigger_element;
10828         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10829         change->actual_trigger_player_bits = trigger_player;
10830         change->actual_trigger_side = trigger_side;
10831         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10832         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10833
10834         if ((change->can_change && !change_done) || change->has_action)
10835         {
10836           int x, y;
10837
10838           SCAN_PLAYFIELD(x, y)
10839           {
10840             if (Feld[x][y] == element)
10841             {
10842               if (change->can_change && !change_done)
10843               {
10844                 // if element already changed in this frame, not only prevent
10845                 // another element change (checked in ChangeElement()), but
10846                 // also prevent additional element actions for this element
10847
10848                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10849                     !level.use_action_after_change_bug)
10850                   continue;
10851
10852                 ChangeDelay[x][y] = 1;
10853                 ChangeEvent[x][y] = trigger_event;
10854
10855                 HandleElementChange(x, y, p);
10856               }
10857               else if (change->has_action)
10858               {
10859                 // if element already changed in this frame, not only prevent
10860                 // another element change (checked in ChangeElement()), but
10861                 // also prevent additional element actions for this element
10862
10863                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10864                     !level.use_action_after_change_bug)
10865                   continue;
10866
10867                 ExecuteCustomElementAction(x, y, element, p);
10868                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10869               }
10870             }
10871           }
10872
10873           if (change->can_change)
10874           {
10875             change_done = TRUE;
10876             change_done_any = TRUE;
10877           }
10878         }
10879       }
10880     }
10881   }
10882
10883   RECURSION_LOOP_DETECTION_END();
10884
10885   return change_done_any;
10886 }
10887
10888 static boolean CheckElementChangeExt(int x, int y,
10889                                      int element,
10890                                      int trigger_element,
10891                                      int trigger_event,
10892                                      int trigger_player,
10893                                      int trigger_side)
10894 {
10895   boolean change_done = FALSE;
10896   int p;
10897
10898   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10899       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10900     return FALSE;
10901
10902   if (Feld[x][y] == EL_BLOCKED)
10903   {
10904     Blocked2Moving(x, y, &x, &y);
10905     element = Feld[x][y];
10906   }
10907
10908   // check if element has already changed or is about to change after moving
10909   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10910        Feld[x][y] != element) ||
10911
10912       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10913        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10914         ChangePage[x][y] != -1)))
10915     return FALSE;
10916
10917   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10918
10919   for (p = 0; p < element_info[element].num_change_pages; p++)
10920   {
10921     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10922
10923     /* check trigger element for all events where the element that is checked
10924        for changing interacts with a directly adjacent element -- this is
10925        different to element changes that affect other elements to change on the
10926        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10927     boolean check_trigger_element =
10928       (trigger_event == CE_TOUCHING_X ||
10929        trigger_event == CE_HITTING_X ||
10930        trigger_event == CE_HIT_BY_X ||
10931        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10932
10933     if (change->can_change_or_has_action &&
10934         change->has_event[trigger_event] &&
10935         change->trigger_side & trigger_side &&
10936         change->trigger_player & trigger_player &&
10937         (!check_trigger_element ||
10938          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10939     {
10940       change->actual_trigger_element = trigger_element;
10941       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10942       change->actual_trigger_player_bits = trigger_player;
10943       change->actual_trigger_side = trigger_side;
10944       change->actual_trigger_ce_value = CustomValue[x][y];
10945       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10946
10947       // special case: trigger element not at (x,y) position for some events
10948       if (check_trigger_element)
10949       {
10950         static struct
10951         {
10952           int dx, dy;
10953         } move_xy[] =
10954           {
10955             {  0,  0 },
10956             { -1,  0 },
10957             { +1,  0 },
10958             {  0,  0 },
10959             {  0, -1 },
10960             {  0,  0 }, { 0, 0 }, { 0, 0 },
10961             {  0, +1 }
10962           };
10963
10964         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10965         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10966
10967         change->actual_trigger_ce_value = CustomValue[xx][yy];
10968         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10969       }
10970
10971       if (change->can_change && !change_done)
10972       {
10973         ChangeDelay[x][y] = 1;
10974         ChangeEvent[x][y] = trigger_event;
10975
10976         HandleElementChange(x, y, p);
10977
10978         change_done = TRUE;
10979       }
10980       else if (change->has_action)
10981       {
10982         ExecuteCustomElementAction(x, y, element, p);
10983         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10984       }
10985     }
10986   }
10987
10988   RECURSION_LOOP_DETECTION_END();
10989
10990   return change_done;
10991 }
10992
10993 static void PlayPlayerSound(struct PlayerInfo *player)
10994 {
10995   int jx = player->jx, jy = player->jy;
10996   int sound_element = player->artwork_element;
10997   int last_action = player->last_action_waiting;
10998   int action = player->action_waiting;
10999
11000   if (player->is_waiting)
11001   {
11002     if (action != last_action)
11003       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11004     else
11005       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11006   }
11007   else
11008   {
11009     if (action != last_action)
11010       StopSound(element_info[sound_element].sound[last_action]);
11011
11012     if (last_action == ACTION_SLEEPING)
11013       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11014   }
11015 }
11016
11017 static void PlayAllPlayersSound(void)
11018 {
11019   int i;
11020
11021   for (i = 0; i < MAX_PLAYERS; i++)
11022     if (stored_player[i].active)
11023       PlayPlayerSound(&stored_player[i]);
11024 }
11025
11026 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11027 {
11028   boolean last_waiting = player->is_waiting;
11029   int move_dir = player->MovDir;
11030
11031   player->dir_waiting = move_dir;
11032   player->last_action_waiting = player->action_waiting;
11033
11034   if (is_waiting)
11035   {
11036     if (!last_waiting)          // not waiting -> waiting
11037     {
11038       player->is_waiting = TRUE;
11039
11040       player->frame_counter_bored =
11041         FrameCounter +
11042         game.player_boring_delay_fixed +
11043         GetSimpleRandom(game.player_boring_delay_random);
11044       player->frame_counter_sleeping =
11045         FrameCounter +
11046         game.player_sleeping_delay_fixed +
11047         GetSimpleRandom(game.player_sleeping_delay_random);
11048
11049       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11050     }
11051
11052     if (game.player_sleeping_delay_fixed +
11053         game.player_sleeping_delay_random > 0 &&
11054         player->anim_delay_counter == 0 &&
11055         player->post_delay_counter == 0 &&
11056         FrameCounter >= player->frame_counter_sleeping)
11057       player->is_sleeping = TRUE;
11058     else if (game.player_boring_delay_fixed +
11059              game.player_boring_delay_random > 0 &&
11060              FrameCounter >= player->frame_counter_bored)
11061       player->is_bored = TRUE;
11062
11063     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11064                               player->is_bored ? ACTION_BORING :
11065                               ACTION_WAITING);
11066
11067     if (player->is_sleeping && player->use_murphy)
11068     {
11069       // special case for sleeping Murphy when leaning against non-free tile
11070
11071       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11072           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11073            !IS_MOVING(player->jx - 1, player->jy)))
11074         move_dir = MV_LEFT;
11075       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11076                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11077                 !IS_MOVING(player->jx + 1, player->jy)))
11078         move_dir = MV_RIGHT;
11079       else
11080         player->is_sleeping = FALSE;
11081
11082       player->dir_waiting = move_dir;
11083     }
11084
11085     if (player->is_sleeping)
11086     {
11087       if (player->num_special_action_sleeping > 0)
11088       {
11089         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11090         {
11091           int last_special_action = player->special_action_sleeping;
11092           int num_special_action = player->num_special_action_sleeping;
11093           int special_action =
11094             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11095              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11096              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11097              last_special_action + 1 : ACTION_SLEEPING);
11098           int special_graphic =
11099             el_act_dir2img(player->artwork_element, special_action, move_dir);
11100
11101           player->anim_delay_counter =
11102             graphic_info[special_graphic].anim_delay_fixed +
11103             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11104           player->post_delay_counter =
11105             graphic_info[special_graphic].post_delay_fixed +
11106             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11107
11108           player->special_action_sleeping = special_action;
11109         }
11110
11111         if (player->anim_delay_counter > 0)
11112         {
11113           player->action_waiting = player->special_action_sleeping;
11114           player->anim_delay_counter--;
11115         }
11116         else if (player->post_delay_counter > 0)
11117         {
11118           player->post_delay_counter--;
11119         }
11120       }
11121     }
11122     else if (player->is_bored)
11123     {
11124       if (player->num_special_action_bored > 0)
11125       {
11126         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11127         {
11128           int special_action =
11129             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11130           int special_graphic =
11131             el_act_dir2img(player->artwork_element, special_action, move_dir);
11132
11133           player->anim_delay_counter =
11134             graphic_info[special_graphic].anim_delay_fixed +
11135             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11136           player->post_delay_counter =
11137             graphic_info[special_graphic].post_delay_fixed +
11138             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11139
11140           player->special_action_bored = special_action;
11141         }
11142
11143         if (player->anim_delay_counter > 0)
11144         {
11145           player->action_waiting = player->special_action_bored;
11146           player->anim_delay_counter--;
11147         }
11148         else if (player->post_delay_counter > 0)
11149         {
11150           player->post_delay_counter--;
11151         }
11152       }
11153     }
11154   }
11155   else if (last_waiting)        // waiting -> not waiting
11156   {
11157     player->is_waiting = FALSE;
11158     player->is_bored = FALSE;
11159     player->is_sleeping = FALSE;
11160
11161     player->frame_counter_bored = -1;
11162     player->frame_counter_sleeping = -1;
11163
11164     player->anim_delay_counter = 0;
11165     player->post_delay_counter = 0;
11166
11167     player->dir_waiting = player->MovDir;
11168     player->action_waiting = ACTION_DEFAULT;
11169
11170     player->special_action_bored = ACTION_DEFAULT;
11171     player->special_action_sleeping = ACTION_DEFAULT;
11172   }
11173 }
11174
11175 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11176 {
11177   if ((!player->is_moving  && player->was_moving) ||
11178       (player->MovPos == 0 && player->was_moving) ||
11179       (player->is_snapping && !player->was_snapping) ||
11180       (player->is_dropping && !player->was_dropping))
11181   {
11182     if (!CheckSaveEngineSnapshotToList())
11183       return;
11184
11185     player->was_moving = FALSE;
11186     player->was_snapping = TRUE;
11187     player->was_dropping = TRUE;
11188   }
11189   else
11190   {
11191     if (player->is_moving)
11192       player->was_moving = TRUE;
11193
11194     if (!player->is_snapping)
11195       player->was_snapping = FALSE;
11196
11197     if (!player->is_dropping)
11198       player->was_dropping = FALSE;
11199   }
11200 }
11201
11202 static void CheckSingleStepMode(struct PlayerInfo *player)
11203 {
11204   if (tape.single_step && tape.recording && !tape.pausing)
11205   {
11206     /* as it is called "single step mode", just return to pause mode when the
11207        player stopped moving after one tile (or never starts moving at all) */
11208     if (!player->is_moving &&
11209         !player->is_pushing &&
11210         !player->is_dropping_pressed)
11211       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11212   }
11213
11214   CheckSaveEngineSnapshot(player);
11215 }
11216
11217 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11218 {
11219   int left      = player_action & JOY_LEFT;
11220   int right     = player_action & JOY_RIGHT;
11221   int up        = player_action & JOY_UP;
11222   int down      = player_action & JOY_DOWN;
11223   int button1   = player_action & JOY_BUTTON_1;
11224   int button2   = player_action & JOY_BUTTON_2;
11225   int dx        = (left ? -1 : right ? 1 : 0);
11226   int dy        = (up   ? -1 : down  ? 1 : 0);
11227
11228   if (!player->active || tape.pausing)
11229     return 0;
11230
11231   if (player_action)
11232   {
11233     if (button1)
11234       SnapField(player, dx, dy);
11235     else
11236     {
11237       if (button2)
11238         DropElement(player);
11239
11240       MovePlayer(player, dx, dy);
11241     }
11242
11243     CheckSingleStepMode(player);
11244
11245     SetPlayerWaiting(player, FALSE);
11246
11247     return player_action;
11248   }
11249   else
11250   {
11251     // no actions for this player (no input at player's configured device)
11252
11253     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11254     SnapField(player, 0, 0);
11255     CheckGravityMovementWhenNotMoving(player);
11256
11257     if (player->MovPos == 0)
11258       SetPlayerWaiting(player, TRUE);
11259
11260     if (player->MovPos == 0)    // needed for tape.playing
11261       player->is_moving = FALSE;
11262
11263     player->is_dropping = FALSE;
11264     player->is_dropping_pressed = FALSE;
11265     player->drop_pressed_delay = 0;
11266
11267     CheckSingleStepMode(player);
11268
11269     return 0;
11270   }
11271 }
11272
11273 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11274                                          byte *tape_action)
11275 {
11276   if (!tape.use_mouse_actions)
11277     return;
11278
11279   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11280   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11281   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11282 }
11283
11284 static void SetTapeActionFromMouseAction(byte *tape_action,
11285                                          struct MouseActionInfo *mouse_action)
11286 {
11287   if (!tape.use_mouse_actions)
11288     return;
11289
11290   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11291   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11292   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11293 }
11294
11295 static void CheckLevelSolved(void)
11296 {
11297   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11298   {
11299     if (game_em.level_solved &&
11300         !game_em.game_over)                             // game won
11301     {
11302       LevelSolved();
11303
11304       game_em.game_over = TRUE;
11305
11306       game.all_players_gone = TRUE;
11307     }
11308
11309     if (game_em.game_over)                              // game lost
11310       game.all_players_gone = TRUE;
11311   }
11312   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11313   {
11314     if (game_sp.level_solved &&
11315         !game_sp.game_over)                             // game won
11316     {
11317       LevelSolved();
11318
11319       game_sp.game_over = TRUE;
11320
11321       game.all_players_gone = TRUE;
11322     }
11323
11324     if (game_sp.game_over)                              // game lost
11325       game.all_players_gone = TRUE;
11326   }
11327   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11328   {
11329     if (game_mm.level_solved &&
11330         !game_mm.game_over)                             // game won
11331     {
11332       LevelSolved();
11333
11334       game_mm.game_over = TRUE;
11335
11336       game.all_players_gone = TRUE;
11337     }
11338
11339     if (game_mm.game_over)                              // game lost
11340       game.all_players_gone = TRUE;
11341   }
11342 }
11343
11344 static void CheckLevelTime(void)
11345 {
11346   int i;
11347
11348   if (TimeFrames >= FRAMES_PER_SECOND)
11349   {
11350     TimeFrames = 0;
11351     TapeTime++;
11352
11353     for (i = 0; i < MAX_PLAYERS; i++)
11354     {
11355       struct PlayerInfo *player = &stored_player[i];
11356
11357       if (SHIELD_ON(player))
11358       {
11359         player->shield_normal_time_left--;
11360
11361         if (player->shield_deadly_time_left > 0)
11362           player->shield_deadly_time_left--;
11363       }
11364     }
11365
11366     if (!game.LevelSolved && !level.use_step_counter)
11367     {
11368       TimePlayed++;
11369
11370       if (TimeLeft > 0)
11371       {
11372         TimeLeft--;
11373
11374         if (TimeLeft <= 10 && setup.time_limit)
11375           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11376
11377         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11378            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11379
11380         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11381
11382         if (!TimeLeft && setup.time_limit)
11383         {
11384           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11385             game_em.lev->killed_out_of_time = TRUE;
11386           else
11387             for (i = 0; i < MAX_PLAYERS; i++)
11388               KillPlayer(&stored_player[i]);
11389         }
11390       }
11391       else if (game.no_time_limit && !game.all_players_gone)
11392       {
11393         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11394       }
11395
11396       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11397     }
11398
11399     if (tape.recording || tape.playing)
11400       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11401   }
11402
11403   if (tape.recording || tape.playing)
11404     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11405
11406   UpdateAndDisplayGameControlValues();
11407 }
11408
11409 void AdvanceFrameAndPlayerCounters(int player_nr)
11410 {
11411   int i;
11412
11413   // advance frame counters (global frame counter and time frame counter)
11414   FrameCounter++;
11415   TimeFrames++;
11416
11417   // advance player counters (counters for move delay, move animation etc.)
11418   for (i = 0; i < MAX_PLAYERS; i++)
11419   {
11420     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11421     int move_delay_value = stored_player[i].move_delay_value;
11422     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11423
11424     if (!advance_player_counters)       // not all players may be affected
11425       continue;
11426
11427     if (move_frames == 0)       // less than one move per game frame
11428     {
11429       int stepsize = TILEX / move_delay_value;
11430       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11431       int count = (stored_player[i].is_moving ?
11432                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11433
11434       if (count % delay == 0)
11435         move_frames = 1;
11436     }
11437
11438     stored_player[i].Frame += move_frames;
11439
11440     if (stored_player[i].MovPos != 0)
11441       stored_player[i].StepFrame += move_frames;
11442
11443     if (stored_player[i].move_delay > 0)
11444       stored_player[i].move_delay--;
11445
11446     // due to bugs in previous versions, counter must count up, not down
11447     if (stored_player[i].push_delay != -1)
11448       stored_player[i].push_delay++;
11449
11450     if (stored_player[i].drop_delay > 0)
11451       stored_player[i].drop_delay--;
11452
11453     if (stored_player[i].is_dropping_pressed)
11454       stored_player[i].drop_pressed_delay++;
11455   }
11456 }
11457
11458 void StartGameActions(boolean init_network_game, boolean record_tape,
11459                       int random_seed)
11460 {
11461   unsigned int new_random_seed = InitRND(random_seed);
11462
11463   if (record_tape)
11464     TapeStartRecording(new_random_seed);
11465
11466   if (init_network_game)
11467   {
11468     SendToServer_LevelFile();
11469     SendToServer_StartPlaying();
11470
11471     return;
11472   }
11473
11474   InitGame();
11475 }
11476
11477 static void GameActionsExt(void)
11478 {
11479 #if 0
11480   static unsigned int game_frame_delay = 0;
11481 #endif
11482   unsigned int game_frame_delay_value;
11483   byte *recorded_player_action;
11484   byte summarized_player_action = 0;
11485   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11486   int i;
11487
11488   // detect endless loops, caused by custom element programming
11489   if (recursion_loop_detected && recursion_loop_depth == 0)
11490   {
11491     char *message = getStringCat3("Internal Error! Element ",
11492                                   EL_NAME(recursion_loop_element),
11493                                   " caused endless loop! Quit the game?");
11494
11495     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11496           EL_NAME(recursion_loop_element));
11497
11498     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11499
11500     recursion_loop_detected = FALSE;    // if game should be continued
11501
11502     free(message);
11503
11504     return;
11505   }
11506
11507   if (game.restart_level)
11508     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11509
11510   CheckLevelSolved();
11511
11512   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11513     GameWon();
11514
11515   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11516     TapeStop();
11517
11518   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11519     return;
11520
11521   game_frame_delay_value =
11522     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11523
11524   if (tape.playing && tape.warp_forward && !tape.pausing)
11525     game_frame_delay_value = 0;
11526
11527   SetVideoFrameDelay(game_frame_delay_value);
11528
11529   // (de)activate virtual buttons depending on current game status
11530   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11531   {
11532     if (game.all_players_gone)  // if no players there to be controlled anymore
11533       SetOverlayActive(FALSE);
11534     else if (!tape.playing)     // if game continues after tape stopped playing
11535       SetOverlayActive(TRUE);
11536   }
11537
11538 #if 0
11539 #if 0
11540   // ---------- main game synchronization point ----------
11541
11542   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11543
11544   printf("::: skip == %d\n", skip);
11545
11546 #else
11547   // ---------- main game synchronization point ----------
11548
11549   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11550 #endif
11551 #endif
11552
11553   if (network_playing && !network_player_action_received)
11554   {
11555     // try to get network player actions in time
11556
11557     // last chance to get network player actions without main loop delay
11558     HandleNetworking();
11559
11560     // game was quit by network peer
11561     if (game_status != GAME_MODE_PLAYING)
11562       return;
11563
11564     // check if network player actions still missing and game still running
11565     if (!network_player_action_received && !checkGameEnded())
11566       return;           // failed to get network player actions in time
11567
11568     // do not yet reset "network_player_action_received" (for tape.pausing)
11569   }
11570
11571   if (tape.pausing)
11572     return;
11573
11574   // at this point we know that we really continue executing the game
11575
11576   network_player_action_received = FALSE;
11577
11578   // when playing tape, read previously recorded player input from tape data
11579   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11580
11581   local_player->effective_mouse_action = local_player->mouse_action;
11582
11583   if (recorded_player_action != NULL)
11584     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11585                                  recorded_player_action);
11586
11587   // TapePlayAction() may return NULL when toggling to "pause before death"
11588   if (tape.pausing)
11589     return;
11590
11591   if (tape.set_centered_player)
11592   {
11593     game.centered_player_nr_next = tape.centered_player_nr_next;
11594     game.set_centered_player = TRUE;
11595   }
11596
11597   for (i = 0; i < MAX_PLAYERS; i++)
11598   {
11599     summarized_player_action |= stored_player[i].action;
11600
11601     if (!network_playing && (game.team_mode || tape.playing))
11602       stored_player[i].effective_action = stored_player[i].action;
11603   }
11604
11605   if (network_playing && !checkGameEnded())
11606     SendToServer_MovePlayer(summarized_player_action);
11607
11608   // summarize all actions at local players mapped input device position
11609   // (this allows using different input devices in single player mode)
11610   if (!network.enabled && !game.team_mode)
11611     stored_player[map_player_action[local_player->index_nr]].effective_action =
11612       summarized_player_action;
11613
11614   // summarize all actions at centered player in local team mode
11615   if (tape.recording &&
11616       setup.team_mode && !network.enabled &&
11617       setup.input_on_focus &&
11618       game.centered_player_nr != -1)
11619   {
11620     for (i = 0; i < MAX_PLAYERS; i++)
11621       stored_player[map_player_action[i]].effective_action =
11622         (i == game.centered_player_nr ? summarized_player_action : 0);
11623   }
11624
11625   if (recorded_player_action != NULL)
11626     for (i = 0; i < MAX_PLAYERS; i++)
11627       stored_player[i].effective_action = recorded_player_action[i];
11628
11629   for (i = 0; i < MAX_PLAYERS; i++)
11630   {
11631     tape_action[i] = stored_player[i].effective_action;
11632
11633     /* (this may happen in the RND game engine if a player was not present on
11634        the playfield on level start, but appeared later from a custom element */
11635     if (setup.team_mode &&
11636         tape.recording &&
11637         tape_action[i] &&
11638         !tape.player_participates[i])
11639       tape.player_participates[i] = TRUE;
11640   }
11641
11642   SetTapeActionFromMouseAction(tape_action,
11643                                &local_player->effective_mouse_action);
11644
11645   // only record actions from input devices, but not programmed actions
11646   if (tape.recording)
11647     TapeRecordAction(tape_action);
11648
11649   // remember if game was played (especially after tape stopped playing)
11650   if (!tape.playing && summarized_player_action)
11651     game.GamePlayed = TRUE;
11652
11653 #if USE_NEW_PLAYER_ASSIGNMENTS
11654   // !!! also map player actions in single player mode !!!
11655   // if (game.team_mode)
11656   if (1)
11657   {
11658     byte mapped_action[MAX_PLAYERS];
11659
11660 #if DEBUG_PLAYER_ACTIONS
11661     printf(":::");
11662     for (i = 0; i < MAX_PLAYERS; i++)
11663       printf(" %d, ", stored_player[i].effective_action);
11664 #endif
11665
11666     for (i = 0; i < MAX_PLAYERS; i++)
11667       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11668
11669     for (i = 0; i < MAX_PLAYERS; i++)
11670       stored_player[i].effective_action = mapped_action[i];
11671
11672 #if DEBUG_PLAYER_ACTIONS
11673     printf(" =>");
11674     for (i = 0; i < MAX_PLAYERS; i++)
11675       printf(" %d, ", stored_player[i].effective_action);
11676     printf("\n");
11677 #endif
11678   }
11679 #if DEBUG_PLAYER_ACTIONS
11680   else
11681   {
11682     printf(":::");
11683     for (i = 0; i < MAX_PLAYERS; i++)
11684       printf(" %d, ", stored_player[i].effective_action);
11685     printf("\n");
11686   }
11687 #endif
11688 #endif
11689
11690   for (i = 0; i < MAX_PLAYERS; i++)
11691   {
11692     // allow engine snapshot in case of changed movement attempt
11693     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11694         (stored_player[i].effective_action & KEY_MOTION))
11695       game.snapshot.changed_action = TRUE;
11696
11697     // allow engine snapshot in case of snapping/dropping attempt
11698     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11699         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11700       game.snapshot.changed_action = TRUE;
11701
11702     game.snapshot.last_action[i] = stored_player[i].effective_action;
11703   }
11704
11705   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11706   {
11707     GameActions_EM_Main();
11708   }
11709   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11710   {
11711     GameActions_SP_Main();
11712   }
11713   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11714   {
11715     GameActions_MM_Main();
11716   }
11717   else
11718   {
11719     GameActions_RND_Main();
11720   }
11721
11722   BlitScreenToBitmap(backbuffer);
11723
11724   CheckLevelSolved();
11725   CheckLevelTime();
11726
11727   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11728
11729   if (global.show_frames_per_second)
11730   {
11731     static unsigned int fps_counter = 0;
11732     static int fps_frames = 0;
11733     unsigned int fps_delay_ms = Counter() - fps_counter;
11734
11735     fps_frames++;
11736
11737     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11738     {
11739       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11740
11741       fps_frames = 0;
11742       fps_counter = Counter();
11743
11744       // always draw FPS to screen after FPS value was updated
11745       redraw_mask |= REDRAW_FPS;
11746     }
11747
11748     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11749     if (GetDrawDeactivationMask() == REDRAW_NONE)
11750       redraw_mask |= REDRAW_FPS;
11751   }
11752 }
11753
11754 static void GameActions_CheckSaveEngineSnapshot(void)
11755 {
11756   if (!game.snapshot.save_snapshot)
11757     return;
11758
11759   // clear flag for saving snapshot _before_ saving snapshot
11760   game.snapshot.save_snapshot = FALSE;
11761
11762   SaveEngineSnapshotToList();
11763 }
11764
11765 void GameActions(void)
11766 {
11767   GameActionsExt();
11768
11769   GameActions_CheckSaveEngineSnapshot();
11770 }
11771
11772 void GameActions_EM_Main(void)
11773 {
11774   byte effective_action[MAX_PLAYERS];
11775   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11776   int i;
11777
11778   for (i = 0; i < MAX_PLAYERS; i++)
11779     effective_action[i] = stored_player[i].effective_action;
11780
11781   GameActions_EM(effective_action, warp_mode);
11782 }
11783
11784 void GameActions_SP_Main(void)
11785 {
11786   byte effective_action[MAX_PLAYERS];
11787   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11788   int i;
11789
11790   for (i = 0; i < MAX_PLAYERS; i++)
11791     effective_action[i] = stored_player[i].effective_action;
11792
11793   GameActions_SP(effective_action, warp_mode);
11794
11795   for (i = 0; i < MAX_PLAYERS; i++)
11796   {
11797     if (stored_player[i].force_dropping)
11798       stored_player[i].action |= KEY_BUTTON_DROP;
11799
11800     stored_player[i].force_dropping = FALSE;
11801   }
11802 }
11803
11804 void GameActions_MM_Main(void)
11805 {
11806   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11807
11808   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11809 }
11810
11811 void GameActions_RND_Main(void)
11812 {
11813   GameActions_RND();
11814 }
11815
11816 void GameActions_RND(void)
11817 {
11818   static struct MouseActionInfo mouse_action_last = { 0 };
11819   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11820   int magic_wall_x = 0, magic_wall_y = 0;
11821   int i, x, y, element, graphic, last_gfx_frame;
11822
11823   InitPlayfieldScanModeVars();
11824
11825   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11826   {
11827     SCAN_PLAYFIELD(x, y)
11828     {
11829       ChangeCount[x][y] = 0;
11830       ChangeEvent[x][y] = -1;
11831     }
11832   }
11833
11834   if (game.set_centered_player)
11835   {
11836     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11837
11838     // switching to "all players" only possible if all players fit to screen
11839     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11840     {
11841       game.centered_player_nr_next = game.centered_player_nr;
11842       game.set_centered_player = FALSE;
11843     }
11844
11845     // do not switch focus to non-existing (or non-active) player
11846     if (game.centered_player_nr_next >= 0 &&
11847         !stored_player[game.centered_player_nr_next].active)
11848     {
11849       game.centered_player_nr_next = game.centered_player_nr;
11850       game.set_centered_player = FALSE;
11851     }
11852   }
11853
11854   if (game.set_centered_player &&
11855       ScreenMovPos == 0)        // screen currently aligned at tile position
11856   {
11857     int sx, sy;
11858
11859     if (game.centered_player_nr_next == -1)
11860     {
11861       setScreenCenteredToAllPlayers(&sx, &sy);
11862     }
11863     else
11864     {
11865       sx = stored_player[game.centered_player_nr_next].jx;
11866       sy = stored_player[game.centered_player_nr_next].jy;
11867     }
11868
11869     game.centered_player_nr = game.centered_player_nr_next;
11870     game.set_centered_player = FALSE;
11871
11872     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11873     DrawGameDoorValues();
11874   }
11875
11876   for (i = 0; i < MAX_PLAYERS; i++)
11877   {
11878     int actual_player_action = stored_player[i].effective_action;
11879
11880 #if 1
11881     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11882        - rnd_equinox_tetrachloride 048
11883        - rnd_equinox_tetrachloride_ii 096
11884        - rnd_emanuel_schmieg 002
11885        - doctor_sloan_ww 001, 020
11886     */
11887     if (stored_player[i].MovPos == 0)
11888       CheckGravityMovement(&stored_player[i]);
11889 #endif
11890
11891     // overwrite programmed action with tape action
11892     if (stored_player[i].programmed_action)
11893       actual_player_action = stored_player[i].programmed_action;
11894
11895     PlayerActions(&stored_player[i], actual_player_action);
11896
11897     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11898   }
11899
11900   ScrollScreen(NULL, SCROLL_GO_ON);
11901
11902   /* for backwards compatibility, the following code emulates a fixed bug that
11903      occured when pushing elements (causing elements that just made their last
11904      pushing step to already (if possible) make their first falling step in the
11905      same game frame, which is bad); this code is also needed to use the famous
11906      "spring push bug" which is used in older levels and might be wanted to be
11907      used also in newer levels, but in this case the buggy pushing code is only
11908      affecting the "spring" element and no other elements */
11909
11910   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11911   {
11912     for (i = 0; i < MAX_PLAYERS; i++)
11913     {
11914       struct PlayerInfo *player = &stored_player[i];
11915       int x = player->jx;
11916       int y = player->jy;
11917
11918       if (player->active && player->is_pushing && player->is_moving &&
11919           IS_MOVING(x, y) &&
11920           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11921            Feld[x][y] == EL_SPRING))
11922       {
11923         ContinueMoving(x, y);
11924
11925         // continue moving after pushing (this is actually a bug)
11926         if (!IS_MOVING(x, y))
11927           Stop[x][y] = FALSE;
11928       }
11929     }
11930   }
11931
11932   SCAN_PLAYFIELD(x, y)
11933   {
11934     Last[x][y] = Feld[x][y];
11935
11936     ChangeCount[x][y] = 0;
11937     ChangeEvent[x][y] = -1;
11938
11939     // this must be handled before main playfield loop
11940     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11941     {
11942       MovDelay[x][y]--;
11943       if (MovDelay[x][y] <= 0)
11944         RemoveField(x, y);
11945     }
11946
11947     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11948     {
11949       MovDelay[x][y]--;
11950       if (MovDelay[x][y] <= 0)
11951       {
11952         RemoveField(x, y);
11953         TEST_DrawLevelField(x, y);
11954
11955         TestIfElementTouchesCustomElement(x, y);        // for empty space
11956       }
11957     }
11958
11959 #if DEBUG
11960     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11961     {
11962       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11963       printf("GameActions(): This should never happen!\n");
11964
11965       ChangePage[x][y] = -1;
11966     }
11967 #endif
11968
11969     Stop[x][y] = FALSE;
11970     if (WasJustMoving[x][y] > 0)
11971       WasJustMoving[x][y]--;
11972     if (WasJustFalling[x][y] > 0)
11973       WasJustFalling[x][y]--;
11974     if (CheckCollision[x][y] > 0)
11975       CheckCollision[x][y]--;
11976     if (CheckImpact[x][y] > 0)
11977       CheckImpact[x][y]--;
11978
11979     GfxFrame[x][y]++;
11980
11981     /* reset finished pushing action (not done in ContinueMoving() to allow
11982        continuous pushing animation for elements with zero push delay) */
11983     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11984     {
11985       ResetGfxAnimation(x, y);
11986       TEST_DrawLevelField(x, y);
11987     }
11988
11989 #if DEBUG
11990     if (IS_BLOCKED(x, y))
11991     {
11992       int oldx, oldy;
11993
11994       Blocked2Moving(x, y, &oldx, &oldy);
11995       if (!IS_MOVING(oldx, oldy))
11996       {
11997         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11998         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11999         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12000         printf("GameActions(): This should never happen!\n");
12001       }
12002     }
12003 #endif
12004   }
12005
12006   if (mouse_action.button)
12007   {
12008     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12009
12010     x = mouse_action.lx;
12011     y = mouse_action.ly;
12012     element = Feld[x][y];
12013
12014     if (new_button)
12015     {
12016       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12017       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12018     }
12019
12020     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12021     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12022   }
12023
12024   SCAN_PLAYFIELD(x, y)
12025   {
12026     element = Feld[x][y];
12027     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12028     last_gfx_frame = GfxFrame[x][y];
12029
12030     ResetGfxFrame(x, y);
12031
12032     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12033       DrawLevelGraphicAnimation(x, y, graphic);
12034
12035     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12036         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12037       ResetRandomAnimationValue(x, y);
12038
12039     SetRandomAnimationValue(x, y);
12040
12041     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12042
12043     if (IS_INACTIVE(element))
12044     {
12045       if (IS_ANIMATED(graphic))
12046         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12047
12048       continue;
12049     }
12050
12051     // this may take place after moving, so 'element' may have changed
12052     if (IS_CHANGING(x, y) &&
12053         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12054     {
12055       int page = element_info[element].event_page_nr[CE_DELAY];
12056
12057       HandleElementChange(x, y, page);
12058
12059       element = Feld[x][y];
12060       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12061     }
12062
12063     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12064     {
12065       StartMoving(x, y);
12066
12067       element = Feld[x][y];
12068       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12069
12070       if (IS_ANIMATED(graphic) &&
12071           !IS_MOVING(x, y) &&
12072           !Stop[x][y])
12073         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12074
12075       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12076         TEST_DrawTwinkleOnField(x, y);
12077     }
12078     else if (element == EL_ACID)
12079     {
12080       if (!Stop[x][y])
12081         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12082     }
12083     else if ((element == EL_EXIT_OPEN ||
12084               element == EL_EM_EXIT_OPEN ||
12085               element == EL_SP_EXIT_OPEN ||
12086               element == EL_STEEL_EXIT_OPEN ||
12087               element == EL_EM_STEEL_EXIT_OPEN ||
12088               element == EL_SP_TERMINAL ||
12089               element == EL_SP_TERMINAL_ACTIVE ||
12090               element == EL_EXTRA_TIME ||
12091               element == EL_SHIELD_NORMAL ||
12092               element == EL_SHIELD_DEADLY) &&
12093              IS_ANIMATED(graphic))
12094       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12095     else if (IS_MOVING(x, y))
12096       ContinueMoving(x, y);
12097     else if (IS_ACTIVE_BOMB(element))
12098       CheckDynamite(x, y);
12099     else if (element == EL_AMOEBA_GROWING)
12100       AmoebeWaechst(x, y);
12101     else if (element == EL_AMOEBA_SHRINKING)
12102       AmoebaDisappearing(x, y);
12103
12104 #if !USE_NEW_AMOEBA_CODE
12105     else if (IS_AMOEBALIVE(element))
12106       AmoebeAbleger(x, y);
12107 #endif
12108
12109     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12110       Life(x, y);
12111     else if (element == EL_EXIT_CLOSED)
12112       CheckExit(x, y);
12113     else if (element == EL_EM_EXIT_CLOSED)
12114       CheckExitEM(x, y);
12115     else if (element == EL_STEEL_EXIT_CLOSED)
12116       CheckExitSteel(x, y);
12117     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12118       CheckExitSteelEM(x, y);
12119     else if (element == EL_SP_EXIT_CLOSED)
12120       CheckExitSP(x, y);
12121     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12122              element == EL_EXPANDABLE_STEELWALL_GROWING)
12123       MauerWaechst(x, y);
12124     else if (element == EL_EXPANDABLE_WALL ||
12125              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12126              element == EL_EXPANDABLE_WALL_VERTICAL ||
12127              element == EL_EXPANDABLE_WALL_ANY ||
12128              element == EL_BD_EXPANDABLE_WALL)
12129       MauerAbleger(x, y);
12130     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12131              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12132              element == EL_EXPANDABLE_STEELWALL_ANY)
12133       MauerAblegerStahl(x, y);
12134     else if (element == EL_FLAMES)
12135       CheckForDragon(x, y);
12136     else if (element == EL_EXPLOSION)
12137       ; // drawing of correct explosion animation is handled separately
12138     else if (element == EL_ELEMENT_SNAPPING ||
12139              element == EL_DIAGONAL_SHRINKING ||
12140              element == EL_DIAGONAL_GROWING)
12141     {
12142       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12143
12144       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12145     }
12146     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12147       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12148
12149     if (IS_BELT_ACTIVE(element))
12150       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12151
12152     if (game.magic_wall_active)
12153     {
12154       int jx = local_player->jx, jy = local_player->jy;
12155
12156       // play the element sound at the position nearest to the player
12157       if ((element == EL_MAGIC_WALL_FULL ||
12158            element == EL_MAGIC_WALL_ACTIVE ||
12159            element == EL_MAGIC_WALL_EMPTYING ||
12160            element == EL_BD_MAGIC_WALL_FULL ||
12161            element == EL_BD_MAGIC_WALL_ACTIVE ||
12162            element == EL_BD_MAGIC_WALL_EMPTYING ||
12163            element == EL_DC_MAGIC_WALL_FULL ||
12164            element == EL_DC_MAGIC_WALL_ACTIVE ||
12165            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12166           ABS(x - jx) + ABS(y - jy) <
12167           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12168       {
12169         magic_wall_x = x;
12170         magic_wall_y = y;
12171       }
12172     }
12173   }
12174
12175 #if USE_NEW_AMOEBA_CODE
12176   // new experimental amoeba growth stuff
12177   if (!(FrameCounter % 8))
12178   {
12179     static unsigned int random = 1684108901;
12180
12181     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12182     {
12183       x = RND(lev_fieldx);
12184       y = RND(lev_fieldy);
12185       element = Feld[x][y];
12186
12187       if (!IS_PLAYER(x,y) &&
12188           (element == EL_EMPTY ||
12189            CAN_GROW_INTO(element) ||
12190            element == EL_QUICKSAND_EMPTY ||
12191            element == EL_QUICKSAND_FAST_EMPTY ||
12192            element == EL_ACID_SPLASH_LEFT ||
12193            element == EL_ACID_SPLASH_RIGHT))
12194       {
12195         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12196             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12197             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12198             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12199           Feld[x][y] = EL_AMOEBA_DROP;
12200       }
12201
12202       random = random * 129 + 1;
12203     }
12204   }
12205 #endif
12206
12207   game.explosions_delayed = FALSE;
12208
12209   SCAN_PLAYFIELD(x, y)
12210   {
12211     element = Feld[x][y];
12212
12213     if (ExplodeField[x][y])
12214       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12215     else if (element == EL_EXPLOSION)
12216       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12217
12218     ExplodeField[x][y] = EX_TYPE_NONE;
12219   }
12220
12221   game.explosions_delayed = TRUE;
12222
12223   if (game.magic_wall_active)
12224   {
12225     if (!(game.magic_wall_time_left % 4))
12226     {
12227       int element = Feld[magic_wall_x][magic_wall_y];
12228
12229       if (element == EL_BD_MAGIC_WALL_FULL ||
12230           element == EL_BD_MAGIC_WALL_ACTIVE ||
12231           element == EL_BD_MAGIC_WALL_EMPTYING)
12232         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12233       else if (element == EL_DC_MAGIC_WALL_FULL ||
12234                element == EL_DC_MAGIC_WALL_ACTIVE ||
12235                element == EL_DC_MAGIC_WALL_EMPTYING)
12236         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12237       else
12238         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12239     }
12240
12241     if (game.magic_wall_time_left > 0)
12242     {
12243       game.magic_wall_time_left--;
12244
12245       if (!game.magic_wall_time_left)
12246       {
12247         SCAN_PLAYFIELD(x, y)
12248         {
12249           element = Feld[x][y];
12250
12251           if (element == EL_MAGIC_WALL_ACTIVE ||
12252               element == EL_MAGIC_WALL_FULL)
12253           {
12254             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12255             TEST_DrawLevelField(x, y);
12256           }
12257           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12258                    element == EL_BD_MAGIC_WALL_FULL)
12259           {
12260             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12261             TEST_DrawLevelField(x, y);
12262           }
12263           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12264                    element == EL_DC_MAGIC_WALL_FULL)
12265           {
12266             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12267             TEST_DrawLevelField(x, y);
12268           }
12269         }
12270
12271         game.magic_wall_active = FALSE;
12272       }
12273     }
12274   }
12275
12276   if (game.light_time_left > 0)
12277   {
12278     game.light_time_left--;
12279
12280     if (game.light_time_left == 0)
12281       RedrawAllLightSwitchesAndInvisibleElements();
12282   }
12283
12284   if (game.timegate_time_left > 0)
12285   {
12286     game.timegate_time_left--;
12287
12288     if (game.timegate_time_left == 0)
12289       CloseAllOpenTimegates();
12290   }
12291
12292   if (game.lenses_time_left > 0)
12293   {
12294     game.lenses_time_left--;
12295
12296     if (game.lenses_time_left == 0)
12297       RedrawAllInvisibleElementsForLenses();
12298   }
12299
12300   if (game.magnify_time_left > 0)
12301   {
12302     game.magnify_time_left--;
12303
12304     if (game.magnify_time_left == 0)
12305       RedrawAllInvisibleElementsForMagnifier();
12306   }
12307
12308   for (i = 0; i < MAX_PLAYERS; i++)
12309   {
12310     struct PlayerInfo *player = &stored_player[i];
12311
12312     if (SHIELD_ON(player))
12313     {
12314       if (player->shield_deadly_time_left)
12315         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12316       else if (player->shield_normal_time_left)
12317         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12318     }
12319   }
12320
12321 #if USE_DELAYED_GFX_REDRAW
12322   SCAN_PLAYFIELD(x, y)
12323   {
12324     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12325     {
12326       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12327          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12328
12329       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12330         DrawLevelField(x, y);
12331
12332       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12333         DrawLevelFieldCrumbled(x, y);
12334
12335       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12336         DrawLevelFieldCrumbledNeighbours(x, y);
12337
12338       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12339         DrawTwinkleOnField(x, y);
12340     }
12341
12342     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12343   }
12344 #endif
12345
12346   DrawAllPlayers();
12347   PlayAllPlayersSound();
12348
12349   for (i = 0; i < MAX_PLAYERS; i++)
12350   {
12351     struct PlayerInfo *player = &stored_player[i];
12352
12353     if (player->show_envelope != 0 && (!player->active ||
12354                                        player->MovPos == 0))
12355     {
12356       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12357
12358       player->show_envelope = 0;
12359     }
12360   }
12361
12362   // use random number generator in every frame to make it less predictable
12363   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12364     RND(1);
12365
12366   mouse_action_last = mouse_action;
12367 }
12368
12369 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12370 {
12371   int min_x = x, min_y = y, max_x = x, max_y = y;
12372   int i;
12373
12374   for (i = 0; i < MAX_PLAYERS; i++)
12375   {
12376     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12377
12378     if (!stored_player[i].active || &stored_player[i] == player)
12379       continue;
12380
12381     min_x = MIN(min_x, jx);
12382     min_y = MIN(min_y, jy);
12383     max_x = MAX(max_x, jx);
12384     max_y = MAX(max_y, jy);
12385   }
12386
12387   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12388 }
12389
12390 static boolean AllPlayersInVisibleScreen(void)
12391 {
12392   int i;
12393
12394   for (i = 0; i < MAX_PLAYERS; i++)
12395   {
12396     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12397
12398     if (!stored_player[i].active)
12399       continue;
12400
12401     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12402       return FALSE;
12403   }
12404
12405   return TRUE;
12406 }
12407
12408 void ScrollLevel(int dx, int dy)
12409 {
12410   int scroll_offset = 2 * TILEX_VAR;
12411   int x, y;
12412
12413   BlitBitmap(drawto_field, drawto_field,
12414              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12415              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12416              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12417              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12418              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12419              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12420
12421   if (dx != 0)
12422   {
12423     x = (dx == 1 ? BX1 : BX2);
12424     for (y = BY1; y <= BY2; y++)
12425       DrawScreenField(x, y);
12426   }
12427
12428   if (dy != 0)
12429   {
12430     y = (dy == 1 ? BY1 : BY2);
12431     for (x = BX1; x <= BX2; x++)
12432       DrawScreenField(x, y);
12433   }
12434
12435   redraw_mask |= REDRAW_FIELD;
12436 }
12437
12438 static boolean canFallDown(struct PlayerInfo *player)
12439 {
12440   int jx = player->jx, jy = player->jy;
12441
12442   return (IN_LEV_FIELD(jx, jy + 1) &&
12443           (IS_FREE(jx, jy + 1) ||
12444            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12445           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12446           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12447 }
12448
12449 static boolean canPassField(int x, int y, int move_dir)
12450 {
12451   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12452   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12453   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12454   int nextx = x + dx;
12455   int nexty = y + dy;
12456   int element = Feld[x][y];
12457
12458   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12459           !CAN_MOVE(element) &&
12460           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12461           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12462           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12463 }
12464
12465 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12466 {
12467   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12468   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12469   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12470   int newx = x + dx;
12471   int newy = y + dy;
12472
12473   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12474           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12475           (IS_DIGGABLE(Feld[newx][newy]) ||
12476            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12477            canPassField(newx, newy, move_dir)));
12478 }
12479
12480 static void CheckGravityMovement(struct PlayerInfo *player)
12481 {
12482   if (player->gravity && !player->programmed_action)
12483   {
12484     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12485     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12486     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12487     int jx = player->jx, jy = player->jy;
12488     boolean player_is_moving_to_valid_field =
12489       (!player_is_snapping &&
12490        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12491         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12492     boolean player_can_fall_down = canFallDown(player);
12493
12494     if (player_can_fall_down &&
12495         !player_is_moving_to_valid_field)
12496       player->programmed_action = MV_DOWN;
12497   }
12498 }
12499
12500 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12501 {
12502   return CheckGravityMovement(player);
12503
12504   if (player->gravity && !player->programmed_action)
12505   {
12506     int jx = player->jx, jy = player->jy;
12507     boolean field_under_player_is_free =
12508       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12509     boolean player_is_standing_on_valid_field =
12510       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12511        (IS_WALKABLE(Feld[jx][jy]) &&
12512         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12513
12514     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12515       player->programmed_action = MV_DOWN;
12516   }
12517 }
12518
12519 /*
12520   MovePlayerOneStep()
12521   -----------------------------------------------------------------------------
12522   dx, dy:               direction (non-diagonal) to try to move the player to
12523   real_dx, real_dy:     direction as read from input device (can be diagonal)
12524 */
12525
12526 boolean MovePlayerOneStep(struct PlayerInfo *player,
12527                           int dx, int dy, int real_dx, int real_dy)
12528 {
12529   int jx = player->jx, jy = player->jy;
12530   int new_jx = jx + dx, new_jy = jy + dy;
12531   int can_move;
12532   boolean player_can_move = !player->cannot_move;
12533
12534   if (!player->active || (!dx && !dy))
12535     return MP_NO_ACTION;
12536
12537   player->MovDir = (dx < 0 ? MV_LEFT :
12538                     dx > 0 ? MV_RIGHT :
12539                     dy < 0 ? MV_UP :
12540                     dy > 0 ? MV_DOWN :  MV_NONE);
12541
12542   if (!IN_LEV_FIELD(new_jx, new_jy))
12543     return MP_NO_ACTION;
12544
12545   if (!player_can_move)
12546   {
12547     if (player->MovPos == 0)
12548     {
12549       player->is_moving = FALSE;
12550       player->is_digging = FALSE;
12551       player->is_collecting = FALSE;
12552       player->is_snapping = FALSE;
12553       player->is_pushing = FALSE;
12554     }
12555   }
12556
12557   if (!network.enabled && game.centered_player_nr == -1 &&
12558       !AllPlayersInSight(player, new_jx, new_jy))
12559     return MP_NO_ACTION;
12560
12561   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12562   if (can_move != MP_MOVING)
12563     return can_move;
12564
12565   // check if DigField() has caused relocation of the player
12566   if (player->jx != jx || player->jy != jy)
12567     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12568
12569   StorePlayer[jx][jy] = 0;
12570   player->last_jx = jx;
12571   player->last_jy = jy;
12572   player->jx = new_jx;
12573   player->jy = new_jy;
12574   StorePlayer[new_jx][new_jy] = player->element_nr;
12575
12576   if (player->move_delay_value_next != -1)
12577   {
12578     player->move_delay_value = player->move_delay_value_next;
12579     player->move_delay_value_next = -1;
12580   }
12581
12582   player->MovPos =
12583     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12584
12585   player->step_counter++;
12586
12587   PlayerVisit[jx][jy] = FrameCounter;
12588
12589   player->is_moving = TRUE;
12590
12591 #if 1
12592   // should better be called in MovePlayer(), but this breaks some tapes
12593   ScrollPlayer(player, SCROLL_INIT);
12594 #endif
12595
12596   return MP_MOVING;
12597 }
12598
12599 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12600 {
12601   int jx = player->jx, jy = player->jy;
12602   int old_jx = jx, old_jy = jy;
12603   int moved = MP_NO_ACTION;
12604
12605   if (!player->active)
12606     return FALSE;
12607
12608   if (!dx && !dy)
12609   {
12610     if (player->MovPos == 0)
12611     {
12612       player->is_moving = FALSE;
12613       player->is_digging = FALSE;
12614       player->is_collecting = FALSE;
12615       player->is_snapping = FALSE;
12616       player->is_pushing = FALSE;
12617     }
12618
12619     return FALSE;
12620   }
12621
12622   if (player->move_delay > 0)
12623     return FALSE;
12624
12625   player->move_delay = -1;              // set to "uninitialized" value
12626
12627   // store if player is automatically moved to next field
12628   player->is_auto_moving = (player->programmed_action != MV_NONE);
12629
12630   // remove the last programmed player action
12631   player->programmed_action = 0;
12632
12633   if (player->MovPos)
12634   {
12635     // should only happen if pre-1.2 tape recordings are played
12636     // this is only for backward compatibility
12637
12638     int original_move_delay_value = player->move_delay_value;
12639
12640 #if DEBUG
12641     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12642            tape.counter);
12643 #endif
12644
12645     // scroll remaining steps with finest movement resolution
12646     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12647
12648     while (player->MovPos)
12649     {
12650       ScrollPlayer(player, SCROLL_GO_ON);
12651       ScrollScreen(NULL, SCROLL_GO_ON);
12652
12653       AdvanceFrameAndPlayerCounters(player->index_nr);
12654
12655       DrawAllPlayers();
12656       BackToFront_WithFrameDelay(0);
12657     }
12658
12659     player->move_delay_value = original_move_delay_value;
12660   }
12661
12662   player->is_active = FALSE;
12663
12664   if (player->last_move_dir & MV_HORIZONTAL)
12665   {
12666     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12667       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12668   }
12669   else
12670   {
12671     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12672       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12673   }
12674
12675   if (!moved && !player->is_active)
12676   {
12677     player->is_moving = FALSE;
12678     player->is_digging = FALSE;
12679     player->is_collecting = FALSE;
12680     player->is_snapping = FALSE;
12681     player->is_pushing = FALSE;
12682   }
12683
12684   jx = player->jx;
12685   jy = player->jy;
12686
12687   if (moved & MP_MOVING && !ScreenMovPos &&
12688       (player->index_nr == game.centered_player_nr ||
12689        game.centered_player_nr == -1))
12690   {
12691     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12692
12693     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12694     {
12695       // actual player has left the screen -- scroll in that direction
12696       if (jx != old_jx)         // player has moved horizontally
12697         scroll_x += (jx - old_jx);
12698       else                      // player has moved vertically
12699         scroll_y += (jy - old_jy);
12700     }
12701     else
12702     {
12703       int offset_raw = game.scroll_delay_value;
12704
12705       if (jx != old_jx)         // player has moved horizontally
12706       {
12707         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12708         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12709         int new_scroll_x = jx - MIDPOSX + offset_x;
12710
12711         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12712             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12713           scroll_x = new_scroll_x;
12714
12715         // don't scroll over playfield boundaries
12716         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12717
12718         // don't scroll more than one field at a time
12719         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12720
12721         // don't scroll against the player's moving direction
12722         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12723             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12724           scroll_x = old_scroll_x;
12725       }
12726       else                      // player has moved vertically
12727       {
12728         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12729         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12730         int new_scroll_y = jy - MIDPOSY + offset_y;
12731
12732         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12733             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12734           scroll_y = new_scroll_y;
12735
12736         // don't scroll over playfield boundaries
12737         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12738
12739         // don't scroll more than one field at a time
12740         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12741
12742         // don't scroll against the player's moving direction
12743         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12744             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12745           scroll_y = old_scroll_y;
12746       }
12747     }
12748
12749     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12750     {
12751       if (!network.enabled && game.centered_player_nr == -1 &&
12752           !AllPlayersInVisibleScreen())
12753       {
12754         scroll_x = old_scroll_x;
12755         scroll_y = old_scroll_y;
12756       }
12757       else
12758       {
12759         ScrollScreen(player, SCROLL_INIT);
12760         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12761       }
12762     }
12763   }
12764
12765   player->StepFrame = 0;
12766
12767   if (moved & MP_MOVING)
12768   {
12769     if (old_jx != jx && old_jy == jy)
12770       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12771     else if (old_jx == jx && old_jy != jy)
12772       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12773
12774     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12775
12776     player->last_move_dir = player->MovDir;
12777     player->is_moving = TRUE;
12778     player->is_snapping = FALSE;
12779     player->is_switching = FALSE;
12780     player->is_dropping = FALSE;
12781     player->is_dropping_pressed = FALSE;
12782     player->drop_pressed_delay = 0;
12783
12784 #if 0
12785     // should better be called here than above, but this breaks some tapes
12786     ScrollPlayer(player, SCROLL_INIT);
12787 #endif
12788   }
12789   else
12790   {
12791     CheckGravityMovementWhenNotMoving(player);
12792
12793     player->is_moving = FALSE;
12794
12795     /* at this point, the player is allowed to move, but cannot move right now
12796        (e.g. because of something blocking the way) -- ensure that the player
12797        is also allowed to move in the next frame (in old versions before 3.1.1,
12798        the player was forced to wait again for eight frames before next try) */
12799
12800     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12801       player->move_delay = 0;   // allow direct movement in the next frame
12802   }
12803
12804   if (player->move_delay == -1)         // not yet initialized by DigField()
12805     player->move_delay = player->move_delay_value;
12806
12807   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12808   {
12809     TestIfPlayerTouchesBadThing(jx, jy);
12810     TestIfPlayerTouchesCustomElement(jx, jy);
12811   }
12812
12813   if (!player->active)
12814     RemovePlayer(player);
12815
12816   return moved;
12817 }
12818
12819 void ScrollPlayer(struct PlayerInfo *player, int mode)
12820 {
12821   int jx = player->jx, jy = player->jy;
12822   int last_jx = player->last_jx, last_jy = player->last_jy;
12823   int move_stepsize = TILEX / player->move_delay_value;
12824
12825   if (!player->active)
12826     return;
12827
12828   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12829     return;
12830
12831   if (mode == SCROLL_INIT)
12832   {
12833     player->actual_frame_counter = FrameCounter;
12834     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12835
12836     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12837         Feld[last_jx][last_jy] == EL_EMPTY)
12838     {
12839       int last_field_block_delay = 0;   // start with no blocking at all
12840       int block_delay_adjustment = player->block_delay_adjustment;
12841
12842       // if player blocks last field, add delay for exactly one move
12843       if (player->block_last_field)
12844       {
12845         last_field_block_delay += player->move_delay_value;
12846
12847         // when blocking enabled, prevent moving up despite gravity
12848         if (player->gravity && player->MovDir == MV_UP)
12849           block_delay_adjustment = -1;
12850       }
12851
12852       // add block delay adjustment (also possible when not blocking)
12853       last_field_block_delay += block_delay_adjustment;
12854
12855       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12856       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12857     }
12858
12859     if (player->MovPos != 0)    // player has not yet reached destination
12860       return;
12861   }
12862   else if (!FrameReached(&player->actual_frame_counter, 1))
12863     return;
12864
12865   if (player->MovPos != 0)
12866   {
12867     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12868     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12869
12870     // before DrawPlayer() to draw correct player graphic for this case
12871     if (player->MovPos == 0)
12872       CheckGravityMovement(player);
12873   }
12874
12875   if (player->MovPos == 0)      // player reached destination field
12876   {
12877     if (player->move_delay_reset_counter > 0)
12878     {
12879       player->move_delay_reset_counter--;
12880
12881       if (player->move_delay_reset_counter == 0)
12882       {
12883         // continue with normal speed after quickly moving through gate
12884         HALVE_PLAYER_SPEED(player);
12885
12886         // be able to make the next move without delay
12887         player->move_delay = 0;
12888       }
12889     }
12890
12891     player->last_jx = jx;
12892     player->last_jy = jy;
12893
12894     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12895         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12896         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12897         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12898         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12899         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12900         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12901         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12902     {
12903       ExitPlayer(player);
12904
12905       if (game.players_still_needed == 0 &&
12906           (game.friends_still_needed == 0 ||
12907            IS_SP_ELEMENT(Feld[jx][jy])))
12908         LevelSolved();
12909     }
12910
12911     // this breaks one level: "machine", level 000
12912     {
12913       int move_direction = player->MovDir;
12914       int enter_side = MV_DIR_OPPOSITE(move_direction);
12915       int leave_side = move_direction;
12916       int old_jx = last_jx;
12917       int old_jy = last_jy;
12918       int old_element = Feld[old_jx][old_jy];
12919       int new_element = Feld[jx][jy];
12920
12921       if (IS_CUSTOM_ELEMENT(old_element))
12922         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12923                                    CE_LEFT_BY_PLAYER,
12924                                    player->index_bit, leave_side);
12925
12926       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12927                                           CE_PLAYER_LEAVES_X,
12928                                           player->index_bit, leave_side);
12929
12930       if (IS_CUSTOM_ELEMENT(new_element))
12931         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12932                                    player->index_bit, enter_side);
12933
12934       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12935                                           CE_PLAYER_ENTERS_X,
12936                                           player->index_bit, enter_side);
12937
12938       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12939                                         CE_MOVE_OF_X, move_direction);
12940     }
12941
12942     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12943     {
12944       TestIfPlayerTouchesBadThing(jx, jy);
12945       TestIfPlayerTouchesCustomElement(jx, jy);
12946
12947       /* needed because pushed element has not yet reached its destination,
12948          so it would trigger a change event at its previous field location */
12949       if (!player->is_pushing)
12950         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12951
12952       if (!player->active)
12953         RemovePlayer(player);
12954     }
12955
12956     if (!game.LevelSolved && level.use_step_counter)
12957     {
12958       int i;
12959
12960       TimePlayed++;
12961
12962       if (TimeLeft > 0)
12963       {
12964         TimeLeft--;
12965
12966         if (TimeLeft <= 10 && setup.time_limit)
12967           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12968
12969         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12970
12971         DisplayGameControlValues();
12972
12973         if (!TimeLeft && setup.time_limit)
12974           for (i = 0; i < MAX_PLAYERS; i++)
12975             KillPlayer(&stored_player[i]);
12976       }
12977       else if (game.no_time_limit && !game.all_players_gone)
12978       {
12979         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12980
12981         DisplayGameControlValues();
12982       }
12983     }
12984
12985     if (tape.single_step && tape.recording && !tape.pausing &&
12986         !player->programmed_action)
12987       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12988
12989     if (!player->programmed_action)
12990       CheckSaveEngineSnapshot(player);
12991   }
12992 }
12993
12994 void ScrollScreen(struct PlayerInfo *player, int mode)
12995 {
12996   static unsigned int screen_frame_counter = 0;
12997
12998   if (mode == SCROLL_INIT)
12999   {
13000     // set scrolling step size according to actual player's moving speed
13001     ScrollStepSize = TILEX / player->move_delay_value;
13002
13003     screen_frame_counter = FrameCounter;
13004     ScreenMovDir = player->MovDir;
13005     ScreenMovPos = player->MovPos;
13006     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13007     return;
13008   }
13009   else if (!FrameReached(&screen_frame_counter, 1))
13010     return;
13011
13012   if (ScreenMovPos)
13013   {
13014     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13015     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13016     redraw_mask |= REDRAW_FIELD;
13017   }
13018   else
13019     ScreenMovDir = MV_NONE;
13020 }
13021
13022 void TestIfPlayerTouchesCustomElement(int x, int y)
13023 {
13024   static int xy[4][2] =
13025   {
13026     { 0, -1 },
13027     { -1, 0 },
13028     { +1, 0 },
13029     { 0, +1 }
13030   };
13031   static int trigger_sides[4][2] =
13032   {
13033     // center side       border side
13034     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13035     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13036     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13037     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13038   };
13039   static int touch_dir[4] =
13040   {
13041     MV_LEFT | MV_RIGHT,
13042     MV_UP   | MV_DOWN,
13043     MV_UP   | MV_DOWN,
13044     MV_LEFT | MV_RIGHT
13045   };
13046   int center_element = Feld[x][y];      // should always be non-moving!
13047   int i;
13048
13049   for (i = 0; i < NUM_DIRECTIONS; i++)
13050   {
13051     int xx = x + xy[i][0];
13052     int yy = y + xy[i][1];
13053     int center_side = trigger_sides[i][0];
13054     int border_side = trigger_sides[i][1];
13055     int border_element;
13056
13057     if (!IN_LEV_FIELD(xx, yy))
13058       continue;
13059
13060     if (IS_PLAYER(x, y))                // player found at center element
13061     {
13062       struct PlayerInfo *player = PLAYERINFO(x, y);
13063
13064       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13065         border_element = Feld[xx][yy];          // may be moving!
13066       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13067         border_element = Feld[xx][yy];
13068       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13069         border_element = MovingOrBlocked2Element(xx, yy);
13070       else
13071         continue;               // center and border element do not touch
13072
13073       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13074                                  player->index_bit, border_side);
13075       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13076                                           CE_PLAYER_TOUCHES_X,
13077                                           player->index_bit, border_side);
13078
13079       {
13080         /* use player element that is initially defined in the level playfield,
13081            not the player element that corresponds to the runtime player number
13082            (example: a level that contains EL_PLAYER_3 as the only player would
13083            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13084         int player_element = PLAYERINFO(x, y)->initial_element;
13085
13086         CheckElementChangeBySide(xx, yy, border_element, player_element,
13087                                  CE_TOUCHING_X, border_side);
13088       }
13089     }
13090     else if (IS_PLAYER(xx, yy))         // player found at border element
13091     {
13092       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13093
13094       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13095       {
13096         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13097           continue;             // center and border element do not touch
13098       }
13099
13100       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13101                                  player->index_bit, center_side);
13102       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13103                                           CE_PLAYER_TOUCHES_X,
13104                                           player->index_bit, center_side);
13105
13106       {
13107         /* use player element that is initially defined in the level playfield,
13108            not the player element that corresponds to the runtime player number
13109            (example: a level that contains EL_PLAYER_3 as the only player would
13110            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13111         int player_element = PLAYERINFO(xx, yy)->initial_element;
13112
13113         CheckElementChangeBySide(x, y, center_element, player_element,
13114                                  CE_TOUCHING_X, center_side);
13115       }
13116
13117       break;
13118     }
13119   }
13120 }
13121
13122 void TestIfElementTouchesCustomElement(int x, int y)
13123 {
13124   static int xy[4][2] =
13125   {
13126     { 0, -1 },
13127     { -1, 0 },
13128     { +1, 0 },
13129     { 0, +1 }
13130   };
13131   static int trigger_sides[4][2] =
13132   {
13133     // center side      border side
13134     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13135     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13136     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13137     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13138   };
13139   static int touch_dir[4] =
13140   {
13141     MV_LEFT | MV_RIGHT,
13142     MV_UP   | MV_DOWN,
13143     MV_UP   | MV_DOWN,
13144     MV_LEFT | MV_RIGHT
13145   };
13146   boolean change_center_element = FALSE;
13147   int center_element = Feld[x][y];      // should always be non-moving!
13148   int border_element_old[NUM_DIRECTIONS];
13149   int i;
13150
13151   for (i = 0; i < NUM_DIRECTIONS; i++)
13152   {
13153     int xx = x + xy[i][0];
13154     int yy = y + xy[i][1];
13155     int border_element;
13156
13157     border_element_old[i] = -1;
13158
13159     if (!IN_LEV_FIELD(xx, yy))
13160       continue;
13161
13162     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13163       border_element = Feld[xx][yy];    // may be moving!
13164     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13165       border_element = Feld[xx][yy];
13166     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13167       border_element = MovingOrBlocked2Element(xx, yy);
13168     else
13169       continue;                 // center and border element do not touch
13170
13171     border_element_old[i] = border_element;
13172   }
13173
13174   for (i = 0; i < NUM_DIRECTIONS; i++)
13175   {
13176     int xx = x + xy[i][0];
13177     int yy = y + xy[i][1];
13178     int center_side = trigger_sides[i][0];
13179     int border_element = border_element_old[i];
13180
13181     if (border_element == -1)
13182       continue;
13183
13184     // check for change of border element
13185     CheckElementChangeBySide(xx, yy, border_element, center_element,
13186                              CE_TOUCHING_X, center_side);
13187
13188     // (center element cannot be player, so we dont have to check this here)
13189   }
13190
13191   for (i = 0; i < NUM_DIRECTIONS; i++)
13192   {
13193     int xx = x + xy[i][0];
13194     int yy = y + xy[i][1];
13195     int border_side = trigger_sides[i][1];
13196     int border_element = border_element_old[i];
13197
13198     if (border_element == -1)
13199       continue;
13200
13201     // check for change of center element (but change it only once)
13202     if (!change_center_element)
13203       change_center_element =
13204         CheckElementChangeBySide(x, y, center_element, border_element,
13205                                  CE_TOUCHING_X, border_side);
13206
13207     if (IS_PLAYER(xx, yy))
13208     {
13209       /* use player element that is initially defined in the level playfield,
13210          not the player element that corresponds to the runtime player number
13211          (example: a level that contains EL_PLAYER_3 as the only player would
13212          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13213       int player_element = PLAYERINFO(xx, yy)->initial_element;
13214
13215       CheckElementChangeBySide(x, y, center_element, player_element,
13216                                CE_TOUCHING_X, border_side);
13217     }
13218   }
13219 }
13220
13221 void TestIfElementHitsCustomElement(int x, int y, int direction)
13222 {
13223   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13224   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13225   int hitx = x + dx, hity = y + dy;
13226   int hitting_element = Feld[x][y];
13227   int touched_element;
13228
13229   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13230     return;
13231
13232   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13233                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13234
13235   if (IN_LEV_FIELD(hitx, hity))
13236   {
13237     int opposite_direction = MV_DIR_OPPOSITE(direction);
13238     int hitting_side = direction;
13239     int touched_side = opposite_direction;
13240     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13241                           MovDir[hitx][hity] != direction ||
13242                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13243
13244     object_hit = TRUE;
13245
13246     if (object_hit)
13247     {
13248       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13249                                CE_HITTING_X, touched_side);
13250
13251       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13252                                CE_HIT_BY_X, hitting_side);
13253
13254       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13255                                CE_HIT_BY_SOMETHING, opposite_direction);
13256
13257       if (IS_PLAYER(hitx, hity))
13258       {
13259         /* use player element that is initially defined in the level playfield,
13260            not the player element that corresponds to the runtime player number
13261            (example: a level that contains EL_PLAYER_3 as the only player would
13262            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13263         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13264
13265         CheckElementChangeBySide(x, y, hitting_element, player_element,
13266                                  CE_HITTING_X, touched_side);
13267       }
13268     }
13269   }
13270
13271   // "hitting something" is also true when hitting the playfield border
13272   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13273                            CE_HITTING_SOMETHING, direction);
13274 }
13275
13276 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13277 {
13278   int i, kill_x = -1, kill_y = -1;
13279
13280   int bad_element = -1;
13281   static int test_xy[4][2] =
13282   {
13283     { 0, -1 },
13284     { -1, 0 },
13285     { +1, 0 },
13286     { 0, +1 }
13287   };
13288   static int test_dir[4] =
13289   {
13290     MV_UP,
13291     MV_LEFT,
13292     MV_RIGHT,
13293     MV_DOWN
13294   };
13295
13296   for (i = 0; i < NUM_DIRECTIONS; i++)
13297   {
13298     int test_x, test_y, test_move_dir, test_element;
13299
13300     test_x = good_x + test_xy[i][0];
13301     test_y = good_y + test_xy[i][1];
13302
13303     if (!IN_LEV_FIELD(test_x, test_y))
13304       continue;
13305
13306     test_move_dir =
13307       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13308
13309     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13310
13311     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13312        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13313     */
13314     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13315         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13316     {
13317       kill_x = test_x;
13318       kill_y = test_y;
13319       bad_element = test_element;
13320
13321       break;
13322     }
13323   }
13324
13325   if (kill_x != -1 || kill_y != -1)
13326   {
13327     if (IS_PLAYER(good_x, good_y))
13328     {
13329       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13330
13331       if (player->shield_deadly_time_left > 0 &&
13332           !IS_INDESTRUCTIBLE(bad_element))
13333         Bang(kill_x, kill_y);
13334       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13335         KillPlayer(player);
13336     }
13337     else
13338       Bang(good_x, good_y);
13339   }
13340 }
13341
13342 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13343 {
13344   int i, kill_x = -1, kill_y = -1;
13345   int bad_element = Feld[bad_x][bad_y];
13346   static int test_xy[4][2] =
13347   {
13348     { 0, -1 },
13349     { -1, 0 },
13350     { +1, 0 },
13351     { 0, +1 }
13352   };
13353   static int touch_dir[4] =
13354   {
13355     MV_LEFT | MV_RIGHT,
13356     MV_UP   | MV_DOWN,
13357     MV_UP   | MV_DOWN,
13358     MV_LEFT | MV_RIGHT
13359   };
13360   static int test_dir[4] =
13361   {
13362     MV_UP,
13363     MV_LEFT,
13364     MV_RIGHT,
13365     MV_DOWN
13366   };
13367
13368   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13369     return;
13370
13371   for (i = 0; i < NUM_DIRECTIONS; i++)
13372   {
13373     int test_x, test_y, test_move_dir, test_element;
13374
13375     test_x = bad_x + test_xy[i][0];
13376     test_y = bad_y + test_xy[i][1];
13377
13378     if (!IN_LEV_FIELD(test_x, test_y))
13379       continue;
13380
13381     test_move_dir =
13382       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13383
13384     test_element = Feld[test_x][test_y];
13385
13386     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13387        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13388     */
13389     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13390         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13391     {
13392       // good thing is player or penguin that does not move away
13393       if (IS_PLAYER(test_x, test_y))
13394       {
13395         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13396
13397         if (bad_element == EL_ROBOT && player->is_moving)
13398           continue;     // robot does not kill player if he is moving
13399
13400         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13401         {
13402           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13403             continue;           // center and border element do not touch
13404         }
13405
13406         kill_x = test_x;
13407         kill_y = test_y;
13408
13409         break;
13410       }
13411       else if (test_element == EL_PENGUIN)
13412       {
13413         kill_x = test_x;
13414         kill_y = test_y;
13415
13416         break;
13417       }
13418     }
13419   }
13420
13421   if (kill_x != -1 || kill_y != -1)
13422   {
13423     if (IS_PLAYER(kill_x, kill_y))
13424     {
13425       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13426
13427       if (player->shield_deadly_time_left > 0 &&
13428           !IS_INDESTRUCTIBLE(bad_element))
13429         Bang(bad_x, bad_y);
13430       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13431         KillPlayer(player);
13432     }
13433     else
13434       Bang(kill_x, kill_y);
13435   }
13436 }
13437
13438 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13439 {
13440   int bad_element = Feld[bad_x][bad_y];
13441   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13442   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13443   int test_x = bad_x + dx, test_y = bad_y + dy;
13444   int test_move_dir, test_element;
13445   int kill_x = -1, kill_y = -1;
13446
13447   if (!IN_LEV_FIELD(test_x, test_y))
13448     return;
13449
13450   test_move_dir =
13451     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13452
13453   test_element = Feld[test_x][test_y];
13454
13455   if (test_move_dir != bad_move_dir)
13456   {
13457     // good thing can be player or penguin that does not move away
13458     if (IS_PLAYER(test_x, test_y))
13459     {
13460       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13461
13462       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13463          player as being hit when he is moving towards the bad thing, because
13464          the "get hit by" condition would be lost after the player stops) */
13465       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13466         return;         // player moves away from bad thing
13467
13468       kill_x = test_x;
13469       kill_y = test_y;
13470     }
13471     else if (test_element == EL_PENGUIN)
13472     {
13473       kill_x = test_x;
13474       kill_y = test_y;
13475     }
13476   }
13477
13478   if (kill_x != -1 || kill_y != -1)
13479   {
13480     if (IS_PLAYER(kill_x, kill_y))
13481     {
13482       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13483
13484       if (player->shield_deadly_time_left > 0 &&
13485           !IS_INDESTRUCTIBLE(bad_element))
13486         Bang(bad_x, bad_y);
13487       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13488         KillPlayer(player);
13489     }
13490     else
13491       Bang(kill_x, kill_y);
13492   }
13493 }
13494
13495 void TestIfPlayerTouchesBadThing(int x, int y)
13496 {
13497   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13498 }
13499
13500 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13501 {
13502   TestIfGoodThingHitsBadThing(x, y, move_dir);
13503 }
13504
13505 void TestIfBadThingTouchesPlayer(int x, int y)
13506 {
13507   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13508 }
13509
13510 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13511 {
13512   TestIfBadThingHitsGoodThing(x, y, move_dir);
13513 }
13514
13515 void TestIfFriendTouchesBadThing(int x, int y)
13516 {
13517   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13518 }
13519
13520 void TestIfBadThingTouchesFriend(int x, int y)
13521 {
13522   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13523 }
13524
13525 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13526 {
13527   int i, kill_x = bad_x, kill_y = bad_y;
13528   static int xy[4][2] =
13529   {
13530     { 0, -1 },
13531     { -1, 0 },
13532     { +1, 0 },
13533     { 0, +1 }
13534   };
13535
13536   for (i = 0; i < NUM_DIRECTIONS; i++)
13537   {
13538     int x, y, element;
13539
13540     x = bad_x + xy[i][0];
13541     y = bad_y + xy[i][1];
13542     if (!IN_LEV_FIELD(x, y))
13543       continue;
13544
13545     element = Feld[x][y];
13546     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13547         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13548     {
13549       kill_x = x;
13550       kill_y = y;
13551       break;
13552     }
13553   }
13554
13555   if (kill_x != bad_x || kill_y != bad_y)
13556     Bang(bad_x, bad_y);
13557 }
13558
13559 void KillPlayer(struct PlayerInfo *player)
13560 {
13561   int jx = player->jx, jy = player->jy;
13562
13563   if (!player->active)
13564     return;
13565
13566 #if 0
13567   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13568          player->killed, player->active, player->reanimated);
13569 #endif
13570
13571   /* the following code was introduced to prevent an infinite loop when calling
13572      -> Bang()
13573      -> CheckTriggeredElementChangeExt()
13574      -> ExecuteCustomElementAction()
13575      -> KillPlayer()
13576      -> (infinitely repeating the above sequence of function calls)
13577      which occurs when killing the player while having a CE with the setting
13578      "kill player X when explosion of <player X>"; the solution using a new
13579      field "player->killed" was chosen for backwards compatibility, although
13580      clever use of the fields "player->active" etc. would probably also work */
13581 #if 1
13582   if (player->killed)
13583     return;
13584 #endif
13585
13586   player->killed = TRUE;
13587
13588   // remove accessible field at the player's position
13589   Feld[jx][jy] = EL_EMPTY;
13590
13591   // deactivate shield (else Bang()/Explode() would not work right)
13592   player->shield_normal_time_left = 0;
13593   player->shield_deadly_time_left = 0;
13594
13595 #if 0
13596   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13597          player->killed, player->active, player->reanimated);
13598 #endif
13599
13600   Bang(jx, jy);
13601
13602 #if 0
13603   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13604          player->killed, player->active, player->reanimated);
13605 #endif
13606
13607   if (player->reanimated)       // killed player may have been reanimated
13608     player->killed = player->reanimated = FALSE;
13609   else
13610     BuryPlayer(player);
13611 }
13612
13613 static void KillPlayerUnlessEnemyProtected(int x, int y)
13614 {
13615   if (!PLAYER_ENEMY_PROTECTED(x, y))
13616     KillPlayer(PLAYERINFO(x, y));
13617 }
13618
13619 static void KillPlayerUnlessExplosionProtected(int x, int y)
13620 {
13621   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13622     KillPlayer(PLAYERINFO(x, y));
13623 }
13624
13625 void BuryPlayer(struct PlayerInfo *player)
13626 {
13627   int jx = player->jx, jy = player->jy;
13628
13629   if (!player->active)
13630     return;
13631
13632   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13633   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13634
13635   RemovePlayer(player);
13636
13637   player->buried = TRUE;
13638
13639   if (game.all_players_gone)
13640     game.GameOver = TRUE;
13641 }
13642
13643 void RemovePlayer(struct PlayerInfo *player)
13644 {
13645   int jx = player->jx, jy = player->jy;
13646   int i, found = FALSE;
13647
13648   player->present = FALSE;
13649   player->active = FALSE;
13650
13651   // required for some CE actions (even if the player is not active anymore)
13652   player->MovPos = 0;
13653
13654   if (!ExplodeField[jx][jy])
13655     StorePlayer[jx][jy] = 0;
13656
13657   if (player->is_moving)
13658     TEST_DrawLevelField(player->last_jx, player->last_jy);
13659
13660   for (i = 0; i < MAX_PLAYERS; i++)
13661     if (stored_player[i].active)
13662       found = TRUE;
13663
13664   if (!found)
13665   {
13666     game.all_players_gone = TRUE;
13667     game.GameOver = TRUE;
13668   }
13669
13670   game.exit_x = game.robot_wheel_x = jx;
13671   game.exit_y = game.robot_wheel_y = jy;
13672 }
13673
13674 void ExitPlayer(struct PlayerInfo *player)
13675 {
13676   DrawPlayer(player);   // needed here only to cleanup last field
13677   RemovePlayer(player);
13678
13679   if (game.players_still_needed > 0)
13680     game.players_still_needed--;
13681 }
13682
13683 static void setFieldForSnapping(int x, int y, int element, int direction)
13684 {
13685   struct ElementInfo *ei = &element_info[element];
13686   int direction_bit = MV_DIR_TO_BIT(direction);
13687   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13688   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13689                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13690
13691   Feld[x][y] = EL_ELEMENT_SNAPPING;
13692   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13693
13694   ResetGfxAnimation(x, y);
13695
13696   GfxElement[x][y] = element;
13697   GfxAction[x][y] = action;
13698   GfxDir[x][y] = direction;
13699   GfxFrame[x][y] = -1;
13700 }
13701
13702 /*
13703   =============================================================================
13704   checkDiagonalPushing()
13705   -----------------------------------------------------------------------------
13706   check if diagonal input device direction results in pushing of object
13707   (by checking if the alternative direction is walkable, diggable, ...)
13708   =============================================================================
13709 */
13710
13711 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13712                                     int x, int y, int real_dx, int real_dy)
13713 {
13714   int jx, jy, dx, dy, xx, yy;
13715
13716   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13717     return TRUE;
13718
13719   // diagonal direction: check alternative direction
13720   jx = player->jx;
13721   jy = player->jy;
13722   dx = x - jx;
13723   dy = y - jy;
13724   xx = jx + (dx == 0 ? real_dx : 0);
13725   yy = jy + (dy == 0 ? real_dy : 0);
13726
13727   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13728 }
13729
13730 /*
13731   =============================================================================
13732   DigField()
13733   -----------------------------------------------------------------------------
13734   x, y:                 field next to player (non-diagonal) to try to dig to
13735   real_dx, real_dy:     direction as read from input device (can be diagonal)
13736   =============================================================================
13737 */
13738
13739 static int DigField(struct PlayerInfo *player,
13740                     int oldx, int oldy, int x, int y,
13741                     int real_dx, int real_dy, int mode)
13742 {
13743   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13744   boolean player_was_pushing = player->is_pushing;
13745   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13746   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13747   int jx = oldx, jy = oldy;
13748   int dx = x - jx, dy = y - jy;
13749   int nextx = x + dx, nexty = y + dy;
13750   int move_direction = (dx == -1 ? MV_LEFT  :
13751                         dx == +1 ? MV_RIGHT :
13752                         dy == -1 ? MV_UP    :
13753                         dy == +1 ? MV_DOWN  : MV_NONE);
13754   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13755   int dig_side = MV_DIR_OPPOSITE(move_direction);
13756   int old_element = Feld[jx][jy];
13757   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13758   int collect_count;
13759
13760   if (is_player)                // function can also be called by EL_PENGUIN
13761   {
13762     if (player->MovPos == 0)
13763     {
13764       player->is_digging = FALSE;
13765       player->is_collecting = FALSE;
13766     }
13767
13768     if (player->MovPos == 0)    // last pushing move finished
13769       player->is_pushing = FALSE;
13770
13771     if (mode == DF_NO_PUSH)     // player just stopped pushing
13772     {
13773       player->is_switching = FALSE;
13774       player->push_delay = -1;
13775
13776       return MP_NO_ACTION;
13777     }
13778   }
13779
13780   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13781     old_element = Back[jx][jy];
13782
13783   // in case of element dropped at player position, check background
13784   else if (Back[jx][jy] != EL_EMPTY &&
13785            game.engine_version >= VERSION_IDENT(2,2,0,0))
13786     old_element = Back[jx][jy];
13787
13788   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13789     return MP_NO_ACTION;        // field has no opening in this direction
13790
13791   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13792     return MP_NO_ACTION;        // field has no opening in this direction
13793
13794   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13795   {
13796     SplashAcid(x, y);
13797
13798     Feld[jx][jy] = player->artwork_element;
13799     InitMovingField(jx, jy, MV_DOWN);
13800     Store[jx][jy] = EL_ACID;
13801     ContinueMoving(jx, jy);
13802     BuryPlayer(player);
13803
13804     return MP_DONT_RUN_INTO;
13805   }
13806
13807   if (player_can_move && DONT_RUN_INTO(element))
13808   {
13809     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13810
13811     return MP_DONT_RUN_INTO;
13812   }
13813
13814   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13815     return MP_NO_ACTION;
13816
13817   collect_count = element_info[element].collect_count_initial;
13818
13819   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13820     return MP_NO_ACTION;
13821
13822   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13823     player_can_move = player_can_move_or_snap;
13824
13825   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13826       game.engine_version >= VERSION_IDENT(2,2,0,0))
13827   {
13828     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13829                                player->index_bit, dig_side);
13830     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13831                                         player->index_bit, dig_side);
13832
13833     if (element == EL_DC_LANDMINE)
13834       Bang(x, y);
13835
13836     if (Feld[x][y] != element)          // field changed by snapping
13837       return MP_ACTION;
13838
13839     return MP_NO_ACTION;
13840   }
13841
13842   if (player->gravity && is_player && !player->is_auto_moving &&
13843       canFallDown(player) && move_direction != MV_DOWN &&
13844       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13845     return MP_NO_ACTION;        // player cannot walk here due to gravity
13846
13847   if (player_can_move &&
13848       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13849   {
13850     int sound_element = SND_ELEMENT(element);
13851     int sound_action = ACTION_WALKING;
13852
13853     if (IS_RND_GATE(element))
13854     {
13855       if (!player->key[RND_GATE_NR(element)])
13856         return MP_NO_ACTION;
13857     }
13858     else if (IS_RND_GATE_GRAY(element))
13859     {
13860       if (!player->key[RND_GATE_GRAY_NR(element)])
13861         return MP_NO_ACTION;
13862     }
13863     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13864     {
13865       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13866         return MP_NO_ACTION;
13867     }
13868     else if (element == EL_EXIT_OPEN ||
13869              element == EL_EM_EXIT_OPEN ||
13870              element == EL_EM_EXIT_OPENING ||
13871              element == EL_STEEL_EXIT_OPEN ||
13872              element == EL_EM_STEEL_EXIT_OPEN ||
13873              element == EL_EM_STEEL_EXIT_OPENING ||
13874              element == EL_SP_EXIT_OPEN ||
13875              element == EL_SP_EXIT_OPENING)
13876     {
13877       sound_action = ACTION_PASSING;    // player is passing exit
13878     }
13879     else if (element == EL_EMPTY)
13880     {
13881       sound_action = ACTION_MOVING;             // nothing to walk on
13882     }
13883
13884     // play sound from background or player, whatever is available
13885     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13886       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13887     else
13888       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13889   }
13890   else if (player_can_move &&
13891            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13892   {
13893     if (!ACCESS_FROM(element, opposite_direction))
13894       return MP_NO_ACTION;      // field not accessible from this direction
13895
13896     if (CAN_MOVE(element))      // only fixed elements can be passed!
13897       return MP_NO_ACTION;
13898
13899     if (IS_EM_GATE(element))
13900     {
13901       if (!player->key[EM_GATE_NR(element)])
13902         return MP_NO_ACTION;
13903     }
13904     else if (IS_EM_GATE_GRAY(element))
13905     {
13906       if (!player->key[EM_GATE_GRAY_NR(element)])
13907         return MP_NO_ACTION;
13908     }
13909     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13910     {
13911       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13912         return MP_NO_ACTION;
13913     }
13914     else if (IS_EMC_GATE(element))
13915     {
13916       if (!player->key[EMC_GATE_NR(element)])
13917         return MP_NO_ACTION;
13918     }
13919     else if (IS_EMC_GATE_GRAY(element))
13920     {
13921       if (!player->key[EMC_GATE_GRAY_NR(element)])
13922         return MP_NO_ACTION;
13923     }
13924     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13925     {
13926       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13927         return MP_NO_ACTION;
13928     }
13929     else if (element == EL_DC_GATE_WHITE ||
13930              element == EL_DC_GATE_WHITE_GRAY ||
13931              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13932     {
13933       if (player->num_white_keys == 0)
13934         return MP_NO_ACTION;
13935
13936       player->num_white_keys--;
13937     }
13938     else if (IS_SP_PORT(element))
13939     {
13940       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13941           element == EL_SP_GRAVITY_PORT_RIGHT ||
13942           element == EL_SP_GRAVITY_PORT_UP ||
13943           element == EL_SP_GRAVITY_PORT_DOWN)
13944         player->gravity = !player->gravity;
13945       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13946                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13947                element == EL_SP_GRAVITY_ON_PORT_UP ||
13948                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13949         player->gravity = TRUE;
13950       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13951                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13952                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13953                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13954         player->gravity = FALSE;
13955     }
13956
13957     // automatically move to the next field with double speed
13958     player->programmed_action = move_direction;
13959
13960     if (player->move_delay_reset_counter == 0)
13961     {
13962       player->move_delay_reset_counter = 2;     // two double speed steps
13963
13964       DOUBLE_PLAYER_SPEED(player);
13965     }
13966
13967     PlayLevelSoundAction(x, y, ACTION_PASSING);
13968   }
13969   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13970   {
13971     RemoveField(x, y);
13972
13973     if (mode != DF_SNAP)
13974     {
13975       GfxElement[x][y] = GFX_ELEMENT(element);
13976       player->is_digging = TRUE;
13977     }
13978
13979     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13980
13981     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13982                                         player->index_bit, dig_side);
13983
13984     if (mode == DF_SNAP)
13985     {
13986       if (level.block_snap_field)
13987         setFieldForSnapping(x, y, element, move_direction);
13988       else
13989         TestIfElementTouchesCustomElement(x, y);        // for empty space
13990
13991       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13992                                           player->index_bit, dig_side);
13993     }
13994   }
13995   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13996   {
13997     RemoveField(x, y);
13998
13999     if (is_player && mode != DF_SNAP)
14000     {
14001       GfxElement[x][y] = element;
14002       player->is_collecting = TRUE;
14003     }
14004
14005     if (element == EL_SPEED_PILL)
14006     {
14007       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14008     }
14009     else if (element == EL_EXTRA_TIME && level.time > 0)
14010     {
14011       TimeLeft += level.extra_time;
14012
14013       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14014
14015       DisplayGameControlValues();
14016     }
14017     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14018     {
14019       player->shield_normal_time_left += level.shield_normal_time;
14020       if (element == EL_SHIELD_DEADLY)
14021         player->shield_deadly_time_left += level.shield_deadly_time;
14022     }
14023     else if (element == EL_DYNAMITE ||
14024              element == EL_EM_DYNAMITE ||
14025              element == EL_SP_DISK_RED)
14026     {
14027       if (player->inventory_size < MAX_INVENTORY_SIZE)
14028         player->inventory_element[player->inventory_size++] = element;
14029
14030       DrawGameDoorValues();
14031     }
14032     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14033     {
14034       player->dynabomb_count++;
14035       player->dynabombs_left++;
14036     }
14037     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14038     {
14039       player->dynabomb_size++;
14040     }
14041     else if (element == EL_DYNABOMB_INCREASE_POWER)
14042     {
14043       player->dynabomb_xl = TRUE;
14044     }
14045     else if (IS_KEY(element))
14046     {
14047       player->key[KEY_NR(element)] = TRUE;
14048
14049       DrawGameDoorValues();
14050     }
14051     else if (element == EL_DC_KEY_WHITE)
14052     {
14053       player->num_white_keys++;
14054
14055       // display white keys?
14056       // DrawGameDoorValues();
14057     }
14058     else if (IS_ENVELOPE(element))
14059     {
14060       player->show_envelope = element;
14061     }
14062     else if (element == EL_EMC_LENSES)
14063     {
14064       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14065
14066       RedrawAllInvisibleElementsForLenses();
14067     }
14068     else if (element == EL_EMC_MAGNIFIER)
14069     {
14070       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14071
14072       RedrawAllInvisibleElementsForMagnifier();
14073     }
14074     else if (IS_DROPPABLE(element) ||
14075              IS_THROWABLE(element))     // can be collected and dropped
14076     {
14077       int i;
14078
14079       if (collect_count == 0)
14080         player->inventory_infinite_element = element;
14081       else
14082         for (i = 0; i < collect_count; i++)
14083           if (player->inventory_size < MAX_INVENTORY_SIZE)
14084             player->inventory_element[player->inventory_size++] = element;
14085
14086       DrawGameDoorValues();
14087     }
14088     else if (collect_count > 0)
14089     {
14090       game.gems_still_needed -= collect_count;
14091       if (game.gems_still_needed < 0)
14092         game.gems_still_needed = 0;
14093
14094       game.snapshot.collected_item = TRUE;
14095
14096       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14097
14098       DisplayGameControlValues();
14099     }
14100
14101     RaiseScoreElement(element);
14102     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14103
14104     if (is_player)
14105       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14106                                           player->index_bit, dig_side);
14107
14108     if (mode == DF_SNAP)
14109     {
14110       if (level.block_snap_field)
14111         setFieldForSnapping(x, y, element, move_direction);
14112       else
14113         TestIfElementTouchesCustomElement(x, y);        // for empty space
14114
14115       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14116                                           player->index_bit, dig_side);
14117     }
14118   }
14119   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14120   {
14121     if (mode == DF_SNAP && element != EL_BD_ROCK)
14122       return MP_NO_ACTION;
14123
14124     if (CAN_FALL(element) && dy)
14125       return MP_NO_ACTION;
14126
14127     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14128         !(element == EL_SPRING && level.use_spring_bug))
14129       return MP_NO_ACTION;
14130
14131     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14132         ((move_direction & MV_VERTICAL &&
14133           ((element_info[element].move_pattern & MV_LEFT &&
14134             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14135            (element_info[element].move_pattern & MV_RIGHT &&
14136             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14137          (move_direction & MV_HORIZONTAL &&
14138           ((element_info[element].move_pattern & MV_UP &&
14139             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14140            (element_info[element].move_pattern & MV_DOWN &&
14141             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14142       return MP_NO_ACTION;
14143
14144     // do not push elements already moving away faster than player
14145     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14146         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14147       return MP_NO_ACTION;
14148
14149     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14150     {
14151       if (player->push_delay_value == -1 || !player_was_pushing)
14152         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14153     }
14154     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14155     {
14156       if (player->push_delay_value == -1)
14157         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14158     }
14159     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14160     {
14161       if (!player->is_pushing)
14162         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14163     }
14164
14165     player->is_pushing = TRUE;
14166     player->is_active = TRUE;
14167
14168     if (!(IN_LEV_FIELD(nextx, nexty) &&
14169           (IS_FREE(nextx, nexty) ||
14170            (IS_SB_ELEMENT(element) &&
14171             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14172            (IS_CUSTOM_ELEMENT(element) &&
14173             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14174       return MP_NO_ACTION;
14175
14176     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14177       return MP_NO_ACTION;
14178
14179     if (player->push_delay == -1)       // new pushing; restart delay
14180       player->push_delay = 0;
14181
14182     if (player->push_delay < player->push_delay_value &&
14183         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14184         element != EL_SPRING && element != EL_BALLOON)
14185     {
14186       // make sure that there is no move delay before next try to push
14187       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14188         player->move_delay = 0;
14189
14190       return MP_NO_ACTION;
14191     }
14192
14193     if (IS_CUSTOM_ELEMENT(element) &&
14194         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14195     {
14196       if (!DigFieldByCE(nextx, nexty, element))
14197         return MP_NO_ACTION;
14198     }
14199
14200     if (IS_SB_ELEMENT(element))
14201     {
14202       boolean sokoban_task_solved = FALSE;
14203
14204       if (element == EL_SOKOBAN_FIELD_FULL)
14205       {
14206         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14207
14208         IncrementSokobanFieldsNeeded();
14209         IncrementSokobanObjectsNeeded();
14210       }
14211
14212       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14213       {
14214         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14215
14216         DecrementSokobanFieldsNeeded();
14217         DecrementSokobanObjectsNeeded();
14218
14219         // sokoban object was pushed from empty field to sokoban field
14220         if (Back[x][y] == EL_EMPTY)
14221           sokoban_task_solved = TRUE;
14222       }
14223
14224       Feld[x][y] = EL_SOKOBAN_OBJECT;
14225
14226       if (Back[x][y] == Back[nextx][nexty])
14227         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14228       else if (Back[x][y] != 0)
14229         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14230                                     ACTION_EMPTYING);
14231       else
14232         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14233                                     ACTION_FILLING);
14234
14235       if (sokoban_task_solved &&
14236           game.sokoban_fields_still_needed == 0 &&
14237           game.sokoban_objects_still_needed == 0 &&
14238           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14239       {
14240         game.players_still_needed = 0;
14241
14242         LevelSolved();
14243
14244         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14245       }
14246     }
14247     else
14248       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14249
14250     InitMovingField(x, y, move_direction);
14251     GfxAction[x][y] = ACTION_PUSHING;
14252
14253     if (mode == DF_SNAP)
14254       ContinueMoving(x, y);
14255     else
14256       MovPos[x][y] = (dx != 0 ? dx : dy);
14257
14258     Pushed[x][y] = TRUE;
14259     Pushed[nextx][nexty] = TRUE;
14260
14261     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14262       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14263     else
14264       player->push_delay_value = -1;    // get new value later
14265
14266     // check for element change _after_ element has been pushed
14267     if (game.use_change_when_pushing_bug)
14268     {
14269       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14270                                  player->index_bit, dig_side);
14271       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14272                                           player->index_bit, dig_side);
14273     }
14274   }
14275   else if (IS_SWITCHABLE(element))
14276   {
14277     if (PLAYER_SWITCHING(player, x, y))
14278     {
14279       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14280                                           player->index_bit, dig_side);
14281
14282       return MP_ACTION;
14283     }
14284
14285     player->is_switching = TRUE;
14286     player->switch_x = x;
14287     player->switch_y = y;
14288
14289     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14290
14291     if (element == EL_ROBOT_WHEEL)
14292     {
14293       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14294
14295       game.robot_wheel_x = x;
14296       game.robot_wheel_y = y;
14297       game.robot_wheel_active = TRUE;
14298
14299       TEST_DrawLevelField(x, y);
14300     }
14301     else if (element == EL_SP_TERMINAL)
14302     {
14303       int xx, yy;
14304
14305       SCAN_PLAYFIELD(xx, yy)
14306       {
14307         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14308         {
14309           Bang(xx, yy);
14310         }
14311         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14312         {
14313           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14314
14315           ResetGfxAnimation(xx, yy);
14316           TEST_DrawLevelField(xx, yy);
14317         }
14318       }
14319     }
14320     else if (IS_BELT_SWITCH(element))
14321     {
14322       ToggleBeltSwitch(x, y);
14323     }
14324     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14325              element == EL_SWITCHGATE_SWITCH_DOWN ||
14326              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14327              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14328     {
14329       ToggleSwitchgateSwitch(x, y);
14330     }
14331     else if (element == EL_LIGHT_SWITCH ||
14332              element == EL_LIGHT_SWITCH_ACTIVE)
14333     {
14334       ToggleLightSwitch(x, y);
14335     }
14336     else if (element == EL_TIMEGATE_SWITCH ||
14337              element == EL_DC_TIMEGATE_SWITCH)
14338     {
14339       ActivateTimegateSwitch(x, y);
14340     }
14341     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14342              element == EL_BALLOON_SWITCH_RIGHT ||
14343              element == EL_BALLOON_SWITCH_UP    ||
14344              element == EL_BALLOON_SWITCH_DOWN  ||
14345              element == EL_BALLOON_SWITCH_NONE  ||
14346              element == EL_BALLOON_SWITCH_ANY)
14347     {
14348       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14349                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14350                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14351                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14352                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14353                              move_direction);
14354     }
14355     else if (element == EL_LAMP)
14356     {
14357       Feld[x][y] = EL_LAMP_ACTIVE;
14358       game.lights_still_needed--;
14359
14360       ResetGfxAnimation(x, y);
14361       TEST_DrawLevelField(x, y);
14362     }
14363     else if (element == EL_TIME_ORB_FULL)
14364     {
14365       Feld[x][y] = EL_TIME_ORB_EMPTY;
14366
14367       if (level.time > 0 || level.use_time_orb_bug)
14368       {
14369         TimeLeft += level.time_orb_time;
14370         game.no_time_limit = FALSE;
14371
14372         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14373
14374         DisplayGameControlValues();
14375       }
14376
14377       ResetGfxAnimation(x, y);
14378       TEST_DrawLevelField(x, y);
14379     }
14380     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14381              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14382     {
14383       int xx, yy;
14384
14385       game.ball_active = !game.ball_active;
14386
14387       SCAN_PLAYFIELD(xx, yy)
14388       {
14389         int e = Feld[xx][yy];
14390
14391         if (game.ball_active)
14392         {
14393           if (e == EL_EMC_MAGIC_BALL)
14394             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14395           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14396             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14397         }
14398         else
14399         {
14400           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14401             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14402           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14403             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14404         }
14405       }
14406     }
14407
14408     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14409                                         player->index_bit, dig_side);
14410
14411     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14412                                         player->index_bit, dig_side);
14413
14414     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14415                                         player->index_bit, dig_side);
14416
14417     return MP_ACTION;
14418   }
14419   else
14420   {
14421     if (!PLAYER_SWITCHING(player, x, y))
14422     {
14423       player->is_switching = TRUE;
14424       player->switch_x = x;
14425       player->switch_y = y;
14426
14427       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14428                                  player->index_bit, dig_side);
14429       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14430                                           player->index_bit, dig_side);
14431
14432       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14433                                  player->index_bit, dig_side);
14434       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14435                                           player->index_bit, dig_side);
14436     }
14437
14438     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14439                                player->index_bit, dig_side);
14440     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14441                                         player->index_bit, dig_side);
14442
14443     return MP_NO_ACTION;
14444   }
14445
14446   player->push_delay = -1;
14447
14448   if (is_player)                // function can also be called by EL_PENGUIN
14449   {
14450     if (Feld[x][y] != element)          // really digged/collected something
14451     {
14452       player->is_collecting = !player->is_digging;
14453       player->is_active = TRUE;
14454     }
14455   }
14456
14457   return MP_MOVING;
14458 }
14459
14460 static boolean DigFieldByCE(int x, int y, int digging_element)
14461 {
14462   int element = Feld[x][y];
14463
14464   if (!IS_FREE(x, y))
14465   {
14466     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14467                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14468                   ACTION_BREAKING);
14469
14470     // no element can dig solid indestructible elements
14471     if (IS_INDESTRUCTIBLE(element) &&
14472         !IS_DIGGABLE(element) &&
14473         !IS_COLLECTIBLE(element))
14474       return FALSE;
14475
14476     if (AmoebaNr[x][y] &&
14477         (element == EL_AMOEBA_FULL ||
14478          element == EL_BD_AMOEBA ||
14479          element == EL_AMOEBA_GROWING))
14480     {
14481       AmoebaCnt[AmoebaNr[x][y]]--;
14482       AmoebaCnt2[AmoebaNr[x][y]]--;
14483     }
14484
14485     if (IS_MOVING(x, y))
14486       RemoveMovingField(x, y);
14487     else
14488     {
14489       RemoveField(x, y);
14490       TEST_DrawLevelField(x, y);
14491     }
14492
14493     // if digged element was about to explode, prevent the explosion
14494     ExplodeField[x][y] = EX_TYPE_NONE;
14495
14496     PlayLevelSoundAction(x, y, action);
14497   }
14498
14499   Store[x][y] = EL_EMPTY;
14500
14501   // this makes it possible to leave the removed element again
14502   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14503     Store[x][y] = element;
14504
14505   return TRUE;
14506 }
14507
14508 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14509 {
14510   int jx = player->jx, jy = player->jy;
14511   int x = jx + dx, y = jy + dy;
14512   int snap_direction = (dx == -1 ? MV_LEFT  :
14513                         dx == +1 ? MV_RIGHT :
14514                         dy == -1 ? MV_UP    :
14515                         dy == +1 ? MV_DOWN  : MV_NONE);
14516   boolean can_continue_snapping = (level.continuous_snapping &&
14517                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14518
14519   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14520     return FALSE;
14521
14522   if (!player->active || !IN_LEV_FIELD(x, y))
14523     return FALSE;
14524
14525   if (dx && dy)
14526     return FALSE;
14527
14528   if (!dx && !dy)
14529   {
14530     if (player->MovPos == 0)
14531       player->is_pushing = FALSE;
14532
14533     player->is_snapping = FALSE;
14534
14535     if (player->MovPos == 0)
14536     {
14537       player->is_moving = FALSE;
14538       player->is_digging = FALSE;
14539       player->is_collecting = FALSE;
14540     }
14541
14542     return FALSE;
14543   }
14544
14545   // prevent snapping with already pressed snap key when not allowed
14546   if (player->is_snapping && !can_continue_snapping)
14547     return FALSE;
14548
14549   player->MovDir = snap_direction;
14550
14551   if (player->MovPos == 0)
14552   {
14553     player->is_moving = FALSE;
14554     player->is_digging = FALSE;
14555     player->is_collecting = FALSE;
14556   }
14557
14558   player->is_dropping = FALSE;
14559   player->is_dropping_pressed = FALSE;
14560   player->drop_pressed_delay = 0;
14561
14562   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14563     return FALSE;
14564
14565   player->is_snapping = TRUE;
14566   player->is_active = TRUE;
14567
14568   if (player->MovPos == 0)
14569   {
14570     player->is_moving = FALSE;
14571     player->is_digging = FALSE;
14572     player->is_collecting = FALSE;
14573   }
14574
14575   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14576     TEST_DrawLevelField(player->last_jx, player->last_jy);
14577
14578   TEST_DrawLevelField(x, y);
14579
14580   return TRUE;
14581 }
14582
14583 static boolean DropElement(struct PlayerInfo *player)
14584 {
14585   int old_element, new_element;
14586   int dropx = player->jx, dropy = player->jy;
14587   int drop_direction = player->MovDir;
14588   int drop_side = drop_direction;
14589   int drop_element = get_next_dropped_element(player);
14590
14591   /* do not drop an element on top of another element; when holding drop key
14592      pressed without moving, dropped element must move away before the next
14593      element can be dropped (this is especially important if the next element
14594      is dynamite, which can be placed on background for historical reasons) */
14595   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14596     return MP_ACTION;
14597
14598   if (IS_THROWABLE(drop_element))
14599   {
14600     dropx += GET_DX_FROM_DIR(drop_direction);
14601     dropy += GET_DY_FROM_DIR(drop_direction);
14602
14603     if (!IN_LEV_FIELD(dropx, dropy))
14604       return FALSE;
14605   }
14606
14607   old_element = Feld[dropx][dropy];     // old element at dropping position
14608   new_element = drop_element;           // default: no change when dropping
14609
14610   // check if player is active, not moving and ready to drop
14611   if (!player->active || player->MovPos || player->drop_delay > 0)
14612     return FALSE;
14613
14614   // check if player has anything that can be dropped
14615   if (new_element == EL_UNDEFINED)
14616     return FALSE;
14617
14618   // only set if player has anything that can be dropped
14619   player->is_dropping_pressed = TRUE;
14620
14621   // check if drop key was pressed long enough for EM style dynamite
14622   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14623     return FALSE;
14624
14625   // check if anything can be dropped at the current position
14626   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14627     return FALSE;
14628
14629   // collected custom elements can only be dropped on empty fields
14630   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14631     return FALSE;
14632
14633   if (old_element != EL_EMPTY)
14634     Back[dropx][dropy] = old_element;   // store old element on this field
14635
14636   ResetGfxAnimation(dropx, dropy);
14637   ResetRandomAnimationValue(dropx, dropy);
14638
14639   if (player->inventory_size > 0 ||
14640       player->inventory_infinite_element != EL_UNDEFINED)
14641   {
14642     if (player->inventory_size > 0)
14643     {
14644       player->inventory_size--;
14645
14646       DrawGameDoorValues();
14647
14648       if (new_element == EL_DYNAMITE)
14649         new_element = EL_DYNAMITE_ACTIVE;
14650       else if (new_element == EL_EM_DYNAMITE)
14651         new_element = EL_EM_DYNAMITE_ACTIVE;
14652       else if (new_element == EL_SP_DISK_RED)
14653         new_element = EL_SP_DISK_RED_ACTIVE;
14654     }
14655
14656     Feld[dropx][dropy] = new_element;
14657
14658     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14659       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14660                           el2img(Feld[dropx][dropy]), 0);
14661
14662     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14663
14664     // needed if previous element just changed to "empty" in the last frame
14665     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14666
14667     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14668                                player->index_bit, drop_side);
14669     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14670                                         CE_PLAYER_DROPS_X,
14671                                         player->index_bit, drop_side);
14672
14673     TestIfElementTouchesCustomElement(dropx, dropy);
14674   }
14675   else          // player is dropping a dyna bomb
14676   {
14677     player->dynabombs_left--;
14678
14679     Feld[dropx][dropy] = new_element;
14680
14681     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14682       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14683                           el2img(Feld[dropx][dropy]), 0);
14684
14685     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14686   }
14687
14688   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14689     InitField_WithBug1(dropx, dropy, FALSE);
14690
14691   new_element = Feld[dropx][dropy];     // element might have changed
14692
14693   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14694       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14695   {
14696     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14697       MovDir[dropx][dropy] = drop_direction;
14698
14699     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14700
14701     // do not cause impact style collision by dropping elements that can fall
14702     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14703   }
14704
14705   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14706   player->is_dropping = TRUE;
14707
14708   player->drop_pressed_delay = 0;
14709   player->is_dropping_pressed = FALSE;
14710
14711   player->drop_x = dropx;
14712   player->drop_y = dropy;
14713
14714   return TRUE;
14715 }
14716
14717 // ----------------------------------------------------------------------------
14718 // game sound playing functions
14719 // ----------------------------------------------------------------------------
14720
14721 static int *loop_sound_frame = NULL;
14722 static int *loop_sound_volume = NULL;
14723
14724 void InitPlayLevelSound(void)
14725 {
14726   int num_sounds = getSoundListSize();
14727
14728   checked_free(loop_sound_frame);
14729   checked_free(loop_sound_volume);
14730
14731   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14732   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14733 }
14734
14735 static void PlayLevelSound(int x, int y, int nr)
14736 {
14737   int sx = SCREENX(x), sy = SCREENY(y);
14738   int volume, stereo_position;
14739   int max_distance = 8;
14740   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14741
14742   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14743       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14744     return;
14745
14746   if (!IN_LEV_FIELD(x, y) ||
14747       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14748       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14749     return;
14750
14751   volume = SOUND_MAX_VOLUME;
14752
14753   if (!IN_SCR_FIELD(sx, sy))
14754   {
14755     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14756     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14757
14758     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14759   }
14760
14761   stereo_position = (SOUND_MAX_LEFT +
14762                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14763                      (SCR_FIELDX + 2 * max_distance));
14764
14765   if (IS_LOOP_SOUND(nr))
14766   {
14767     /* This assures that quieter loop sounds do not overwrite louder ones,
14768        while restarting sound volume comparison with each new game frame. */
14769
14770     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14771       return;
14772
14773     loop_sound_volume[nr] = volume;
14774     loop_sound_frame[nr] = FrameCounter;
14775   }
14776
14777   PlaySoundExt(nr, volume, stereo_position, type);
14778 }
14779
14780 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14781 {
14782   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14783                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14784                  y < LEVELY(BY1) ? LEVELY(BY1) :
14785                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14786                  sound_action);
14787 }
14788
14789 static void PlayLevelSoundAction(int x, int y, int action)
14790 {
14791   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14792 }
14793
14794 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14795 {
14796   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14797
14798   if (sound_effect != SND_UNDEFINED)
14799     PlayLevelSound(x, y, sound_effect);
14800 }
14801
14802 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14803                                               int action)
14804 {
14805   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14806
14807   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14808     PlayLevelSound(x, y, sound_effect);
14809 }
14810
14811 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14812 {
14813   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14814
14815   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14816     PlayLevelSound(x, y, sound_effect);
14817 }
14818
14819 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14820 {
14821   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14822
14823   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14824     StopSound(sound_effect);
14825 }
14826
14827 static int getLevelMusicNr(void)
14828 {
14829   if (levelset.music[level_nr] != MUS_UNDEFINED)
14830     return levelset.music[level_nr];            // from config file
14831   else
14832     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14833 }
14834
14835 static void FadeLevelSounds(void)
14836 {
14837   FadeSounds();
14838 }
14839
14840 static void FadeLevelMusic(void)
14841 {
14842   int music_nr = getLevelMusicNr();
14843   char *curr_music = getCurrentlyPlayingMusicFilename();
14844   char *next_music = getMusicInfoEntryFilename(music_nr);
14845
14846   if (!strEqual(curr_music, next_music))
14847     FadeMusic();
14848 }
14849
14850 void FadeLevelSoundsAndMusic(void)
14851 {
14852   FadeLevelSounds();
14853   FadeLevelMusic();
14854 }
14855
14856 static void PlayLevelMusic(void)
14857 {
14858   int music_nr = getLevelMusicNr();
14859   char *curr_music = getCurrentlyPlayingMusicFilename();
14860   char *next_music = getMusicInfoEntryFilename(music_nr);
14861
14862   if (!strEqual(curr_music, next_music))
14863     PlayMusicLoop(music_nr);
14864 }
14865
14866 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14867 {
14868   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14869   int offset = 0;
14870   int x = xx - offset;
14871   int y = yy - offset;
14872
14873   switch (sample)
14874   {
14875     case SOUND_blank:
14876       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14877       break;
14878
14879     case SOUND_roll:
14880       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14881       break;
14882
14883     case SOUND_stone:
14884       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14885       break;
14886
14887     case SOUND_nut:
14888       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14889       break;
14890
14891     case SOUND_crack:
14892       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14893       break;
14894
14895     case SOUND_bug:
14896       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14897       break;
14898
14899     case SOUND_tank:
14900       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14901       break;
14902
14903     case SOUND_android_clone:
14904       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14905       break;
14906
14907     case SOUND_android_move:
14908       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14909       break;
14910
14911     case SOUND_spring:
14912       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14913       break;
14914
14915     case SOUND_slurp:
14916       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14917       break;
14918
14919     case SOUND_eater:
14920       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14921       break;
14922
14923     case SOUND_eater_eat:
14924       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14925       break;
14926
14927     case SOUND_alien:
14928       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14929       break;
14930
14931     case SOUND_collect:
14932       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14933       break;
14934
14935     case SOUND_diamond:
14936       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14937       break;
14938
14939     case SOUND_squash:
14940       // !!! CHECK THIS !!!
14941 #if 1
14942       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14943 #else
14944       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14945 #endif
14946       break;
14947
14948     case SOUND_wonderfall:
14949       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14950       break;
14951
14952     case SOUND_drip:
14953       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14954       break;
14955
14956     case SOUND_push:
14957       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14958       break;
14959
14960     case SOUND_dirt:
14961       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14962       break;
14963
14964     case SOUND_acid:
14965       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14966       break;
14967
14968     case SOUND_ball:
14969       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14970       break;
14971
14972     case SOUND_slide:
14973       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14974       break;
14975
14976     case SOUND_wonder:
14977       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14978       break;
14979
14980     case SOUND_door:
14981       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14982       break;
14983
14984     case SOUND_exit_open:
14985       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14986       break;
14987
14988     case SOUND_exit_leave:
14989       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14990       break;
14991
14992     case SOUND_dynamite:
14993       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14994       break;
14995
14996     case SOUND_tick:
14997       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14998       break;
14999
15000     case SOUND_press:
15001       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15002       break;
15003
15004     case SOUND_wheel:
15005       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15006       break;
15007
15008     case SOUND_boom:
15009       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15010       break;
15011
15012     case SOUND_die:
15013       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15014       break;
15015
15016     case SOUND_time:
15017       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15018       break;
15019
15020     default:
15021       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15022       break;
15023   }
15024 }
15025
15026 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15027 {
15028   int element = map_element_SP_to_RND(element_sp);
15029   int action = map_action_SP_to_RND(action_sp);
15030   int offset = (setup.sp_show_border_elements ? 0 : 1);
15031   int x = xx - offset;
15032   int y = yy - offset;
15033
15034   PlayLevelSoundElementAction(x, y, element, action);
15035 }
15036
15037 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15038 {
15039   int element = map_element_MM_to_RND(element_mm);
15040   int action = map_action_MM_to_RND(action_mm);
15041   int offset = 0;
15042   int x = xx - offset;
15043   int y = yy - offset;
15044
15045   if (!IS_MM_ELEMENT(element))
15046     element = EL_MM_DEFAULT;
15047
15048   PlayLevelSoundElementAction(x, y, element, action);
15049 }
15050
15051 void PlaySound_MM(int sound_mm)
15052 {
15053   int sound = map_sound_MM_to_RND(sound_mm);
15054
15055   if (sound == SND_UNDEFINED)
15056     return;
15057
15058   PlaySound(sound);
15059 }
15060
15061 void PlaySoundLoop_MM(int sound_mm)
15062 {
15063   int sound = map_sound_MM_to_RND(sound_mm);
15064
15065   if (sound == SND_UNDEFINED)
15066     return;
15067
15068   PlaySoundLoop(sound);
15069 }
15070
15071 void StopSound_MM(int sound_mm)
15072 {
15073   int sound = map_sound_MM_to_RND(sound_mm);
15074
15075   if (sound == SND_UNDEFINED)
15076     return;
15077
15078   StopSound(sound);
15079 }
15080
15081 void RaiseScore(int value)
15082 {
15083   game.score += value;
15084
15085   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15086
15087   DisplayGameControlValues();
15088 }
15089
15090 void RaiseScoreElement(int element)
15091 {
15092   switch (element)
15093   {
15094     case EL_EMERALD:
15095     case EL_BD_DIAMOND:
15096     case EL_EMERALD_YELLOW:
15097     case EL_EMERALD_RED:
15098     case EL_EMERALD_PURPLE:
15099     case EL_SP_INFOTRON:
15100       RaiseScore(level.score[SC_EMERALD]);
15101       break;
15102     case EL_DIAMOND:
15103       RaiseScore(level.score[SC_DIAMOND]);
15104       break;
15105     case EL_CRYSTAL:
15106       RaiseScore(level.score[SC_CRYSTAL]);
15107       break;
15108     case EL_PEARL:
15109       RaiseScore(level.score[SC_PEARL]);
15110       break;
15111     case EL_BUG:
15112     case EL_BD_BUTTERFLY:
15113     case EL_SP_ELECTRON:
15114       RaiseScore(level.score[SC_BUG]);
15115       break;
15116     case EL_SPACESHIP:
15117     case EL_BD_FIREFLY:
15118     case EL_SP_SNIKSNAK:
15119       RaiseScore(level.score[SC_SPACESHIP]);
15120       break;
15121     case EL_YAMYAM:
15122     case EL_DARK_YAMYAM:
15123       RaiseScore(level.score[SC_YAMYAM]);
15124       break;
15125     case EL_ROBOT:
15126       RaiseScore(level.score[SC_ROBOT]);
15127       break;
15128     case EL_PACMAN:
15129       RaiseScore(level.score[SC_PACMAN]);
15130       break;
15131     case EL_NUT:
15132       RaiseScore(level.score[SC_NUT]);
15133       break;
15134     case EL_DYNAMITE:
15135     case EL_EM_DYNAMITE:
15136     case EL_SP_DISK_RED:
15137     case EL_DYNABOMB_INCREASE_NUMBER:
15138     case EL_DYNABOMB_INCREASE_SIZE:
15139     case EL_DYNABOMB_INCREASE_POWER:
15140       RaiseScore(level.score[SC_DYNAMITE]);
15141       break;
15142     case EL_SHIELD_NORMAL:
15143     case EL_SHIELD_DEADLY:
15144       RaiseScore(level.score[SC_SHIELD]);
15145       break;
15146     case EL_EXTRA_TIME:
15147       RaiseScore(level.extra_time_score);
15148       break;
15149     case EL_KEY_1:
15150     case EL_KEY_2:
15151     case EL_KEY_3:
15152     case EL_KEY_4:
15153     case EL_EM_KEY_1:
15154     case EL_EM_KEY_2:
15155     case EL_EM_KEY_3:
15156     case EL_EM_KEY_4:
15157     case EL_EMC_KEY_5:
15158     case EL_EMC_KEY_6:
15159     case EL_EMC_KEY_7:
15160     case EL_EMC_KEY_8:
15161     case EL_DC_KEY_WHITE:
15162       RaiseScore(level.score[SC_KEY]);
15163       break;
15164     default:
15165       RaiseScore(element_info[element].collect_score);
15166       break;
15167   }
15168 }
15169
15170 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15171 {
15172   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15173   {
15174     // closing door required in case of envelope style request dialogs
15175     if (!skip_request)
15176     {
15177       // prevent short reactivation of overlay buttons while closing door
15178       SetOverlayActive(FALSE);
15179
15180       CloseDoor(DOOR_CLOSE_1);
15181     }
15182
15183     if (network.enabled)
15184       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15185     else
15186     {
15187       if (quick_quit)
15188         FadeSkipNextFadeIn();
15189
15190       SetGameStatus(GAME_MODE_MAIN);
15191
15192       DrawMainMenu();
15193     }
15194   }
15195   else          // continue playing the game
15196   {
15197     if (tape.playing && tape.deactivate_display)
15198       TapeDeactivateDisplayOff(TRUE);
15199
15200     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15201
15202     if (tape.playing && tape.deactivate_display)
15203       TapeDeactivateDisplayOn();
15204   }
15205 }
15206
15207 void RequestQuitGame(boolean ask_if_really_quit)
15208 {
15209   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15210   boolean skip_request = game.all_players_gone || quick_quit;
15211
15212   RequestQuitGameExt(skip_request, quick_quit,
15213                      "Do you really want to quit the game?");
15214 }
15215
15216 void RequestRestartGame(char *message)
15217 {
15218   game.restart_game_message = NULL;
15219
15220   boolean has_started_game = hasStartedNetworkGame();
15221   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15222
15223   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15224   {
15225     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15226   }
15227   else
15228   {
15229     SetGameStatus(GAME_MODE_MAIN);
15230
15231     DrawMainMenu();
15232   }
15233 }
15234
15235 void CheckGameOver(void)
15236 {
15237   static boolean last_game_over = FALSE;
15238   static int game_over_delay = 0;
15239   int game_over_delay_value = 50;
15240   boolean game_over = checkGameFailed();
15241
15242   // do not handle game over if request dialog is already active
15243   if (game.request_active)
15244     return;
15245
15246   // do not ask to play again if game was never actually played
15247   if (!game.GamePlayed)
15248     return;
15249
15250   if (!game_over)
15251   {
15252     last_game_over = FALSE;
15253     game_over_delay = game_over_delay_value;
15254
15255     return;
15256   }
15257
15258   if (game_over_delay > 0)
15259   {
15260     game_over_delay--;
15261
15262     return;
15263   }
15264
15265   if (last_game_over != game_over)
15266     game.restart_game_message = (hasStartedNetworkGame() ?
15267                                  "Game over! Play it again?" :
15268                                  "Game over!");
15269
15270   last_game_over = game_over;
15271 }
15272
15273 boolean checkGameSolved(void)
15274 {
15275   // set for all game engines if level was solved
15276   return game.LevelSolved_GameEnd;
15277 }
15278
15279 boolean checkGameFailed(void)
15280 {
15281   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15282     return (game_em.game_over && !game_em.level_solved);
15283   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15284     return (game_sp.game_over && !game_sp.level_solved);
15285   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15286     return (game_mm.game_over && !game_mm.level_solved);
15287   else                          // GAME_ENGINE_TYPE_RND
15288     return (game.GameOver && !game.LevelSolved);
15289 }
15290
15291 boolean checkGameEnded(void)
15292 {
15293   return (checkGameSolved() || checkGameFailed());
15294 }
15295
15296
15297 // ----------------------------------------------------------------------------
15298 // random generator functions
15299 // ----------------------------------------------------------------------------
15300
15301 unsigned int InitEngineRandom_RND(int seed)
15302 {
15303   game.num_random_calls = 0;
15304
15305   return InitEngineRandom(seed);
15306 }
15307
15308 unsigned int RND(int max)
15309 {
15310   if (max > 0)
15311   {
15312     game.num_random_calls++;
15313
15314     return GetEngineRandom(max);
15315   }
15316
15317   return 0;
15318 }
15319
15320
15321 // ----------------------------------------------------------------------------
15322 // game engine snapshot handling functions
15323 // ----------------------------------------------------------------------------
15324
15325 struct EngineSnapshotInfo
15326 {
15327   // runtime values for custom element collect score
15328   int collect_score[NUM_CUSTOM_ELEMENTS];
15329
15330   // runtime values for group element choice position
15331   int choice_pos[NUM_GROUP_ELEMENTS];
15332
15333   // runtime values for belt position animations
15334   int belt_graphic[4][NUM_BELT_PARTS];
15335   int belt_anim_mode[4][NUM_BELT_PARTS];
15336 };
15337
15338 static struct EngineSnapshotInfo engine_snapshot_rnd;
15339 static char *snapshot_level_identifier = NULL;
15340 static int snapshot_level_nr = -1;
15341
15342 static void SaveEngineSnapshotValues_RND(void)
15343 {
15344   static int belt_base_active_element[4] =
15345   {
15346     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15347     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15348     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15349     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15350   };
15351   int i, j;
15352
15353   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15354   {
15355     int element = EL_CUSTOM_START + i;
15356
15357     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15358   }
15359
15360   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15361   {
15362     int element = EL_GROUP_START + i;
15363
15364     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15365   }
15366
15367   for (i = 0; i < 4; i++)
15368   {
15369     for (j = 0; j < NUM_BELT_PARTS; j++)
15370     {
15371       int element = belt_base_active_element[i] + j;
15372       int graphic = el2img(element);
15373       int anim_mode = graphic_info[graphic].anim_mode;
15374
15375       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15376       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15377     }
15378   }
15379 }
15380
15381 static void LoadEngineSnapshotValues_RND(void)
15382 {
15383   unsigned int num_random_calls = game.num_random_calls;
15384   int i, j;
15385
15386   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15387   {
15388     int element = EL_CUSTOM_START + i;
15389
15390     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15391   }
15392
15393   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15394   {
15395     int element = EL_GROUP_START + i;
15396
15397     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15398   }
15399
15400   for (i = 0; i < 4; i++)
15401   {
15402     for (j = 0; j < NUM_BELT_PARTS; j++)
15403     {
15404       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15405       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15406
15407       graphic_info[graphic].anim_mode = anim_mode;
15408     }
15409   }
15410
15411   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15412   {
15413     InitRND(tape.random_seed);
15414     for (i = 0; i < num_random_calls; i++)
15415       RND(1);
15416   }
15417
15418   if (game.num_random_calls != num_random_calls)
15419   {
15420     Error(ERR_INFO, "number of random calls out of sync");
15421     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15422     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15423     Error(ERR_EXIT, "this should not happen -- please debug");
15424   }
15425 }
15426
15427 void FreeEngineSnapshotSingle(void)
15428 {
15429   FreeSnapshotSingle();
15430
15431   setString(&snapshot_level_identifier, NULL);
15432   snapshot_level_nr = -1;
15433 }
15434
15435 void FreeEngineSnapshotList(void)
15436 {
15437   FreeSnapshotList();
15438 }
15439
15440 static ListNode *SaveEngineSnapshotBuffers(void)
15441 {
15442   ListNode *buffers = NULL;
15443
15444   // copy some special values to a structure better suited for the snapshot
15445
15446   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15447     SaveEngineSnapshotValues_RND();
15448   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15449     SaveEngineSnapshotValues_EM();
15450   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15451     SaveEngineSnapshotValues_SP(&buffers);
15452   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15453     SaveEngineSnapshotValues_MM(&buffers);
15454
15455   // save values stored in special snapshot structure
15456
15457   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15458     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15459   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15460     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15461   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15462     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15463   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15464     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15465
15466   // save further RND engine values
15467
15468   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15469   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15470   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15471
15472   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15473   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15474   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15475   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15476   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15477
15478   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15479   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15480   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15481
15482   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15483
15484   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15485   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15486
15487   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15488   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15489   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15490   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15491   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15492   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15493   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15494   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15495   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15496   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15497   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15498   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15499   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15500   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15501   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15502   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15503   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15504   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15505
15506   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15507   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15508
15509   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15510   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15511   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15512
15513   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15514   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15515
15516   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15517   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15518   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15519   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15520   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15521
15522   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15523   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15524
15525 #if 0
15526   ListNode *node = engine_snapshot_list_rnd;
15527   int num_bytes = 0;
15528
15529   while (node != NULL)
15530   {
15531     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15532
15533     node = node->next;
15534   }
15535
15536   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15537 #endif
15538
15539   return buffers;
15540 }
15541
15542 void SaveEngineSnapshotSingle(void)
15543 {
15544   ListNode *buffers = SaveEngineSnapshotBuffers();
15545
15546   // finally save all snapshot buffers to single snapshot
15547   SaveSnapshotSingle(buffers);
15548
15549   // save level identification information
15550   setString(&snapshot_level_identifier, leveldir_current->identifier);
15551   snapshot_level_nr = level_nr;
15552 }
15553
15554 boolean CheckSaveEngineSnapshotToList(void)
15555 {
15556   boolean save_snapshot =
15557     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15558      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15559       game.snapshot.changed_action) ||
15560      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15561       game.snapshot.collected_item));
15562
15563   game.snapshot.changed_action = FALSE;
15564   game.snapshot.collected_item = FALSE;
15565   game.snapshot.save_snapshot = save_snapshot;
15566
15567   return save_snapshot;
15568 }
15569
15570 void SaveEngineSnapshotToList(void)
15571 {
15572   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15573       tape.quick_resume)
15574     return;
15575
15576   ListNode *buffers = SaveEngineSnapshotBuffers();
15577
15578   // finally save all snapshot buffers to snapshot list
15579   SaveSnapshotToList(buffers);
15580 }
15581
15582 void SaveEngineSnapshotToListInitial(void)
15583 {
15584   FreeEngineSnapshotList();
15585
15586   SaveEngineSnapshotToList();
15587 }
15588
15589 static void LoadEngineSnapshotValues(void)
15590 {
15591   // restore special values from snapshot structure
15592
15593   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15594     LoadEngineSnapshotValues_RND();
15595   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15596     LoadEngineSnapshotValues_EM();
15597   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15598     LoadEngineSnapshotValues_SP();
15599   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15600     LoadEngineSnapshotValues_MM();
15601 }
15602
15603 void LoadEngineSnapshotSingle(void)
15604 {
15605   LoadSnapshotSingle();
15606
15607   LoadEngineSnapshotValues();
15608 }
15609
15610 static void LoadEngineSnapshot_Undo(int steps)
15611 {
15612   LoadSnapshotFromList_Older(steps);
15613
15614   LoadEngineSnapshotValues();
15615 }
15616
15617 static void LoadEngineSnapshot_Redo(int steps)
15618 {
15619   LoadSnapshotFromList_Newer(steps);
15620
15621   LoadEngineSnapshotValues();
15622 }
15623
15624 boolean CheckEngineSnapshotSingle(void)
15625 {
15626   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15627           snapshot_level_nr == level_nr);
15628 }
15629
15630 boolean CheckEngineSnapshotList(void)
15631 {
15632   return CheckSnapshotList();
15633 }
15634
15635
15636 // ---------- new game button stuff -------------------------------------------
15637
15638 static struct
15639 {
15640   int graphic;
15641   struct XY *pos;
15642   int gadget_id;
15643   boolean *setup_value;
15644   boolean allowed_on_tape;
15645   boolean is_touch_button;
15646   char *infotext;
15647 } gamebutton_info[NUM_GAME_BUTTONS] =
15648 {
15649   {
15650     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15651     GAME_CTRL_ID_STOP,                          NULL,
15652     TRUE, FALSE,                                "stop game"
15653   },
15654   {
15655     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15656     GAME_CTRL_ID_PAUSE,                         NULL,
15657     TRUE, FALSE,                                "pause game"
15658   },
15659   {
15660     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15661     GAME_CTRL_ID_PLAY,                          NULL,
15662     TRUE, FALSE,                                "play game"
15663   },
15664   {
15665     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15666     GAME_CTRL_ID_UNDO,                          NULL,
15667     TRUE, FALSE,                                "undo step"
15668   },
15669   {
15670     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15671     GAME_CTRL_ID_REDO,                          NULL,
15672     TRUE, FALSE,                                "redo step"
15673   },
15674   {
15675     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15676     GAME_CTRL_ID_SAVE,                          NULL,
15677     TRUE, FALSE,                                "save game"
15678   },
15679   {
15680     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15681     GAME_CTRL_ID_PAUSE2,                        NULL,
15682     TRUE, FALSE,                                "pause game"
15683   },
15684   {
15685     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15686     GAME_CTRL_ID_LOAD,                          NULL,
15687     TRUE, FALSE,                                "load game"
15688   },
15689   {
15690     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15691     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15692     FALSE, FALSE,                               "stop game"
15693   },
15694   {
15695     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15696     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15697     FALSE, FALSE,                               "pause game"
15698   },
15699   {
15700     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15701     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15702     FALSE, FALSE,                               "play game"
15703   },
15704   {
15705     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15706     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15707     FALSE, TRUE,                                "stop game"
15708   },
15709   {
15710     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15711     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15712     FALSE, TRUE,                                "pause game"
15713   },
15714   {
15715     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15716     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15717     TRUE, FALSE,                                "background music on/off"
15718   },
15719   {
15720     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15721     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15722     TRUE, FALSE,                                "sound loops on/off"
15723   },
15724   {
15725     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15726     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15727     TRUE, FALSE,                                "normal sounds on/off"
15728   },
15729   {
15730     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15731     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15732     FALSE, FALSE,                               "background music on/off"
15733   },
15734   {
15735     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15736     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15737     FALSE, FALSE,                               "sound loops on/off"
15738   },
15739   {
15740     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15741     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15742     FALSE, FALSE,                               "normal sounds on/off"
15743   }
15744 };
15745
15746 void CreateGameButtons(void)
15747 {
15748   int i;
15749
15750   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15751   {
15752     int graphic = gamebutton_info[i].graphic;
15753     struct GraphicInfo *gfx = &graphic_info[graphic];
15754     struct XY *pos = gamebutton_info[i].pos;
15755     struct GadgetInfo *gi;
15756     int button_type;
15757     boolean checked;
15758     unsigned int event_mask;
15759     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15760     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15761     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15762     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15763     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15764     int gd_x   = gfx->src_x;
15765     int gd_y   = gfx->src_y;
15766     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15767     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15768     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15769     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15770     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15771     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15772     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15773     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15774     int id = i;
15775
15776     if (gfx->bitmap == NULL)
15777     {
15778       game_gadget[id] = NULL;
15779
15780       continue;
15781     }
15782
15783     if (id == GAME_CTRL_ID_STOP ||
15784         id == GAME_CTRL_ID_PANEL_STOP ||
15785         id == GAME_CTRL_ID_TOUCH_STOP ||
15786         id == GAME_CTRL_ID_PLAY ||
15787         id == GAME_CTRL_ID_PANEL_PLAY ||
15788         id == GAME_CTRL_ID_SAVE ||
15789         id == GAME_CTRL_ID_LOAD)
15790     {
15791       button_type = GD_TYPE_NORMAL_BUTTON;
15792       checked = FALSE;
15793       event_mask = GD_EVENT_RELEASED;
15794     }
15795     else if (id == GAME_CTRL_ID_UNDO ||
15796              id == GAME_CTRL_ID_REDO)
15797     {
15798       button_type = GD_TYPE_NORMAL_BUTTON;
15799       checked = FALSE;
15800       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15801     }
15802     else
15803     {
15804       button_type = GD_TYPE_CHECK_BUTTON;
15805       checked = (gamebutton_info[i].setup_value != NULL ?
15806                  *gamebutton_info[i].setup_value : FALSE);
15807       event_mask = GD_EVENT_PRESSED;
15808     }
15809
15810     gi = CreateGadget(GDI_CUSTOM_ID, id,
15811                       GDI_IMAGE_ID, graphic,
15812                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15813                       GDI_X, base_x + x,
15814                       GDI_Y, base_y + y,
15815                       GDI_WIDTH, gfx->width,
15816                       GDI_HEIGHT, gfx->height,
15817                       GDI_TYPE, button_type,
15818                       GDI_STATE, GD_BUTTON_UNPRESSED,
15819                       GDI_CHECKED, checked,
15820                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15821                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15822                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15823                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15824                       GDI_DIRECT_DRAW, FALSE,
15825                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15826                       GDI_EVENT_MASK, event_mask,
15827                       GDI_CALLBACK_ACTION, HandleGameButtons,
15828                       GDI_END);
15829
15830     if (gi == NULL)
15831       Error(ERR_EXIT, "cannot create gadget");
15832
15833     game_gadget[id] = gi;
15834   }
15835 }
15836
15837 void FreeGameButtons(void)
15838 {
15839   int i;
15840
15841   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15842     FreeGadget(game_gadget[i]);
15843 }
15844
15845 static void UnmapGameButtonsAtSamePosition(int id)
15846 {
15847   int i;
15848
15849   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15850     if (i != id &&
15851         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15852         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15853       UnmapGadget(game_gadget[i]);
15854 }
15855
15856 static void UnmapGameButtonsAtSamePosition_All(void)
15857 {
15858   if (setup.show_snapshot_buttons)
15859   {
15860     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15861     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15862     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15863   }
15864   else
15865   {
15866     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15867     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15868     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15869
15870     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15871     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15872     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15873   }
15874 }
15875
15876 static void MapGameButtonsAtSamePosition(int id)
15877 {
15878   int i;
15879
15880   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15881     if (i != id &&
15882         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15883         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15884       MapGadget(game_gadget[i]);
15885
15886   UnmapGameButtonsAtSamePosition_All();
15887 }
15888
15889 void MapUndoRedoButtons(void)
15890 {
15891   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15892   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15893
15894   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15895   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15896 }
15897
15898 void UnmapUndoRedoButtons(void)
15899 {
15900   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15901   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15902
15903   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15904   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15905 }
15906
15907 void ModifyPauseButtons(void)
15908 {
15909   static int ids[] =
15910   {
15911     GAME_CTRL_ID_PAUSE,
15912     GAME_CTRL_ID_PAUSE2,
15913     GAME_CTRL_ID_PANEL_PAUSE,
15914     GAME_CTRL_ID_TOUCH_PAUSE,
15915     -1
15916   };
15917   int i;
15918
15919   for (i = 0; ids[i] > -1; i++)
15920     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15921 }
15922
15923 static void MapGameButtonsExt(boolean on_tape)
15924 {
15925   int i;
15926
15927   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15928     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15929         i != GAME_CTRL_ID_UNDO &&
15930         i != GAME_CTRL_ID_REDO)
15931       MapGadget(game_gadget[i]);
15932
15933   UnmapGameButtonsAtSamePosition_All();
15934
15935   RedrawGameButtons();
15936 }
15937
15938 static void UnmapGameButtonsExt(boolean on_tape)
15939 {
15940   int i;
15941
15942   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15943     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15944       UnmapGadget(game_gadget[i]);
15945 }
15946
15947 static void RedrawGameButtonsExt(boolean on_tape)
15948 {
15949   int i;
15950
15951   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15952     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15953       RedrawGadget(game_gadget[i]);
15954 }
15955
15956 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15957 {
15958   if (gi == NULL)
15959     return;
15960
15961   gi->checked = state;
15962 }
15963
15964 static void RedrawSoundButtonGadget(int id)
15965 {
15966   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15967              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15968              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15969              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15970              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15971              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15972              id);
15973
15974   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15975   RedrawGadget(game_gadget[id2]);
15976 }
15977
15978 void MapGameButtons(void)
15979 {
15980   MapGameButtonsExt(FALSE);
15981 }
15982
15983 void UnmapGameButtons(void)
15984 {
15985   UnmapGameButtonsExt(FALSE);
15986 }
15987
15988 void RedrawGameButtons(void)
15989 {
15990   RedrawGameButtonsExt(FALSE);
15991 }
15992
15993 void MapGameButtonsOnTape(void)
15994 {
15995   MapGameButtonsExt(TRUE);
15996 }
15997
15998 void UnmapGameButtonsOnTape(void)
15999 {
16000   UnmapGameButtonsExt(TRUE);
16001 }
16002
16003 void RedrawGameButtonsOnTape(void)
16004 {
16005   RedrawGameButtonsExt(TRUE);
16006 }
16007
16008 static void GameUndoRedoExt(void)
16009 {
16010   ClearPlayerAction();
16011
16012   tape.pausing = TRUE;
16013
16014   RedrawPlayfield();
16015   UpdateAndDisplayGameControlValues();
16016
16017   DrawCompleteVideoDisplay();
16018   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16019   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16020   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16021
16022   BackToFront();
16023 }
16024
16025 static void GameUndo(int steps)
16026 {
16027   if (!CheckEngineSnapshotList())
16028     return;
16029
16030   LoadEngineSnapshot_Undo(steps);
16031
16032   GameUndoRedoExt();
16033 }
16034
16035 static void GameRedo(int steps)
16036 {
16037   if (!CheckEngineSnapshotList())
16038     return;
16039
16040   LoadEngineSnapshot_Redo(steps);
16041
16042   GameUndoRedoExt();
16043 }
16044
16045 static void HandleGameButtonsExt(int id, int button)
16046 {
16047   static boolean game_undo_executed = FALSE;
16048   int steps = BUTTON_STEPSIZE(button);
16049   boolean handle_game_buttons =
16050     (game_status == GAME_MODE_PLAYING ||
16051      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16052
16053   if (!handle_game_buttons)
16054     return;
16055
16056   switch (id)
16057   {
16058     case GAME_CTRL_ID_STOP:
16059     case GAME_CTRL_ID_PANEL_STOP:
16060     case GAME_CTRL_ID_TOUCH_STOP:
16061       if (game_status == GAME_MODE_MAIN)
16062         break;
16063
16064       if (tape.playing)
16065         TapeStop();
16066       else
16067         RequestQuitGame(TRUE);
16068
16069       break;
16070
16071     case GAME_CTRL_ID_PAUSE:
16072     case GAME_CTRL_ID_PAUSE2:
16073     case GAME_CTRL_ID_PANEL_PAUSE:
16074     case GAME_CTRL_ID_TOUCH_PAUSE:
16075       if (network.enabled && game_status == GAME_MODE_PLAYING)
16076       {
16077         if (tape.pausing)
16078           SendToServer_ContinuePlaying();
16079         else
16080           SendToServer_PausePlaying();
16081       }
16082       else
16083         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16084
16085       game_undo_executed = FALSE;
16086
16087       break;
16088
16089     case GAME_CTRL_ID_PLAY:
16090     case GAME_CTRL_ID_PANEL_PLAY:
16091       if (game_status == GAME_MODE_MAIN)
16092       {
16093         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16094       }
16095       else if (tape.pausing)
16096       {
16097         if (network.enabled)
16098           SendToServer_ContinuePlaying();
16099         else
16100           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16101       }
16102       break;
16103
16104     case GAME_CTRL_ID_UNDO:
16105       // Important: When using "save snapshot when collecting an item" mode,
16106       // load last (current) snapshot for first "undo" after pressing "pause"
16107       // (else the last-but-one snapshot would be loaded, because the snapshot
16108       // pointer already points to the last snapshot when pressing "pause",
16109       // which is fine for "every step/move" mode, but not for "every collect")
16110       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16111           !game_undo_executed)
16112         steps--;
16113
16114       game_undo_executed = TRUE;
16115
16116       GameUndo(steps);
16117       break;
16118
16119     case GAME_CTRL_ID_REDO:
16120       GameRedo(steps);
16121       break;
16122
16123     case GAME_CTRL_ID_SAVE:
16124       TapeQuickSave();
16125       break;
16126
16127     case GAME_CTRL_ID_LOAD:
16128       TapeQuickLoad();
16129       break;
16130
16131     case SOUND_CTRL_ID_MUSIC:
16132     case SOUND_CTRL_ID_PANEL_MUSIC:
16133       if (setup.sound_music)
16134       { 
16135         setup.sound_music = FALSE;
16136
16137         FadeMusic();
16138       }
16139       else if (audio.music_available)
16140       { 
16141         setup.sound = setup.sound_music = TRUE;
16142
16143         SetAudioMode(setup.sound);
16144
16145         if (game_status == GAME_MODE_PLAYING)
16146           PlayLevelMusic();
16147       }
16148
16149       RedrawSoundButtonGadget(id);
16150
16151       break;
16152
16153     case SOUND_CTRL_ID_LOOPS:
16154     case SOUND_CTRL_ID_PANEL_LOOPS:
16155       if (setup.sound_loops)
16156         setup.sound_loops = FALSE;
16157       else if (audio.loops_available)
16158       {
16159         setup.sound = setup.sound_loops = TRUE;
16160
16161         SetAudioMode(setup.sound);
16162       }
16163
16164       RedrawSoundButtonGadget(id);
16165
16166       break;
16167
16168     case SOUND_CTRL_ID_SIMPLE:
16169     case SOUND_CTRL_ID_PANEL_SIMPLE:
16170       if (setup.sound_simple)
16171         setup.sound_simple = FALSE;
16172       else if (audio.sound_available)
16173       {
16174         setup.sound = setup.sound_simple = TRUE;
16175
16176         SetAudioMode(setup.sound);
16177       }
16178
16179       RedrawSoundButtonGadget(id);
16180
16181       break;
16182
16183     default:
16184       break;
16185   }
16186 }
16187
16188 static void HandleGameButtons(struct GadgetInfo *gi)
16189 {
16190   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16191 }
16192
16193 void HandleSoundButtonKeys(Key key)
16194 {
16195   if (key == setup.shortcut.sound_simple)
16196     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16197   else if (key == setup.shortcut.sound_loops)
16198     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16199   else if (key == setup.shortcut.sound_music)
16200     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16201 }