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