fixed handling of chain explosions in EM engine for old tapes
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebeNachbarNr(int, int);
1096 void AmoebeUmwandeln(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Feld[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Feld[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Feld[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Feld[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       if (options.debug)
1773       {
1774         printf("- player element %d activated", player->element_nr);
1775         printf(" (local player is %d and currently %s)\n",
1776                local_player->element_nr,
1777                local_player->active ? "active" : "not active");
1778       }
1779     }
1780 #endif
1781
1782     Feld[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788   // always check if player was just killed and should be reanimated
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active && player->killed)
1794       player->reanimated = TRUE; // if player was just killed, reanimate him
1795   }
1796 }
1797
1798 static void InitField(int x, int y, boolean init_game)
1799 {
1800   int element = Feld[x][y];
1801
1802   switch (element)
1803   {
1804     case EL_SP_MURPHY:
1805     case EL_PLAYER_1:
1806     case EL_PLAYER_2:
1807     case EL_PLAYER_3:
1808     case EL_PLAYER_4:
1809       InitPlayerField(x, y, element, init_game);
1810       break;
1811
1812     case EL_SOKOBAN_FIELD_PLAYER:
1813       element = Feld[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817       InitField(x, y, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_EMPTY:
1821       IncrementSokobanFieldsNeeded();
1822       break;
1823
1824     case EL_SOKOBAN_OBJECT:
1825       IncrementSokobanObjectsNeeded();
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880     case EL_SPRING_LEFT:
1881     case EL_SPRING_RIGHT:
1882       InitMovDir(x, y);
1883       break;
1884
1885     case EL_AMOEBA_FULL:
1886     case EL_BD_AMOEBA:
1887       InitAmoebaNr(x, y);
1888       break;
1889
1890     case EL_AMOEBA_DROP:
1891       if (y == lev_fieldy - 1)
1892       {
1893         Feld[x][y] = EL_AMOEBA_GROWING;
1894         Store[x][y] = EL_AMOEBA_WET;
1895       }
1896       break;
1897
1898     case EL_DYNAMITE_ACTIVE:
1899     case EL_SP_DISK_RED_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904       MovDelay[x][y] = 96;
1905       break;
1906
1907     case EL_EM_DYNAMITE_ACTIVE:
1908       MovDelay[x][y] = 32;
1909       break;
1910
1911     case EL_LAMP:
1912       game.lights_still_needed++;
1913       break;
1914
1915     case EL_PENGUIN:
1916       game.friends_still_needed++;
1917       break;
1918
1919     case EL_PIG:
1920     case EL_DRAGON:
1921       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922       break;
1923
1924     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936       if (init_game)
1937       {
1938         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1939         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1940         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1941
1942         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1943         {
1944           game.belt_dir[belt_nr] = belt_dir;
1945           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1946         }
1947         else    // more than one switch -- set it like the first switch
1948         {
1949           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1950         }
1951       }
1952       break;
1953
1954     case EL_LIGHT_SWITCH_ACTIVE:
1955       if (init_game)
1956         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1957       break;
1958
1959     case EL_INVISIBLE_STEELWALL:
1960     case EL_INVISIBLE_WALL:
1961     case EL_INVISIBLE_SAND:
1962       if (game.light_time_left > 0 ||
1963           game.lenses_time_left > 0)
1964         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1965       break;
1966
1967     case EL_EMC_MAGIC_BALL:
1968       if (game.ball_active)
1969         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1970       break;
1971
1972     case EL_EMC_MAGIC_BALL_SWITCH:
1973       if (game.ball_active)
1974         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1975       break;
1976
1977     case EL_TRIGGER_PLAYER:
1978     case EL_TRIGGER_ELEMENT:
1979     case EL_TRIGGER_CE_VALUE:
1980     case EL_TRIGGER_CE_SCORE:
1981     case EL_SELF:
1982     case EL_ANY_ELEMENT:
1983     case EL_CURRENT_CE_VALUE:
1984     case EL_CURRENT_CE_SCORE:
1985     case EL_PREV_CE_1:
1986     case EL_PREV_CE_2:
1987     case EL_PREV_CE_3:
1988     case EL_PREV_CE_4:
1989     case EL_PREV_CE_5:
1990     case EL_PREV_CE_6:
1991     case EL_PREV_CE_7:
1992     case EL_PREV_CE_8:
1993     case EL_NEXT_CE_1:
1994     case EL_NEXT_CE_2:
1995     case EL_NEXT_CE_3:
1996     case EL_NEXT_CE_4:
1997     case EL_NEXT_CE_5:
1998     case EL_NEXT_CE_6:
1999     case EL_NEXT_CE_7:
2000     case EL_NEXT_CE_8:
2001       // reference elements should not be used on the playfield
2002       Feld[x][y] = EL_EMPTY;
2003       break;
2004
2005     default:
2006       if (IS_CUSTOM_ELEMENT(element))
2007       {
2008         if (CAN_MOVE(element))
2009           InitMovDir(x, y);
2010
2011         if (!element_info[element].use_last_ce_value || init_game)
2012           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2013       }
2014       else if (IS_GROUP_ELEMENT(element))
2015       {
2016         Feld[x][y] = GetElementFromGroupElement(element);
2017
2018         InitField(x, y, init_game);
2019       }
2020
2021       break;
2022   }
2023
2024   if (!init_game)
2025     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2026 }
2027
2028 static void InitField_WithBug1(int x, int y, boolean init_game)
2029 {
2030   InitField(x, y, init_game);
2031
2032   // not needed to call InitMovDir() -- already done by InitField()!
2033   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2034       CAN_MOVE(Feld[x][y]))
2035     InitMovDir(x, y);
2036 }
2037
2038 static void InitField_WithBug2(int x, int y, boolean init_game)
2039 {
2040   int old_element = Feld[x][y];
2041
2042   InitField(x, y, init_game);
2043
2044   // not needed to call InitMovDir() -- already done by InitField()!
2045   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2046       CAN_MOVE(old_element) &&
2047       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2048     InitMovDir(x, y);
2049
2050   /* this case is in fact a combination of not less than three bugs:
2051      first, it calls InitMovDir() for elements that can move, although this is
2052      already done by InitField(); then, it checks the element that was at this
2053      field _before_ the call to InitField() (which can change it); lastly, it
2054      was not called for "mole with direction" elements, which were treated as
2055      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2056   */
2057 }
2058
2059 static int get_key_element_from_nr(int key_nr)
2060 {
2061   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2062                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2063                           EL_EM_KEY_1 : EL_KEY_1);
2064
2065   return key_base_element + key_nr;
2066 }
2067
2068 static int get_next_dropped_element(struct PlayerInfo *player)
2069 {
2070   return (player->inventory_size > 0 ?
2071           player->inventory_element[player->inventory_size - 1] :
2072           player->inventory_infinite_element != EL_UNDEFINED ?
2073           player->inventory_infinite_element :
2074           player->dynabombs_left > 0 ?
2075           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2076           EL_UNDEFINED);
2077 }
2078
2079 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2080 {
2081   // pos >= 0: get element from bottom of the stack;
2082   // pos <  0: get element from top of the stack
2083
2084   if (pos < 0)
2085   {
2086     int min_inventory_size = -pos;
2087     int inventory_pos = player->inventory_size - min_inventory_size;
2088     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2089
2090     return (player->inventory_size >= min_inventory_size ?
2091             player->inventory_element[inventory_pos] :
2092             player->inventory_infinite_element != EL_UNDEFINED ?
2093             player->inventory_infinite_element :
2094             player->dynabombs_left >= min_dynabombs_left ?
2095             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2096             EL_UNDEFINED);
2097   }
2098   else
2099   {
2100     int min_dynabombs_left = pos + 1;
2101     int min_inventory_size = pos + 1 - player->dynabombs_left;
2102     int inventory_pos = pos - player->dynabombs_left;
2103
2104     return (player->inventory_infinite_element != EL_UNDEFINED ?
2105             player->inventory_infinite_element :
2106             player->dynabombs_left >= min_dynabombs_left ?
2107             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2108             player->inventory_size >= min_inventory_size ?
2109             player->inventory_element[inventory_pos] :
2110             EL_UNDEFINED);
2111   }
2112 }
2113
2114 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2115 {
2116   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2117   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2118   int compare_result;
2119
2120   if (gpo1->sort_priority != gpo2->sort_priority)
2121     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2122   else
2123     compare_result = gpo1->nr - gpo2->nr;
2124
2125   return compare_result;
2126 }
2127
2128 int getPlayerInventorySize(int player_nr)
2129 {
2130   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2131     return game_em.ply[player_nr]->dynamite;
2132   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2133     return game_sp.red_disk_count;
2134   else
2135     return stored_player[player_nr].inventory_size;
2136 }
2137
2138 static void InitGameControlValues(void)
2139 {
2140   int i;
2141
2142   for (i = 0; game_panel_controls[i].nr != -1; i++)
2143   {
2144     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2145     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2146     struct TextPosInfo *pos = gpc->pos;
2147     int nr = gpc->nr;
2148     int type = gpc->type;
2149
2150     if (nr != i)
2151     {
2152       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2153       Error(ERR_EXIT, "this should not happen -- please debug");
2154     }
2155
2156     // force update of game controls after initialization
2157     gpc->value = gpc->last_value = -1;
2158     gpc->frame = gpc->last_frame = -1;
2159     gpc->gfx_frame = -1;
2160
2161     // determine panel value width for later calculation of alignment
2162     if (type == TYPE_INTEGER || type == TYPE_STRING)
2163     {
2164       pos->width = pos->size * getFontWidth(pos->font);
2165       pos->height = getFontHeight(pos->font);
2166     }
2167     else if (type == TYPE_ELEMENT)
2168     {
2169       pos->width = pos->size;
2170       pos->height = pos->size;
2171     }
2172
2173     // fill structure for game panel draw order
2174     gpo->nr = gpc->nr;
2175     gpo->sort_priority = pos->sort_priority;
2176   }
2177
2178   // sort game panel controls according to sort_priority and control number
2179   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2180         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2181 }
2182
2183 static void UpdatePlayfieldElementCount(void)
2184 {
2185   boolean use_element_count = FALSE;
2186   int i, j, x, y;
2187
2188   // first check if it is needed at all to calculate playfield element count
2189   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2190     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2191       use_element_count = TRUE;
2192
2193   if (!use_element_count)
2194     return;
2195
2196   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2197     element_info[i].element_count = 0;
2198
2199   SCAN_PLAYFIELD(x, y)
2200   {
2201     element_info[Feld[x][y]].element_count++;
2202   }
2203
2204   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2205     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2206       if (IS_IN_GROUP(j, i))
2207         element_info[EL_GROUP_START + i].element_count +=
2208           element_info[j].element_count;
2209 }
2210
2211 static void UpdateGameControlValues(void)
2212 {
2213   int i, k;
2214   int time = (game.LevelSolved ?
2215               game.LevelSolved_CountingTime :
2216               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2217               game_em.lev->time :
2218               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2219               game_sp.time_played :
2220               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2221               game_mm.energy_left :
2222               game.no_time_limit ? TimePlayed : TimeLeft);
2223   int score = (game.LevelSolved ?
2224                game.LevelSolved_CountingScore :
2225                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226                game_em.lev->score :
2227                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2228                game_sp.score :
2229                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2230                game_mm.score :
2231                game.score);
2232   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2233               game_em.lev->gems_needed :
2234               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2235               game_sp.infotrons_still_needed :
2236               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2237               game_mm.kettles_still_needed :
2238               game.gems_still_needed);
2239   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2240                      game_em.lev->gems_needed > 0 :
2241                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2242                      game_sp.infotrons_still_needed > 0 :
2243                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2244                      game_mm.kettles_still_needed > 0 ||
2245                      game_mm.lights_still_needed > 0 :
2246                      game.gems_still_needed > 0 ||
2247                      game.sokoban_fields_still_needed > 0 ||
2248                      game.sokoban_objects_still_needed > 0 ||
2249                      game.lights_still_needed > 0);
2250   int health = (game.LevelSolved ?
2251                 game.LevelSolved_CountingHealth :
2252                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2253                 MM_HEALTH(game_mm.laser_overload_value) :
2254                 game.health);
2255
2256   UpdatePlayfieldElementCount();
2257
2258   // update game panel control values
2259
2260   // used instead of "level_nr" (for network games)
2261   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2262   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2263
2264   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2265   for (i = 0; i < MAX_NUM_KEYS; i++)
2266     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2267   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2268   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2269
2270   if (game.centered_player_nr == -1)
2271   {
2272     for (i = 0; i < MAX_PLAYERS; i++)
2273     {
2274       // only one player in Supaplex game engine
2275       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2276         break;
2277
2278       for (k = 0; k < MAX_NUM_KEYS; k++)
2279       {
2280         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2281         {
2282           if (game_em.ply[i]->keys & (1 << k))
2283             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284               get_key_element_from_nr(k);
2285         }
2286         else if (stored_player[i].key[k])
2287           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2288             get_key_element_from_nr(k);
2289       }
2290
2291       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292         getPlayerInventorySize(i);
2293
2294       if (stored_player[i].num_white_keys > 0)
2295         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2296           EL_DC_KEY_WHITE;
2297
2298       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2299         stored_player[i].num_white_keys;
2300     }
2301   }
2302   else
2303   {
2304     int player_nr = game.centered_player_nr;
2305
2306     for (k = 0; k < MAX_NUM_KEYS; k++)
2307     {
2308       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2309       {
2310         if (game_em.ply[player_nr]->keys & (1 << k))
2311           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2312             get_key_element_from_nr(k);
2313       }
2314       else if (stored_player[player_nr].key[k])
2315         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2316           get_key_element_from_nr(k);
2317     }
2318
2319     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2320       getPlayerInventorySize(player_nr);
2321
2322     if (stored_player[player_nr].num_white_keys > 0)
2323       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2324
2325     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2326       stored_player[player_nr].num_white_keys;
2327   }
2328
2329   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2330   {
2331     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2332       get_inventory_element_from_pos(local_player, i);
2333     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2334       get_inventory_element_from_pos(local_player, -i - 1);
2335   }
2336
2337   game_panel_controls[GAME_PANEL_SCORE].value = score;
2338   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2339
2340   game_panel_controls[GAME_PANEL_TIME].value = time;
2341
2342   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2343   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2344   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2345
2346   if (level.time == 0)
2347     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2348   else
2349     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2350
2351   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2352   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2353
2354   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2355
2356   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2357     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2358      EL_EMPTY);
2359   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2360     local_player->shield_normal_time_left;
2361   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2362     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2363      EL_EMPTY);
2364   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2365     local_player->shield_deadly_time_left;
2366
2367   game_panel_controls[GAME_PANEL_EXIT].value =
2368     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2369
2370   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2371     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2372   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2373     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2374      EL_EMC_MAGIC_BALL_SWITCH);
2375
2376   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2377     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2378   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2379     game.light_time_left;
2380
2381   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2382     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2383   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2384     game.timegate_time_left;
2385
2386   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2387     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2388
2389   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2390     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2391   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2392     game.lenses_time_left;
2393
2394   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2395     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2396   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2397     game.magnify_time_left;
2398
2399   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2400     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2401      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2402      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2403      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2404      EL_BALLOON_SWITCH_NONE);
2405
2406   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2407     local_player->dynabomb_count;
2408   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2409     local_player->dynabomb_size;
2410   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2411     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2412
2413   game_panel_controls[GAME_PANEL_PENGUINS].value =
2414     game.friends_still_needed;
2415
2416   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2417     game.sokoban_objects_still_needed;
2418   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2419     game.sokoban_fields_still_needed;
2420
2421   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2422     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2423
2424   for (i = 0; i < NUM_BELTS; i++)
2425   {
2426     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2427       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2428        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2429     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2430       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2431   }
2432
2433   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2434     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2435   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2436     game.magic_wall_time_left;
2437
2438   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2439     local_player->gravity;
2440
2441   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2442     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2443
2444   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2445     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2446       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2447        game.panel.element[i].id : EL_UNDEFINED);
2448
2449   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2450     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2451       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2452        element_info[game.panel.element_count[i].id].element_count : 0);
2453
2454   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2455     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2456       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2457        element_info[game.panel.ce_score[i].id].collect_score : 0);
2458
2459   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2460     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2461       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2462        element_info[game.panel.ce_score_element[i].id].collect_score :
2463        EL_UNDEFINED);
2464
2465   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2466   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2467   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2468
2469   // update game panel control frames
2470
2471   for (i = 0; game_panel_controls[i].nr != -1; i++)
2472   {
2473     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2474
2475     if (gpc->type == TYPE_ELEMENT)
2476     {
2477       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2478       {
2479         int last_anim_random_frame = gfx.anim_random_frame;
2480         int element = gpc->value;
2481         int graphic = el2panelimg(element);
2482
2483         if (gpc->value != gpc->last_value)
2484         {
2485           gpc->gfx_frame = 0;
2486           gpc->gfx_random = INIT_GFX_RANDOM();
2487         }
2488         else
2489         {
2490           gpc->gfx_frame++;
2491
2492           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2493               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2494             gpc->gfx_random = INIT_GFX_RANDOM();
2495         }
2496
2497         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2498           gfx.anim_random_frame = gpc->gfx_random;
2499
2500         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2501           gpc->gfx_frame = element_info[element].collect_score;
2502
2503         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2504                                               gpc->gfx_frame);
2505
2506         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2507           gfx.anim_random_frame = last_anim_random_frame;
2508       }
2509     }
2510     else if (gpc->type == TYPE_GRAPHIC)
2511     {
2512       if (gpc->graphic != IMG_UNDEFINED)
2513       {
2514         int last_anim_random_frame = gfx.anim_random_frame;
2515         int graphic = gpc->graphic;
2516
2517         if (gpc->value != gpc->last_value)
2518         {
2519           gpc->gfx_frame = 0;
2520           gpc->gfx_random = INIT_GFX_RANDOM();
2521         }
2522         else
2523         {
2524           gpc->gfx_frame++;
2525
2526           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2527               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2528             gpc->gfx_random = INIT_GFX_RANDOM();
2529         }
2530
2531         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2532           gfx.anim_random_frame = gpc->gfx_random;
2533
2534         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2535
2536         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2537           gfx.anim_random_frame = last_anim_random_frame;
2538       }
2539     }
2540   }
2541 }
2542
2543 static void DisplayGameControlValues(void)
2544 {
2545   boolean redraw_panel = FALSE;
2546   int i;
2547
2548   for (i = 0; game_panel_controls[i].nr != -1; i++)
2549   {
2550     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2551
2552     if (PANEL_DEACTIVATED(gpc->pos))
2553       continue;
2554
2555     if (gpc->value == gpc->last_value &&
2556         gpc->frame == gpc->last_frame)
2557       continue;
2558
2559     redraw_panel = TRUE;
2560   }
2561
2562   if (!redraw_panel)
2563     return;
2564
2565   // copy default game door content to main double buffer
2566
2567   // !!! CHECK AGAIN !!!
2568   SetPanelBackground();
2569   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2570   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2571
2572   // redraw game control buttons
2573   RedrawGameButtons();
2574
2575   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2576
2577   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2578   {
2579     int nr = game_panel_order[i].nr;
2580     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2581     struct TextPosInfo *pos = gpc->pos;
2582     int type = gpc->type;
2583     int value = gpc->value;
2584     int frame = gpc->frame;
2585     int size = pos->size;
2586     int font = pos->font;
2587     boolean draw_masked = pos->draw_masked;
2588     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2589
2590     if (PANEL_DEACTIVATED(pos))
2591       continue;
2592
2593     gpc->last_value = value;
2594     gpc->last_frame = frame;
2595
2596     if (type == TYPE_INTEGER)
2597     {
2598       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2599           nr == GAME_PANEL_TIME)
2600       {
2601         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2602
2603         if (use_dynamic_size)           // use dynamic number of digits
2604         {
2605           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2606           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2607           int size2 = size1 + 1;
2608           int font1 = pos->font;
2609           int font2 = pos->font_alt;
2610
2611           size = (value < value_change ? size1 : size2);
2612           font = (value < value_change ? font1 : font2);
2613         }
2614       }
2615
2616       // correct text size if "digits" is zero or less
2617       if (size <= 0)
2618         size = strlen(int2str(value, size));
2619
2620       // dynamically correct text alignment
2621       pos->width = size * getFontWidth(font);
2622
2623       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2624                   int2str(value, size), font, mask_mode);
2625     }
2626     else if (type == TYPE_ELEMENT)
2627     {
2628       int element, graphic;
2629       Bitmap *src_bitmap;
2630       int src_x, src_y;
2631       int width, height;
2632       int dst_x = PANEL_XPOS(pos);
2633       int dst_y = PANEL_YPOS(pos);
2634
2635       if (value != EL_UNDEFINED && value != EL_EMPTY)
2636       {
2637         element = value;
2638         graphic = el2panelimg(value);
2639
2640         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2641
2642         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2643           size = TILESIZE;
2644
2645         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2646                               &src_x, &src_y);
2647
2648         width  = graphic_info[graphic].width  * size / TILESIZE;
2649         height = graphic_info[graphic].height * size / TILESIZE;
2650
2651         if (draw_masked)
2652           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2653                            dst_x, dst_y);
2654         else
2655           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2656                      dst_x, dst_y);
2657       }
2658     }
2659     else if (type == TYPE_GRAPHIC)
2660     {
2661       int graphic        = gpc->graphic;
2662       int graphic_active = gpc->graphic_active;
2663       Bitmap *src_bitmap;
2664       int src_x, src_y;
2665       int width, height;
2666       int dst_x = PANEL_XPOS(pos);
2667       int dst_y = PANEL_YPOS(pos);
2668       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2669                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2670
2671       if (graphic != IMG_UNDEFINED && !skip)
2672       {
2673         if (pos->style == STYLE_REVERSE)
2674           value = 100 - value;
2675
2676         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2677
2678         if (pos->direction & MV_HORIZONTAL)
2679         {
2680           width  = graphic_info[graphic_active].width * value / 100;
2681           height = graphic_info[graphic_active].height;
2682
2683           if (pos->direction == MV_LEFT)
2684           {
2685             src_x += graphic_info[graphic_active].width - width;
2686             dst_x += graphic_info[graphic_active].width - width;
2687           }
2688         }
2689         else
2690         {
2691           width  = graphic_info[graphic_active].width;
2692           height = graphic_info[graphic_active].height * value / 100;
2693
2694           if (pos->direction == MV_UP)
2695           {
2696             src_y += graphic_info[graphic_active].height - height;
2697             dst_y += graphic_info[graphic_active].height - height;
2698           }
2699         }
2700
2701         if (draw_masked)
2702           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2703                            dst_x, dst_y);
2704         else
2705           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2706                      dst_x, dst_y);
2707
2708         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2709
2710         if (pos->direction & MV_HORIZONTAL)
2711         {
2712           if (pos->direction == MV_RIGHT)
2713           {
2714             src_x += width;
2715             dst_x += width;
2716           }
2717           else
2718           {
2719             dst_x = PANEL_XPOS(pos);
2720           }
2721
2722           width = graphic_info[graphic].width - width;
2723         }
2724         else
2725         {
2726           if (pos->direction == MV_DOWN)
2727           {
2728             src_y += height;
2729             dst_y += height;
2730           }
2731           else
2732           {
2733             dst_y = PANEL_YPOS(pos);
2734           }
2735
2736           height = graphic_info[graphic].height - height;
2737         }
2738
2739         if (draw_masked)
2740           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2741                            dst_x, dst_y);
2742         else
2743           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2744                      dst_x, dst_y);
2745       }
2746     }
2747     else if (type == TYPE_STRING)
2748     {
2749       boolean active = (value != 0);
2750       char *state_normal = "off";
2751       char *state_active = "on";
2752       char *state = (active ? state_active : state_normal);
2753       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2754                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2755                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2756                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2757
2758       if (nr == GAME_PANEL_GRAVITY_STATE)
2759       {
2760         int font1 = pos->font;          // (used for normal state)
2761         int font2 = pos->font_alt;      // (used for active state)
2762
2763         font = (active ? font2 : font1);
2764       }
2765
2766       if (s != NULL)
2767       {
2768         char *s_cut;
2769
2770         if (size <= 0)
2771         {
2772           // don't truncate output if "chars" is zero or less
2773           size = strlen(s);
2774
2775           // dynamically correct text alignment
2776           pos->width = size * getFontWidth(font);
2777         }
2778
2779         s_cut = getStringCopyN(s, size);
2780
2781         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2782                     s_cut, font, mask_mode);
2783
2784         free(s_cut);
2785       }
2786     }
2787
2788     redraw_mask |= REDRAW_DOOR_1;
2789   }
2790
2791   SetGameStatus(GAME_MODE_PLAYING);
2792 }
2793
2794 void UpdateAndDisplayGameControlValues(void)
2795 {
2796   if (tape.deactivate_display)
2797     return;
2798
2799   UpdateGameControlValues();
2800   DisplayGameControlValues();
2801 }
2802
2803 #if 0
2804 static void UpdateGameDoorValues(void)
2805 {
2806   UpdateGameControlValues();
2807 }
2808 #endif
2809
2810 void DrawGameDoorValues(void)
2811 {
2812   DisplayGameControlValues();
2813 }
2814
2815
2816 // ============================================================================
2817 // InitGameEngine()
2818 // ----------------------------------------------------------------------------
2819 // initialize game engine due to level / tape version number
2820 // ============================================================================
2821
2822 static void InitGameEngine(void)
2823 {
2824   int i, j, k, l, x, y;
2825
2826   // set game engine from tape file when re-playing, else from level file
2827   game.engine_version = (tape.playing ? tape.engine_version :
2828                          level.game_version);
2829
2830   // set single or multi-player game mode (needed for re-playing tapes)
2831   game.team_mode = setup.team_mode;
2832
2833   if (tape.playing)
2834   {
2835     int num_players = 0;
2836
2837     for (i = 0; i < MAX_PLAYERS; i++)
2838       if (tape.player_participates[i])
2839         num_players++;
2840
2841     // multi-player tapes contain input data for more than one player
2842     game.team_mode = (num_players > 1);
2843   }
2844
2845 #if 0
2846   printf("level %d: level.game_version  == %06d\n", level_nr,
2847          level.game_version);
2848   printf("          tape.file_version   == %06d\n",
2849          tape.file_version);
2850   printf("          tape.game_version   == %06d\n",
2851          tape.game_version);
2852   printf("          tape.engine_version == %06d\n",
2853          tape.engine_version);
2854   printf("       => game.engine_version == %06d [tape mode: %s]\n",
2855          game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2856 #endif
2857
2858   // --------------------------------------------------------------------------
2859   // set flags for bugs and changes according to active game engine version
2860   // --------------------------------------------------------------------------
2861
2862   /*
2863     Summary of bugfix:
2864     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2865
2866     Bug was introduced in version:
2867     2.0.1
2868
2869     Bug was fixed in version:
2870     4.1.4.2
2871
2872     Description:
2873     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2874     but the property "can fall" was missing, which caused some levels to be
2875     unsolvable. This was fixed in version 4.1.4.2.
2876
2877     Affected levels/tapes:
2878     An example for a tape that was fixed by this bugfix is tape 029 from the
2879     level set "rnd_sam_bateman".
2880     The wrong behaviour will still be used for all levels or tapes that were
2881     created/recorded with it. An example for this is tape 023 from the level
2882     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2883   */
2884
2885   boolean use_amoeba_dropping_cannot_fall_bug =
2886     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2887       game.engine_version <= VERSION_IDENT(4,1,4,1)) ||
2888      (tape.playing &&
2889       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2890       tape.game_version <= VERSION_IDENT(4,1,4,1)));
2891
2892   /*
2893     Summary of bugfix/change:
2894     Fixed move speed of elements entering or leaving magic wall.
2895
2896     Fixed/changed in version:
2897     2.0.1
2898
2899     Description:
2900     Before 2.0.1, move speed of elements entering or leaving magic wall was
2901     twice as fast as it is now.
2902     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2903
2904     Affected levels/tapes:
2905     The first condition is generally needed for all levels/tapes before version
2906     2.0.1, which might use the old behaviour before it was changed; known tapes
2907     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2908     The second condition is an exception from the above case and is needed for
2909     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2910     above, but before it was known that this change would break tapes like the
2911     above and was fixed in 4.1.4.2, so that the changed behaviour was active
2912     although the engine version while recording maybe was before 2.0.1. There
2913     are a lot of tapes that are affected by this exception, like tape 006 from
2914     the level set "rnd_conor_mancone".
2915   */
2916
2917   boolean use_old_move_stepsize_for_magic_wall =
2918     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2919      !(tape.playing &&
2920        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2921        tape.game_version <  VERSION_IDENT(4,1,4,2)));
2922
2923   /*
2924     Summary of bugfix/change:
2925     Fixed handling for custom elements that change when pushed by the player.
2926
2927     Fixed/changed in version:
2928     3.1.0
2929
2930     Description:
2931     Before 3.1.0, custom elements that "change when pushing" changed directly
2932     after the player started pushing them (until then handled in "DigField()").
2933     Since 3.1.0, these custom elements are not changed until the "pushing"
2934     move of the element is finished (now handled in "ContinueMoving()").
2935
2936     Affected levels/tapes:
2937     The first condition is generally needed for all levels/tapes before version
2938     3.1.0, which might use the old behaviour before it was changed; known tapes
2939     that are affected are some tapes from the level set "Walpurgis Gardens" by
2940     Jamie Cullen.
2941     The second condition is an exception from the above case and is needed for
2942     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2943     above (including some development versions of 3.1.0), but before it was
2944     known that this change would break tapes like the above and was fixed in
2945     3.1.1, so that the changed behaviour was active although the engine version
2946     while recording maybe was before 3.1.0. There is at least one tape that is
2947     affected by this exception, which is the tape for the one-level set "Bug
2948     Machine" by Juergen Bonhagen.
2949   */
2950
2951   game.use_change_when_pushing_bug =
2952     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2953      !(tape.playing &&
2954        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2955        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2956
2957   /*
2958     Summary of bugfix/change:
2959     Fixed handling for blocking the field the player leaves when moving.
2960
2961     Fixed/changed in version:
2962     3.1.1
2963
2964     Description:
2965     Before 3.1.1, when "block last field when moving" was enabled, the field
2966     the player is leaving when moving was blocked for the time of the move,
2967     and was directly unblocked afterwards. This resulted in the last field
2968     being blocked for exactly one less than the number of frames of one player
2969     move. Additionally, even when blocking was disabled, the last field was
2970     blocked for exactly one frame.
2971     Since 3.1.1, due to changes in player movement handling, the last field
2972     is not blocked at all when blocking is disabled. When blocking is enabled,
2973     the last field is blocked for exactly the number of frames of one player
2974     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2975     last field is blocked for exactly one more than the number of frames of
2976     one player move.
2977
2978     Affected levels/tapes:
2979     (!!! yet to be determined -- probably many !!!)
2980   */
2981
2982   game.use_block_last_field_bug =
2983     (game.engine_version < VERSION_IDENT(3,1,1,0));
2984
2985   /* various special flags and settings for native Emerald Mine game engine */
2986
2987   game_em.use_single_button =
2988     (game.engine_version > VERSION_IDENT(4,0,0,2));
2989
2990   game_em.use_snap_key_bug =
2991     (game.engine_version < VERSION_IDENT(4,0,1,0));
2992
2993   game_em.use_old_explosions =
2994     (game.engine_version < VERSION_IDENT(4,1,4,2));
2995
2996   game_em.use_wrap_around =
2997     (game.engine_version > VERSION_IDENT(4,1,4,1));
2998
2999   // --------------------------------------------------------------------------
3000
3001   // set maximal allowed number of custom element changes per game frame
3002   game.max_num_changes_per_frame = 1;
3003
3004   // default scan direction: scan playfield from top/left to bottom/right
3005   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3006
3007   // dynamically adjust element properties according to game engine version
3008   InitElementPropertiesEngine(game.engine_version);
3009
3010   // ---------- initialize special element properties -------------------------
3011
3012   // "EL_AMOEBA_DROPPING" missed property "can fall" between 2.0.1 and 4.1.4.1
3013   if (use_amoeba_dropping_cannot_fall_bug)
3014     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3015
3016   // ---------- initialize player's initial move delay ------------------------
3017
3018   // dynamically adjust player properties according to level information
3019   for (i = 0; i < MAX_PLAYERS; i++)
3020     game.initial_move_delay_value[i] =
3021       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3022
3023   // dynamically adjust player properties according to game engine version
3024   for (i = 0; i < MAX_PLAYERS; i++)
3025     game.initial_move_delay[i] =
3026       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3027        game.initial_move_delay_value[i] : 0);
3028
3029   // ---------- initialize player's initial push delay ------------------------
3030
3031   // dynamically adjust player properties according to game engine version
3032   game.initial_push_delay_value =
3033     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3034
3035   // ---------- initialize changing elements ----------------------------------
3036
3037   // initialize changing elements information
3038   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3039   {
3040     struct ElementInfo *ei = &element_info[i];
3041
3042     // this pointer might have been changed in the level editor
3043     ei->change = &ei->change_page[0];
3044
3045     if (!IS_CUSTOM_ELEMENT(i))
3046     {
3047       ei->change->target_element = EL_EMPTY_SPACE;
3048       ei->change->delay_fixed = 0;
3049       ei->change->delay_random = 0;
3050       ei->change->delay_frames = 1;
3051     }
3052
3053     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3054     {
3055       ei->has_change_event[j] = FALSE;
3056
3057       ei->event_page_nr[j] = 0;
3058       ei->event_page[j] = &ei->change_page[0];
3059     }
3060   }
3061
3062   // add changing elements from pre-defined list
3063   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3064   {
3065     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3066     struct ElementInfo *ei = &element_info[ch_delay->element];
3067
3068     ei->change->target_element       = ch_delay->target_element;
3069     ei->change->delay_fixed          = ch_delay->change_delay;
3070
3071     ei->change->pre_change_function  = ch_delay->pre_change_function;
3072     ei->change->change_function      = ch_delay->change_function;
3073     ei->change->post_change_function = ch_delay->post_change_function;
3074
3075     ei->change->can_change = TRUE;
3076     ei->change->can_change_or_has_action = TRUE;
3077
3078     ei->has_change_event[CE_DELAY] = TRUE;
3079
3080     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3081     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3082   }
3083
3084   // ---------- initialize internal run-time variables ------------------------
3085
3086   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3087   {
3088     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3089
3090     for (j = 0; j < ei->num_change_pages; j++)
3091     {
3092       ei->change_page[j].can_change_or_has_action =
3093         (ei->change_page[j].can_change |
3094          ei->change_page[j].has_action);
3095     }
3096   }
3097
3098   // add change events from custom element configuration
3099   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3100   {
3101     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3102
3103     for (j = 0; j < ei->num_change_pages; j++)
3104     {
3105       if (!ei->change_page[j].can_change_or_has_action)
3106         continue;
3107
3108       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3109       {
3110         // only add event page for the first page found with this event
3111         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3112         {
3113           ei->has_change_event[k] = TRUE;
3114
3115           ei->event_page_nr[k] = j;
3116           ei->event_page[k] = &ei->change_page[j];
3117         }
3118       }
3119     }
3120   }
3121
3122   // ---------- initialize reference elements in change conditions ------------
3123
3124   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3125   {
3126     int element = EL_CUSTOM_START + i;
3127     struct ElementInfo *ei = &element_info[element];
3128
3129     for (j = 0; j < ei->num_change_pages; j++)
3130     {
3131       int trigger_element = ei->change_page[j].initial_trigger_element;
3132
3133       if (trigger_element >= EL_PREV_CE_8 &&
3134           trigger_element <= EL_NEXT_CE_8)
3135         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3136
3137       ei->change_page[j].trigger_element = trigger_element;
3138     }
3139   }
3140
3141   // ---------- initialize run-time trigger player and element ----------------
3142
3143   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3144   {
3145     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3146
3147     for (j = 0; j < ei->num_change_pages; j++)
3148     {
3149       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3150       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3151       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3152       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3153       ei->change_page[j].actual_trigger_ce_value = 0;
3154       ei->change_page[j].actual_trigger_ce_score = 0;
3155     }
3156   }
3157
3158   // ---------- initialize trigger events -------------------------------------
3159
3160   // initialize trigger events information
3161   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3162     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3163       trigger_events[i][j] = FALSE;
3164
3165   // add trigger events from element change event properties
3166   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3167   {
3168     struct ElementInfo *ei = &element_info[i];
3169
3170     for (j = 0; j < ei->num_change_pages; j++)
3171     {
3172       if (!ei->change_page[j].can_change_or_has_action)
3173         continue;
3174
3175       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3176       {
3177         int trigger_element = ei->change_page[j].trigger_element;
3178
3179         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3180         {
3181           if (ei->change_page[j].has_event[k])
3182           {
3183             if (IS_GROUP_ELEMENT(trigger_element))
3184             {
3185               struct ElementGroupInfo *group =
3186                 element_info[trigger_element].group;
3187
3188               for (l = 0; l < group->num_elements_resolved; l++)
3189                 trigger_events[group->element_resolved[l]][k] = TRUE;
3190             }
3191             else if (trigger_element == EL_ANY_ELEMENT)
3192               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3193                 trigger_events[l][k] = TRUE;
3194             else
3195               trigger_events[trigger_element][k] = TRUE;
3196           }
3197         }
3198       }
3199     }
3200   }
3201
3202   // ---------- initialize push delay -----------------------------------------
3203
3204   // initialize push delay values to default
3205   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3206   {
3207     if (!IS_CUSTOM_ELEMENT(i))
3208     {
3209       // set default push delay values (corrected since version 3.0.7-1)
3210       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3211       {
3212         element_info[i].push_delay_fixed = 2;
3213         element_info[i].push_delay_random = 8;
3214       }
3215       else
3216       {
3217         element_info[i].push_delay_fixed = 8;
3218         element_info[i].push_delay_random = 8;
3219       }
3220     }
3221   }
3222
3223   // set push delay value for certain elements from pre-defined list
3224   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3225   {
3226     int e = push_delay_list[i].element;
3227
3228     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3229     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3230   }
3231
3232   // set push delay value for Supaplex elements for newer engine versions
3233   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3234   {
3235     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3236     {
3237       if (IS_SP_ELEMENT(i))
3238       {
3239         // set SP push delay to just enough to push under a falling zonk
3240         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3241
3242         element_info[i].push_delay_fixed  = delay;
3243         element_info[i].push_delay_random = 0;
3244       }
3245     }
3246   }
3247
3248   // ---------- initialize move stepsize --------------------------------------
3249
3250   // initialize move stepsize values to default
3251   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3252     if (!IS_CUSTOM_ELEMENT(i))
3253       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3254
3255   // set move stepsize value for certain elements from pre-defined list
3256   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3257   {
3258     int e = move_stepsize_list[i].element;
3259
3260     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3261
3262     // set move stepsize value for certain elements for older engine versions
3263     if (use_old_move_stepsize_for_magic_wall)
3264     {
3265       if (e == EL_MAGIC_WALL_FILLING ||
3266           e == EL_MAGIC_WALL_EMPTYING ||
3267           e == EL_BD_MAGIC_WALL_FILLING ||
3268           e == EL_BD_MAGIC_WALL_EMPTYING)
3269         element_info[e].move_stepsize *= 2;
3270     }
3271   }
3272
3273   // ---------- initialize collect score --------------------------------------
3274
3275   // initialize collect score values for custom elements from initial value
3276   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3277     if (IS_CUSTOM_ELEMENT(i))
3278       element_info[i].collect_score = element_info[i].collect_score_initial;
3279
3280   // ---------- initialize collect count --------------------------------------
3281
3282   // initialize collect count values for non-custom elements
3283   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3284     if (!IS_CUSTOM_ELEMENT(i))
3285       element_info[i].collect_count_initial = 0;
3286
3287   // add collect count values for all elements from pre-defined list
3288   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3289     element_info[collect_count_list[i].element].collect_count_initial =
3290       collect_count_list[i].count;
3291
3292   // ---------- initialize access direction -----------------------------------
3293
3294   // initialize access direction values to default (access from every side)
3295   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3296     if (!IS_CUSTOM_ELEMENT(i))
3297       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3298
3299   // set access direction value for certain elements from pre-defined list
3300   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3301     element_info[access_direction_list[i].element].access_direction =
3302       access_direction_list[i].direction;
3303
3304   // ---------- initialize explosion content ----------------------------------
3305   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3306   {
3307     if (IS_CUSTOM_ELEMENT(i))
3308       continue;
3309
3310     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3311     {
3312       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3313
3314       element_info[i].content.e[x][y] =
3315         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3316          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3317          i == EL_PLAYER_3 ? EL_EMERALD :
3318          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3319          i == EL_MOLE ? EL_EMERALD_RED :
3320          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3321          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3322          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3323          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3324          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3325          i == EL_WALL_EMERALD ? EL_EMERALD :
3326          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3327          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3328          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3329          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3330          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3331          i == EL_WALL_PEARL ? EL_PEARL :
3332          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3333          EL_EMPTY);
3334     }
3335   }
3336
3337   // ---------- initialize recursion detection --------------------------------
3338   recursion_loop_depth = 0;
3339   recursion_loop_detected = FALSE;
3340   recursion_loop_element = EL_UNDEFINED;
3341
3342   // ---------- initialize graphics engine ------------------------------------
3343   game.scroll_delay_value =
3344     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3345      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3346      !setup.forced_scroll_delay           ? 0 :
3347      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3348   game.scroll_delay_value =
3349     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3350
3351   // ---------- initialize game engine snapshots ------------------------------
3352   for (i = 0; i < MAX_PLAYERS; i++)
3353     game.snapshot.last_action[i] = 0;
3354   game.snapshot.changed_action = FALSE;
3355   game.snapshot.collected_item = FALSE;
3356   game.snapshot.mode =
3357     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3358      SNAPSHOT_MODE_EVERY_STEP :
3359      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3360      SNAPSHOT_MODE_EVERY_MOVE :
3361      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3362      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3363   game.snapshot.save_snapshot = FALSE;
3364
3365   // ---------- initialize level time for Supaplex engine ---------------------
3366   // Supaplex levels with time limit currently unsupported -- should be added
3367   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3368     level.time = 0;
3369
3370   // ---------- initialize flags for handling game actions --------------------
3371
3372   // set flags for game actions to default values
3373   game.use_key_actions = TRUE;
3374   game.use_mouse_actions = FALSE;
3375
3376   // when using Mirror Magic game engine, handle mouse events only
3377   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3378   {
3379     game.use_key_actions = FALSE;
3380     game.use_mouse_actions = TRUE;
3381   }
3382
3383   // check for custom elements with mouse click events
3384   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3385   {
3386     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3387     {
3388       int element = EL_CUSTOM_START + i;
3389
3390       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3391           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3392           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3393           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3394         game.use_mouse_actions = TRUE;
3395     }
3396   }
3397 }
3398
3399 static int get_num_special_action(int element, int action_first,
3400                                   int action_last)
3401 {
3402   int num_special_action = 0;
3403   int i, j;
3404
3405   for (i = action_first; i <= action_last; i++)
3406   {
3407     boolean found = FALSE;
3408
3409     for (j = 0; j < NUM_DIRECTIONS; j++)
3410       if (el_act_dir2img(element, i, j) !=
3411           el_act_dir2img(element, ACTION_DEFAULT, j))
3412         found = TRUE;
3413
3414     if (found)
3415       num_special_action++;
3416     else
3417       break;
3418   }
3419
3420   return num_special_action;
3421 }
3422
3423
3424 // ============================================================================
3425 // InitGame()
3426 // ----------------------------------------------------------------------------
3427 // initialize and start new game
3428 // ============================================================================
3429
3430 #if DEBUG_INIT_PLAYER
3431 static void DebugPrintPlayerStatus(char *message)
3432 {
3433   int i;
3434
3435   if (!options.debug)
3436     return;
3437
3438   printf("%s:\n", message);
3439
3440   for (i = 0; i < MAX_PLAYERS; i++)
3441   {
3442     struct PlayerInfo *player = &stored_player[i];
3443
3444     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3445            i + 1,
3446            player->present,
3447            player->connected,
3448            player->connected_locally,
3449            player->connected_network,
3450            player->active);
3451
3452     if (local_player == player)
3453       printf(" (local player)");
3454
3455     printf("\n");
3456   }
3457 }
3458 #endif
3459
3460 void InitGame(void)
3461 {
3462   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3463   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3464   int fade_mask = REDRAW_FIELD;
3465
3466   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3467   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3468   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3469   int initial_move_dir = MV_DOWN;
3470   int i, j, x, y;
3471
3472   // required here to update video display before fading (FIX THIS)
3473   DrawMaskedBorder(REDRAW_DOOR_2);
3474
3475   if (!game.restart_level)
3476     CloseDoor(DOOR_CLOSE_1);
3477
3478   SetGameStatus(GAME_MODE_PLAYING);
3479
3480   if (level_editor_test_game)
3481     FadeSkipNextFadeOut();
3482   else
3483     FadeSetEnterScreen();
3484
3485   if (CheckFadeAll())
3486     fade_mask = REDRAW_ALL;
3487
3488   FadeLevelSoundsAndMusic();
3489
3490   ExpireSoundLoops(TRUE);
3491
3492   FadeOut(fade_mask);
3493
3494   if (level_editor_test_game)
3495     FadeSkipNextFadeIn();
3496
3497   // needed if different viewport properties defined for playing
3498   ChangeViewportPropertiesIfNeeded();
3499
3500   ClearField();
3501
3502   DrawCompleteVideoDisplay();
3503
3504   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3505
3506   InitGameEngine();
3507   InitGameControlValues();
3508
3509   // initialize tape actions from game when recording tape
3510   if (tape.recording)
3511   {
3512     tape.use_key_actions   = game.use_key_actions;
3513     tape.use_mouse_actions = game.use_mouse_actions;
3514   }
3515
3516   // don't play tapes over network
3517   network_playing = (network.enabled && !tape.playing);
3518
3519   for (i = 0; i < MAX_PLAYERS; i++)
3520   {
3521     struct PlayerInfo *player = &stored_player[i];
3522
3523     player->index_nr = i;
3524     player->index_bit = (1 << i);
3525     player->element_nr = EL_PLAYER_1 + i;
3526
3527     player->present = FALSE;
3528     player->active = FALSE;
3529     player->mapped = FALSE;
3530
3531     player->killed = FALSE;
3532     player->reanimated = FALSE;
3533     player->buried = FALSE;
3534
3535     player->action = 0;
3536     player->effective_action = 0;
3537     player->programmed_action = 0;
3538     player->snap_action = 0;
3539
3540     player->mouse_action.lx = 0;
3541     player->mouse_action.ly = 0;
3542     player->mouse_action.button = 0;
3543     player->mouse_action.button_hint = 0;
3544
3545     player->effective_mouse_action.lx = 0;
3546     player->effective_mouse_action.ly = 0;
3547     player->effective_mouse_action.button = 0;
3548     player->effective_mouse_action.button_hint = 0;
3549
3550     for (j = 0; j < MAX_NUM_KEYS; j++)
3551       player->key[j] = FALSE;
3552
3553     player->num_white_keys = 0;
3554
3555     player->dynabomb_count = 0;
3556     player->dynabomb_size = 1;
3557     player->dynabombs_left = 0;
3558     player->dynabomb_xl = FALSE;
3559
3560     player->MovDir = initial_move_dir;
3561     player->MovPos = 0;
3562     player->GfxPos = 0;
3563     player->GfxDir = initial_move_dir;
3564     player->GfxAction = ACTION_DEFAULT;
3565     player->Frame = 0;
3566     player->StepFrame = 0;
3567
3568     player->initial_element = player->element_nr;
3569     player->artwork_element =
3570       (level.use_artwork_element[i] ? level.artwork_element[i] :
3571        player->element_nr);
3572     player->use_murphy = FALSE;
3573
3574     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3575     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3576
3577     player->gravity = level.initial_player_gravity[i];
3578
3579     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3580
3581     player->actual_frame_counter = 0;
3582
3583     player->step_counter = 0;
3584
3585     player->last_move_dir = initial_move_dir;
3586
3587     player->is_active = FALSE;
3588
3589     player->is_waiting = FALSE;
3590     player->is_moving = FALSE;
3591     player->is_auto_moving = FALSE;
3592     player->is_digging = FALSE;
3593     player->is_snapping = FALSE;
3594     player->is_collecting = FALSE;
3595     player->is_pushing = FALSE;
3596     player->is_switching = FALSE;
3597     player->is_dropping = FALSE;
3598     player->is_dropping_pressed = FALSE;
3599
3600     player->is_bored = FALSE;
3601     player->is_sleeping = FALSE;
3602
3603     player->was_waiting = TRUE;
3604     player->was_moving = FALSE;
3605     player->was_snapping = FALSE;
3606     player->was_dropping = FALSE;
3607
3608     player->force_dropping = FALSE;
3609
3610     player->frame_counter_bored = -1;
3611     player->frame_counter_sleeping = -1;
3612
3613     player->anim_delay_counter = 0;
3614     player->post_delay_counter = 0;
3615
3616     player->dir_waiting = initial_move_dir;
3617     player->action_waiting = ACTION_DEFAULT;
3618     player->last_action_waiting = ACTION_DEFAULT;
3619     player->special_action_bored = ACTION_DEFAULT;
3620     player->special_action_sleeping = ACTION_DEFAULT;
3621
3622     player->switch_x = -1;
3623     player->switch_y = -1;
3624
3625     player->drop_x = -1;
3626     player->drop_y = -1;
3627
3628     player->show_envelope = 0;
3629
3630     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3631
3632     player->push_delay       = -1;      // initialized when pushing starts
3633     player->push_delay_value = game.initial_push_delay_value;
3634
3635     player->drop_delay = 0;
3636     player->drop_pressed_delay = 0;
3637
3638     player->last_jx = -1;
3639     player->last_jy = -1;
3640     player->jx = -1;
3641     player->jy = -1;
3642
3643     player->shield_normal_time_left = 0;
3644     player->shield_deadly_time_left = 0;
3645
3646     player->inventory_infinite_element = EL_UNDEFINED;
3647     player->inventory_size = 0;
3648
3649     if (level.use_initial_inventory[i])
3650     {
3651       for (j = 0; j < level.initial_inventory_size[i]; j++)
3652       {
3653         int element = level.initial_inventory_content[i][j];
3654         int collect_count = element_info[element].collect_count_initial;
3655         int k;
3656
3657         if (!IS_CUSTOM_ELEMENT(element))
3658           collect_count = 1;
3659
3660         if (collect_count == 0)
3661           player->inventory_infinite_element = element;
3662         else
3663           for (k = 0; k < collect_count; k++)
3664             if (player->inventory_size < MAX_INVENTORY_SIZE)
3665               player->inventory_element[player->inventory_size++] = element;
3666       }
3667     }
3668
3669     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3670     SnapField(player, 0, 0);
3671
3672     map_player_action[i] = i;
3673   }
3674
3675   network_player_action_received = FALSE;
3676
3677   // initial null action
3678   if (network_playing)
3679     SendToServer_MovePlayer(MV_NONE);
3680
3681   FrameCounter = 0;
3682   TimeFrames = 0;
3683   TimePlayed = 0;
3684   TimeLeft = level.time;
3685   TapeTime = 0;
3686
3687   ScreenMovDir = MV_NONE;
3688   ScreenMovPos = 0;
3689   ScreenGfxPos = 0;
3690
3691   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3692
3693   game.robot_wheel_x = -1;
3694   game.robot_wheel_y = -1;
3695
3696   game.exit_x = -1;
3697   game.exit_y = -1;
3698
3699   game.all_players_gone = FALSE;
3700
3701   game.LevelSolved = FALSE;
3702   game.GameOver = FALSE;
3703
3704   game.GamePlayed = !tape.playing;
3705
3706   game.LevelSolved_GameWon = FALSE;
3707   game.LevelSolved_GameEnd = FALSE;
3708   game.LevelSolved_SaveTape = FALSE;
3709   game.LevelSolved_SaveScore = FALSE;
3710
3711   game.LevelSolved_CountingTime = 0;
3712   game.LevelSolved_CountingScore = 0;
3713   game.LevelSolved_CountingHealth = 0;
3714
3715   game.panel.active = TRUE;
3716
3717   game.no_time_limit = (level.time == 0);
3718
3719   game.yamyam_content_nr = 0;
3720   game.robot_wheel_active = FALSE;
3721   game.magic_wall_active = FALSE;
3722   game.magic_wall_time_left = 0;
3723   game.light_time_left = 0;
3724   game.timegate_time_left = 0;
3725   game.switchgate_pos = 0;
3726   game.wind_direction = level.wind_direction_initial;
3727
3728   game.score = 0;
3729   game.score_final = 0;
3730
3731   game.health = MAX_HEALTH;
3732   game.health_final = MAX_HEALTH;
3733
3734   game.gems_still_needed = level.gems_needed;
3735   game.sokoban_fields_still_needed = 0;
3736   game.sokoban_objects_still_needed = 0;
3737   game.lights_still_needed = 0;
3738   game.players_still_needed = 0;
3739   game.friends_still_needed = 0;
3740
3741   game.lenses_time_left = 0;
3742   game.magnify_time_left = 0;
3743
3744   game.ball_active = level.ball_active_initial;
3745   game.ball_content_nr = 0;
3746
3747   game.explosions_delayed = TRUE;
3748
3749   game.envelope_active = FALSE;
3750
3751   for (i = 0; i < NUM_BELTS; i++)
3752   {
3753     game.belt_dir[i] = MV_NONE;
3754     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3755   }
3756
3757   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3758     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3759
3760 #if DEBUG_INIT_PLAYER
3761   DebugPrintPlayerStatus("Player status at level initialization");
3762 #endif
3763
3764   SCAN_PLAYFIELD(x, y)
3765   {
3766     Feld[x][y] = Last[x][y] = level.field[x][y];
3767     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3768     ChangeDelay[x][y] = 0;
3769     ChangePage[x][y] = -1;
3770     CustomValue[x][y] = 0;              // initialized in InitField()
3771     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3772     AmoebaNr[x][y] = 0;
3773     WasJustMoving[x][y] = 0;
3774     WasJustFalling[x][y] = 0;
3775     CheckCollision[x][y] = 0;
3776     CheckImpact[x][y] = 0;
3777     Stop[x][y] = FALSE;
3778     Pushed[x][y] = FALSE;
3779
3780     ChangeCount[x][y] = 0;
3781     ChangeEvent[x][y] = -1;
3782
3783     ExplodePhase[x][y] = 0;
3784     ExplodeDelay[x][y] = 0;
3785     ExplodeField[x][y] = EX_TYPE_NONE;
3786
3787     RunnerVisit[x][y] = 0;
3788     PlayerVisit[x][y] = 0;
3789
3790     GfxFrame[x][y] = 0;
3791     GfxRandom[x][y] = INIT_GFX_RANDOM();
3792     GfxElement[x][y] = EL_UNDEFINED;
3793     GfxAction[x][y] = ACTION_DEFAULT;
3794     GfxDir[x][y] = MV_NONE;
3795     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3796   }
3797
3798   SCAN_PLAYFIELD(x, y)
3799   {
3800     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3801       emulate_bd = FALSE;
3802     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3803       emulate_sb = FALSE;
3804     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3805       emulate_sp = FALSE;
3806
3807     InitField(x, y, TRUE);
3808
3809     ResetGfxAnimation(x, y);
3810   }
3811
3812   InitBeltMovement();
3813
3814   for (i = 0; i < MAX_PLAYERS; i++)
3815   {
3816     struct PlayerInfo *player = &stored_player[i];
3817
3818     // set number of special actions for bored and sleeping animation
3819     player->num_special_action_bored =
3820       get_num_special_action(player->artwork_element,
3821                              ACTION_BORING_1, ACTION_BORING_LAST);
3822     player->num_special_action_sleeping =
3823       get_num_special_action(player->artwork_element,
3824                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3825   }
3826
3827   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3828                     emulate_sb ? EMU_SOKOBAN :
3829                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3830
3831   // initialize type of slippery elements
3832   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3833   {
3834     if (!IS_CUSTOM_ELEMENT(i))
3835     {
3836       // default: elements slip down either to the left or right randomly
3837       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3838
3839       // SP style elements prefer to slip down on the left side
3840       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3841         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3842
3843       // BD style elements prefer to slip down on the left side
3844       if (game.emulation == EMU_BOULDERDASH)
3845         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3846     }
3847   }
3848
3849   // initialize explosion and ignition delay
3850   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3851   {
3852     if (!IS_CUSTOM_ELEMENT(i))
3853     {
3854       int num_phase = 8;
3855       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3856                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3857                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3858       int last_phase = (num_phase + 1) * delay;
3859       int half_phase = (num_phase / 2) * delay;
3860
3861       element_info[i].explosion_delay = last_phase - 1;
3862       element_info[i].ignition_delay = half_phase;
3863
3864       if (i == EL_BLACK_ORB)
3865         element_info[i].ignition_delay = 1;
3866     }
3867   }
3868
3869   // correct non-moving belts to start moving left
3870   for (i = 0; i < NUM_BELTS; i++)
3871     if (game.belt_dir[i] == MV_NONE)
3872       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3873
3874 #if USE_NEW_PLAYER_ASSIGNMENTS
3875   // use preferred player also in local single-player mode
3876   if (!network.enabled && !game.team_mode)
3877   {
3878     int new_index_nr = setup.network_player_nr;
3879
3880     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3881     {
3882       for (i = 0; i < MAX_PLAYERS; i++)
3883         stored_player[i].connected_locally = FALSE;
3884
3885       stored_player[new_index_nr].connected_locally = TRUE;
3886     }
3887   }
3888
3889   for (i = 0; i < MAX_PLAYERS; i++)
3890   {
3891     stored_player[i].connected = FALSE;
3892
3893     // in network game mode, the local player might not be the first player
3894     if (stored_player[i].connected_locally)
3895       local_player = &stored_player[i];
3896   }
3897
3898   if (!network.enabled)
3899     local_player->connected = TRUE;
3900
3901   if (tape.playing)
3902   {
3903     for (i = 0; i < MAX_PLAYERS; i++)
3904       stored_player[i].connected = tape.player_participates[i];
3905   }
3906   else if (network.enabled)
3907   {
3908     // add team mode players connected over the network (needed for correct
3909     // assignment of player figures from level to locally playing players)
3910
3911     for (i = 0; i < MAX_PLAYERS; i++)
3912       if (stored_player[i].connected_network)
3913         stored_player[i].connected = TRUE;
3914   }
3915   else if (game.team_mode)
3916   {
3917     // try to guess locally connected team mode players (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 (setup.input[i].use_joystick ||
3922           setup.input[i].key.left != KSYM_UNDEFINED)
3923         stored_player[i].connected = TRUE;
3924   }
3925
3926 #if DEBUG_INIT_PLAYER
3927   DebugPrintPlayerStatus("Player status after level initialization");
3928 #endif
3929
3930 #if DEBUG_INIT_PLAYER
3931   if (options.debug)
3932     printf("Reassigning players ...\n");
3933 #endif
3934
3935   // check if any connected player was not found in playfield
3936   for (i = 0; i < MAX_PLAYERS; i++)
3937   {
3938     struct PlayerInfo *player = &stored_player[i];
3939
3940     if (player->connected && !player->present)
3941     {
3942       struct PlayerInfo *field_player = NULL;
3943
3944 #if DEBUG_INIT_PLAYER
3945       if (options.debug)
3946         printf("- looking for field player for player %d ...\n", i + 1);
3947 #endif
3948
3949       // assign first free player found that is present in the playfield
3950
3951       // first try: look for unmapped playfield player that is not connected
3952       for (j = 0; j < MAX_PLAYERS; j++)
3953         if (field_player == NULL &&
3954             stored_player[j].present &&
3955             !stored_player[j].mapped &&
3956             !stored_player[j].connected)
3957           field_player = &stored_player[j];
3958
3959       // second try: look for *any* unmapped playfield player
3960       for (j = 0; j < MAX_PLAYERS; j++)
3961         if (field_player == NULL &&
3962             stored_player[j].present &&
3963             !stored_player[j].mapped)
3964           field_player = &stored_player[j];
3965
3966       if (field_player != NULL)
3967       {
3968         int jx = field_player->jx, jy = field_player->jy;
3969
3970 #if DEBUG_INIT_PLAYER
3971         if (options.debug)
3972           printf("- found player %d\n", field_player->index_nr + 1);
3973 #endif
3974
3975         player->present = FALSE;
3976         player->active = FALSE;
3977
3978         field_player->present = TRUE;
3979         field_player->active = TRUE;
3980
3981         /*
3982         player->initial_element = field_player->initial_element;
3983         player->artwork_element = field_player->artwork_element;
3984
3985         player->block_last_field       = field_player->block_last_field;
3986         player->block_delay_adjustment = field_player->block_delay_adjustment;
3987         */
3988
3989         StorePlayer[jx][jy] = field_player->element_nr;
3990
3991         field_player->jx = field_player->last_jx = jx;
3992         field_player->jy = field_player->last_jy = jy;
3993
3994         if (local_player == player)
3995           local_player = field_player;
3996
3997         map_player_action[field_player->index_nr] = i;
3998
3999         field_player->mapped = TRUE;
4000
4001 #if DEBUG_INIT_PLAYER
4002         if (options.debug)
4003           printf("- map_player_action[%d] == %d\n",
4004                  field_player->index_nr + 1, i + 1);
4005 #endif
4006       }
4007     }
4008
4009     if (player->connected && player->present)
4010       player->mapped = TRUE;
4011   }
4012
4013 #if DEBUG_INIT_PLAYER
4014   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4015 #endif
4016
4017 #else
4018
4019   // check if any connected player was not found in playfield
4020   for (i = 0; i < MAX_PLAYERS; i++)
4021   {
4022     struct PlayerInfo *player = &stored_player[i];
4023
4024     if (player->connected && !player->present)
4025     {
4026       for (j = 0; j < MAX_PLAYERS; j++)
4027       {
4028         struct PlayerInfo *field_player = &stored_player[j];
4029         int jx = field_player->jx, jy = field_player->jy;
4030
4031         // assign first free player found that is present in the playfield
4032         if (field_player->present && !field_player->connected)
4033         {
4034           player->present = TRUE;
4035           player->active = TRUE;
4036
4037           field_player->present = FALSE;
4038           field_player->active = FALSE;
4039
4040           player->initial_element = field_player->initial_element;
4041           player->artwork_element = field_player->artwork_element;
4042
4043           player->block_last_field       = field_player->block_last_field;
4044           player->block_delay_adjustment = field_player->block_delay_adjustment;
4045
4046           StorePlayer[jx][jy] = player->element_nr;
4047
4048           player->jx = player->last_jx = jx;
4049           player->jy = player->last_jy = jy;
4050
4051           break;
4052         }
4053       }
4054     }
4055   }
4056 #endif
4057
4058 #if 0
4059   printf("::: local_player->present == %d\n", local_player->present);
4060 #endif
4061
4062   // set focus to local player for network games, else to all players
4063   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4064   game.centered_player_nr_next = game.centered_player_nr;
4065   game.set_centered_player = FALSE;
4066   game.set_centered_player_wrap = FALSE;
4067
4068   if (network_playing && tape.recording)
4069   {
4070     // store client dependent player focus when recording network games
4071     tape.centered_player_nr_next = game.centered_player_nr_next;
4072     tape.set_centered_player = TRUE;
4073   }
4074
4075   if (tape.playing)
4076   {
4077     // when playing a tape, eliminate all players who do not participate
4078
4079 #if USE_NEW_PLAYER_ASSIGNMENTS
4080
4081     if (!game.team_mode)
4082     {
4083       for (i = 0; i < MAX_PLAYERS; i++)
4084       {
4085         if (stored_player[i].active &&
4086             !tape.player_participates[map_player_action[i]])
4087         {
4088           struct PlayerInfo *player = &stored_player[i];
4089           int jx = player->jx, jy = player->jy;
4090
4091 #if DEBUG_INIT_PLAYER
4092           if (options.debug)
4093             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4094 #endif
4095
4096           player->active = FALSE;
4097           StorePlayer[jx][jy] = 0;
4098           Feld[jx][jy] = EL_EMPTY;
4099         }
4100       }
4101     }
4102
4103 #else
4104
4105     for (i = 0; i < MAX_PLAYERS; i++)
4106     {
4107       if (stored_player[i].active &&
4108           !tape.player_participates[i])
4109       {
4110         struct PlayerInfo *player = &stored_player[i];
4111         int jx = player->jx, jy = player->jy;
4112
4113         player->active = FALSE;
4114         StorePlayer[jx][jy] = 0;
4115         Feld[jx][jy] = EL_EMPTY;
4116       }
4117     }
4118 #endif
4119   }
4120   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4121   {
4122     // when in single player mode, eliminate all but the local player
4123
4124     for (i = 0; i < MAX_PLAYERS; i++)
4125     {
4126       struct PlayerInfo *player = &stored_player[i];
4127
4128       if (player->active && player != local_player)
4129       {
4130         int jx = player->jx, jy = player->jy;
4131
4132         player->active = FALSE;
4133         player->present = FALSE;
4134
4135         StorePlayer[jx][jy] = 0;
4136         Feld[jx][jy] = EL_EMPTY;
4137       }
4138     }
4139   }
4140
4141   for (i = 0; i < MAX_PLAYERS; i++)
4142     if (stored_player[i].active)
4143       game.players_still_needed++;
4144
4145   if (level.solved_by_one_player)
4146     game.players_still_needed = 1;
4147
4148   // when recording the game, store which players take part in the game
4149   if (tape.recording)
4150   {
4151 #if USE_NEW_PLAYER_ASSIGNMENTS
4152     for (i = 0; i < MAX_PLAYERS; i++)
4153       if (stored_player[i].connected)
4154         tape.player_participates[i] = TRUE;
4155 #else
4156     for (i = 0; i < MAX_PLAYERS; i++)
4157       if (stored_player[i].active)
4158         tape.player_participates[i] = TRUE;
4159 #endif
4160   }
4161
4162 #if DEBUG_INIT_PLAYER
4163   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4164 #endif
4165
4166   if (BorderElement == EL_EMPTY)
4167   {
4168     SBX_Left = 0;
4169     SBX_Right = lev_fieldx - SCR_FIELDX;
4170     SBY_Upper = 0;
4171     SBY_Lower = lev_fieldy - SCR_FIELDY;
4172   }
4173   else
4174   {
4175     SBX_Left = -1;
4176     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4177     SBY_Upper = -1;
4178     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4179   }
4180
4181   if (full_lev_fieldx <= SCR_FIELDX)
4182     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4183   if (full_lev_fieldy <= SCR_FIELDY)
4184     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4185
4186   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4187     SBX_Left--;
4188   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4189     SBY_Upper--;
4190
4191   // if local player not found, look for custom element that might create
4192   // the player (make some assumptions about the right custom element)
4193   if (!local_player->present)
4194   {
4195     int start_x = 0, start_y = 0;
4196     int found_rating = 0;
4197     int found_element = EL_UNDEFINED;
4198     int player_nr = local_player->index_nr;
4199
4200     SCAN_PLAYFIELD(x, y)
4201     {
4202       int element = Feld[x][y];
4203       int content;
4204       int xx, yy;
4205       boolean is_player;
4206
4207       if (level.use_start_element[player_nr] &&
4208           level.start_element[player_nr] == element &&
4209           found_rating < 4)
4210       {
4211         start_x = x;
4212         start_y = y;
4213
4214         found_rating = 4;
4215         found_element = element;
4216       }
4217
4218       if (!IS_CUSTOM_ELEMENT(element))
4219         continue;
4220
4221       if (CAN_CHANGE(element))
4222       {
4223         for (i = 0; i < element_info[element].num_change_pages; i++)
4224         {
4225           // check for player created from custom element as single target
4226           content = element_info[element].change_page[i].target_element;
4227           is_player = ELEM_IS_PLAYER(content);
4228
4229           if (is_player && (found_rating < 3 ||
4230                             (found_rating == 3 && element < found_element)))
4231           {
4232             start_x = x;
4233             start_y = y;
4234
4235             found_rating = 3;
4236             found_element = element;
4237           }
4238         }
4239       }
4240
4241       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4242       {
4243         // check for player created from custom element as explosion content
4244         content = element_info[element].content.e[xx][yy];
4245         is_player = ELEM_IS_PLAYER(content);
4246
4247         if (is_player && (found_rating < 2 ||
4248                           (found_rating == 2 && element < found_element)))
4249         {
4250           start_x = x + xx - 1;
4251           start_y = y + yy - 1;
4252
4253           found_rating = 2;
4254           found_element = element;
4255         }
4256
4257         if (!CAN_CHANGE(element))
4258           continue;
4259
4260         for (i = 0; i < element_info[element].num_change_pages; i++)
4261         {
4262           // check for player created from custom element as extended target
4263           content =
4264             element_info[element].change_page[i].target_content.e[xx][yy];
4265
4266           is_player = ELEM_IS_PLAYER(content);
4267
4268           if (is_player && (found_rating < 1 ||
4269                             (found_rating == 1 && element < found_element)))
4270           {
4271             start_x = x + xx - 1;
4272             start_y = y + yy - 1;
4273
4274             found_rating = 1;
4275             found_element = element;
4276           }
4277         }
4278       }
4279     }
4280
4281     scroll_x = SCROLL_POSITION_X(start_x);
4282     scroll_y = SCROLL_POSITION_Y(start_y);
4283   }
4284   else
4285   {
4286     scroll_x = SCROLL_POSITION_X(local_player->jx);
4287     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4288   }
4289
4290   // !!! FIX THIS (START) !!!
4291   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4292   {
4293     InitGameEngine_EM();
4294   }
4295   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4296   {
4297     InitGameEngine_SP();
4298   }
4299   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4300   {
4301     InitGameEngine_MM();
4302   }
4303   else
4304   {
4305     DrawLevel(REDRAW_FIELD);
4306     DrawAllPlayers();
4307
4308     // after drawing the level, correct some elements
4309     if (game.timegate_time_left == 0)
4310       CloseAllOpenTimegates();
4311   }
4312
4313   // blit playfield from scroll buffer to normal back buffer for fading in
4314   BlitScreenToBitmap(backbuffer);
4315   // !!! FIX THIS (END) !!!
4316
4317   DrawMaskedBorder(fade_mask);
4318
4319   FadeIn(fade_mask);
4320
4321 #if 1
4322   // full screen redraw is required at this point in the following cases:
4323   // - special editor door undrawn when game was started from level editor
4324   // - drawing area (playfield) was changed and has to be removed completely
4325   redraw_mask = REDRAW_ALL;
4326   BackToFront();
4327 #endif
4328
4329   if (!game.restart_level)
4330   {
4331     // copy default game door content to main double buffer
4332
4333     // !!! CHECK AGAIN !!!
4334     SetPanelBackground();
4335     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4336     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4337   }
4338
4339   SetPanelBackground();
4340   SetDrawBackgroundMask(REDRAW_DOOR_1);
4341
4342   UpdateAndDisplayGameControlValues();
4343
4344   if (!game.restart_level)
4345   {
4346     UnmapGameButtons();
4347     UnmapTapeButtons();
4348
4349     FreeGameButtons();
4350     CreateGameButtons();
4351
4352     MapGameButtons();
4353     MapTapeButtons();
4354
4355     // copy actual game door content to door double buffer for OpenDoor()
4356     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4357
4358     OpenDoor(DOOR_OPEN_ALL);
4359
4360     KeyboardAutoRepeatOffUnlessAutoplay();
4361
4362 #if DEBUG_INIT_PLAYER
4363     DebugPrintPlayerStatus("Player status (final)");
4364 #endif
4365   }
4366
4367   UnmapAllGadgets();
4368
4369   MapGameButtons();
4370   MapTapeButtons();
4371
4372   if (!game.restart_level && !tape.playing)
4373   {
4374     LevelStats_incPlayed(level_nr);
4375
4376     SaveLevelSetup_SeriesInfo();
4377   }
4378
4379   game.restart_level = FALSE;
4380   game.restart_game_message = NULL;
4381   game.request_active = FALSE;
4382
4383   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4384     InitGameActions_MM();
4385
4386   SaveEngineSnapshotToListInitial();
4387
4388   if (!game.restart_level)
4389   {
4390     PlaySound(SND_GAME_STARTING);
4391
4392     if (setup.sound_music)
4393       PlayLevelMusic();
4394   }
4395 }
4396
4397 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4398                         int actual_player_x, int actual_player_y)
4399 {
4400   // this is used for non-R'n'D game engines to update certain engine values
4401
4402   // needed to determine if sounds are played within the visible screen area
4403   scroll_x = actual_scroll_x;
4404   scroll_y = actual_scroll_y;
4405
4406   // needed to get player position for "follow finger" playing input method
4407   local_player->jx = actual_player_x;
4408   local_player->jy = actual_player_y;
4409 }
4410
4411 void InitMovDir(int x, int y)
4412 {
4413   int i, element = Feld[x][y];
4414   static int xy[4][2] =
4415   {
4416     {  0, +1 },
4417     { +1,  0 },
4418     {  0, -1 },
4419     { -1,  0 }
4420   };
4421   static int direction[3][4] =
4422   {
4423     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4424     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4425     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4426   };
4427
4428   switch (element)
4429   {
4430     case EL_BUG_RIGHT:
4431     case EL_BUG_UP:
4432     case EL_BUG_LEFT:
4433     case EL_BUG_DOWN:
4434       Feld[x][y] = EL_BUG;
4435       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4436       break;
4437
4438     case EL_SPACESHIP_RIGHT:
4439     case EL_SPACESHIP_UP:
4440     case EL_SPACESHIP_LEFT:
4441     case EL_SPACESHIP_DOWN:
4442       Feld[x][y] = EL_SPACESHIP;
4443       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4444       break;
4445
4446     case EL_BD_BUTTERFLY_RIGHT:
4447     case EL_BD_BUTTERFLY_UP:
4448     case EL_BD_BUTTERFLY_LEFT:
4449     case EL_BD_BUTTERFLY_DOWN:
4450       Feld[x][y] = EL_BD_BUTTERFLY;
4451       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4452       break;
4453
4454     case EL_BD_FIREFLY_RIGHT:
4455     case EL_BD_FIREFLY_UP:
4456     case EL_BD_FIREFLY_LEFT:
4457     case EL_BD_FIREFLY_DOWN:
4458       Feld[x][y] = EL_BD_FIREFLY;
4459       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4460       break;
4461
4462     case EL_PACMAN_RIGHT:
4463     case EL_PACMAN_UP:
4464     case EL_PACMAN_LEFT:
4465     case EL_PACMAN_DOWN:
4466       Feld[x][y] = EL_PACMAN;
4467       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4468       break;
4469
4470     case EL_YAMYAM_LEFT:
4471     case EL_YAMYAM_RIGHT:
4472     case EL_YAMYAM_UP:
4473     case EL_YAMYAM_DOWN:
4474       Feld[x][y] = EL_YAMYAM;
4475       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4476       break;
4477
4478     case EL_SP_SNIKSNAK:
4479       MovDir[x][y] = MV_UP;
4480       break;
4481
4482     case EL_SP_ELECTRON:
4483       MovDir[x][y] = MV_LEFT;
4484       break;
4485
4486     case EL_MOLE_LEFT:
4487     case EL_MOLE_RIGHT:
4488     case EL_MOLE_UP:
4489     case EL_MOLE_DOWN:
4490       Feld[x][y] = EL_MOLE;
4491       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4492       break;
4493
4494     case EL_SPRING_LEFT:
4495     case EL_SPRING_RIGHT:
4496       Feld[x][y] = EL_SPRING;
4497       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4498       break;
4499
4500     default:
4501       if (IS_CUSTOM_ELEMENT(element))
4502       {
4503         struct ElementInfo *ei = &element_info[element];
4504         int move_direction_initial = ei->move_direction_initial;
4505         int move_pattern = ei->move_pattern;
4506
4507         if (move_direction_initial == MV_START_PREVIOUS)
4508         {
4509           if (MovDir[x][y] != MV_NONE)
4510             return;
4511
4512           move_direction_initial = MV_START_AUTOMATIC;
4513         }
4514
4515         if (move_direction_initial == MV_START_RANDOM)
4516           MovDir[x][y] = 1 << RND(4);
4517         else if (move_direction_initial & MV_ANY_DIRECTION)
4518           MovDir[x][y] = move_direction_initial;
4519         else if (move_pattern == MV_ALL_DIRECTIONS ||
4520                  move_pattern == MV_TURNING_LEFT ||
4521                  move_pattern == MV_TURNING_RIGHT ||
4522                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4523                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4524                  move_pattern == MV_TURNING_RANDOM)
4525           MovDir[x][y] = 1 << RND(4);
4526         else if (move_pattern == MV_HORIZONTAL)
4527           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4528         else if (move_pattern == MV_VERTICAL)
4529           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4530         else if (move_pattern & MV_ANY_DIRECTION)
4531           MovDir[x][y] = element_info[element].move_pattern;
4532         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4533                  move_pattern == MV_ALONG_RIGHT_SIDE)
4534         {
4535           // use random direction as default start direction
4536           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4537             MovDir[x][y] = 1 << RND(4);
4538
4539           for (i = 0; i < NUM_DIRECTIONS; i++)
4540           {
4541             int x1 = x + xy[i][0];
4542             int y1 = y + xy[i][1];
4543
4544             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4545             {
4546               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4547                 MovDir[x][y] = direction[0][i];
4548               else
4549                 MovDir[x][y] = direction[1][i];
4550
4551               break;
4552             }
4553           }
4554         }                
4555       }
4556       else
4557       {
4558         MovDir[x][y] = 1 << RND(4);
4559
4560         if (element != EL_BUG &&
4561             element != EL_SPACESHIP &&
4562             element != EL_BD_BUTTERFLY &&
4563             element != EL_BD_FIREFLY)
4564           break;
4565
4566         for (i = 0; i < NUM_DIRECTIONS; i++)
4567         {
4568           int x1 = x + xy[i][0];
4569           int y1 = y + xy[i][1];
4570
4571           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4572           {
4573             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4574             {
4575               MovDir[x][y] = direction[0][i];
4576               break;
4577             }
4578             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4579                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4580             {
4581               MovDir[x][y] = direction[1][i];
4582               break;
4583             }
4584           }
4585         }
4586       }
4587       break;
4588   }
4589
4590   GfxDir[x][y] = MovDir[x][y];
4591 }
4592
4593 void InitAmoebaNr(int x, int y)
4594 {
4595   int i;
4596   int group_nr = AmoebeNachbarNr(x, y);
4597
4598   if (group_nr == 0)
4599   {
4600     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4601     {
4602       if (AmoebaCnt[i] == 0)
4603       {
4604         group_nr = i;
4605         break;
4606       }
4607     }
4608   }
4609
4610   AmoebaNr[x][y] = group_nr;
4611   AmoebaCnt[group_nr]++;
4612   AmoebaCnt2[group_nr]++;
4613 }
4614
4615 static void LevelSolved(void)
4616 {
4617   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4618       game.players_still_needed > 0)
4619     return;
4620
4621   game.LevelSolved = TRUE;
4622   game.GameOver = TRUE;
4623
4624   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4625                       game_em.lev->score :
4626                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4627                       game_mm.score :
4628                       game.score);
4629   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4630                        MM_HEALTH(game_mm.laser_overload_value) :
4631                        game.health);
4632
4633   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4634   game.LevelSolved_CountingScore = game.score_final;
4635   game.LevelSolved_CountingHealth = game.health_final;
4636 }
4637
4638 void GameWon(void)
4639 {
4640   static int time_count_steps;
4641   static int time, time_final;
4642   static int score, score_final;
4643   static int health, health_final;
4644   static int game_over_delay_1 = 0;
4645   static int game_over_delay_2 = 0;
4646   static int game_over_delay_3 = 0;
4647   int game_over_delay_value_1 = 50;
4648   int game_over_delay_value_2 = 25;
4649   int game_over_delay_value_3 = 50;
4650
4651   if (!game.LevelSolved_GameWon)
4652   {
4653     int i;
4654
4655     // do not start end game actions before the player stops moving (to exit)
4656     if (local_player->active && local_player->MovPos)
4657       return;
4658
4659     game.LevelSolved_GameWon = TRUE;
4660     game.LevelSolved_SaveTape = tape.recording;
4661     game.LevelSolved_SaveScore = !tape.playing;
4662
4663     if (!tape.playing)
4664     {
4665       LevelStats_incSolved(level_nr);
4666
4667       SaveLevelSetup_SeriesInfo();
4668     }
4669
4670     if (tape.auto_play)         // tape might already be stopped here
4671       tape.auto_play_level_solved = TRUE;
4672
4673     TapeStop();
4674
4675     game_over_delay_1 = 0;
4676     game_over_delay_2 = 0;
4677     game_over_delay_3 = game_over_delay_value_3;
4678
4679     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4680     score = score_final = game.score_final;
4681     health = health_final = game.health_final;
4682
4683     if (level.score[SC_TIME_BONUS] > 0)
4684     {
4685       if (TimeLeft > 0)
4686       {
4687         time_final = 0;
4688         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4689       }
4690       else if (game.no_time_limit && TimePlayed < 999)
4691       {
4692         time_final = 999;
4693         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4694       }
4695
4696       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4697
4698       game_over_delay_1 = game_over_delay_value_1;
4699
4700       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4701       {
4702         health_final = 0;
4703         score_final += health * level.score[SC_TIME_BONUS];
4704
4705         game_over_delay_2 = game_over_delay_value_2;
4706       }
4707
4708       game.score_final = score_final;
4709       game.health_final = health_final;
4710     }
4711
4712     if (level_editor_test_game)
4713     {
4714       time = time_final;
4715       score = score_final;
4716
4717       game.LevelSolved_CountingTime = time;
4718       game.LevelSolved_CountingScore = score;
4719
4720       game_panel_controls[GAME_PANEL_TIME].value = time;
4721       game_panel_controls[GAME_PANEL_SCORE].value = score;
4722
4723       DisplayGameControlValues();
4724     }
4725
4726     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4727     {
4728       // check if last player has left the level
4729       if (game.exit_x >= 0 &&
4730           game.exit_y >= 0)
4731       {
4732         int x = game.exit_x;
4733         int y = game.exit_y;
4734         int element = Feld[x][y];
4735
4736         // close exit door after last player
4737         if ((game.all_players_gone &&
4738              (element == EL_EXIT_OPEN ||
4739               element == EL_SP_EXIT_OPEN ||
4740               element == EL_STEEL_EXIT_OPEN)) ||
4741             element == EL_EM_EXIT_OPEN ||
4742             element == EL_EM_STEEL_EXIT_OPEN)
4743         {
4744
4745           Feld[x][y] =
4746             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4747              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4748              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4749              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4750              EL_EM_STEEL_EXIT_CLOSING);
4751
4752           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4753         }
4754
4755         // player disappears
4756         DrawLevelField(x, y);
4757       }
4758
4759       for (i = 0; i < MAX_PLAYERS; i++)
4760       {
4761         struct PlayerInfo *player = &stored_player[i];
4762
4763         if (player->present)
4764         {
4765           RemovePlayer(player);
4766
4767           // player disappears
4768           DrawLevelField(player->jx, player->jy);
4769         }
4770       }
4771     }
4772
4773     PlaySound(SND_GAME_WINNING);
4774   }
4775
4776   if (game_over_delay_1 > 0)
4777   {
4778     game_over_delay_1--;
4779
4780     return;
4781   }
4782
4783   if (time != time_final)
4784   {
4785     int time_to_go = ABS(time_final - time);
4786     int time_count_dir = (time < time_final ? +1 : -1);
4787
4788     if (time_to_go < time_count_steps)
4789       time_count_steps = 1;
4790
4791     time  += time_count_steps * time_count_dir;
4792     score += time_count_steps * level.score[SC_TIME_BONUS];
4793
4794     game.LevelSolved_CountingTime = time;
4795     game.LevelSolved_CountingScore = score;
4796
4797     game_panel_controls[GAME_PANEL_TIME].value = time;
4798     game_panel_controls[GAME_PANEL_SCORE].value = score;
4799
4800     DisplayGameControlValues();
4801
4802     if (time == time_final)
4803       StopSound(SND_GAME_LEVELTIME_BONUS);
4804     else if (setup.sound_loops)
4805       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4806     else
4807       PlaySound(SND_GAME_LEVELTIME_BONUS);
4808
4809     return;
4810   }
4811
4812   if (game_over_delay_2 > 0)
4813   {
4814     game_over_delay_2--;
4815
4816     return;
4817   }
4818
4819   if (health != health_final)
4820   {
4821     int health_count_dir = (health < health_final ? +1 : -1);
4822
4823     health += health_count_dir;
4824     score  += level.score[SC_TIME_BONUS];
4825
4826     game.LevelSolved_CountingHealth = health;
4827     game.LevelSolved_CountingScore = score;
4828
4829     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4830     game_panel_controls[GAME_PANEL_SCORE].value = score;
4831
4832     DisplayGameControlValues();
4833
4834     if (health == health_final)
4835       StopSound(SND_GAME_LEVELTIME_BONUS);
4836     else if (setup.sound_loops)
4837       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4838     else
4839       PlaySound(SND_GAME_LEVELTIME_BONUS);
4840
4841     return;
4842   }
4843
4844   game.panel.active = FALSE;
4845
4846   if (game_over_delay_3 > 0)
4847   {
4848     game_over_delay_3--;
4849
4850     return;
4851   }
4852
4853   GameEnd();
4854 }
4855
4856 void GameEnd(void)
4857 {
4858   // used instead of "level_nr" (needed for network games)
4859   int last_level_nr = levelset.level_nr;
4860   int hi_pos;
4861
4862   game.LevelSolved_GameEnd = TRUE;
4863
4864   if (game.LevelSolved_SaveTape)
4865   {
4866     // make sure that request dialog to save tape does not open door again
4867     if (!global.use_envelope_request)
4868       CloseDoor(DOOR_CLOSE_1);
4869
4870     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4871   }
4872
4873   // if no tape is to be saved, close both doors simultaneously
4874   CloseDoor(DOOR_CLOSE_ALL);
4875
4876   if (level_editor_test_game)
4877   {
4878     SetGameStatus(GAME_MODE_MAIN);
4879
4880     DrawMainMenu();
4881
4882     return;
4883   }
4884
4885   if (!game.LevelSolved_SaveScore)
4886   {
4887     SetGameStatus(GAME_MODE_MAIN);
4888
4889     DrawMainMenu();
4890
4891     return;
4892   }
4893
4894   if (level_nr == leveldir_current->handicap_level)
4895   {
4896     leveldir_current->handicap_level++;
4897
4898     SaveLevelSetup_SeriesInfo();
4899   }
4900
4901   if (setup.increment_levels &&
4902       level_nr < leveldir_current->last_level &&
4903       !network_playing)
4904   {
4905     level_nr++;         // advance to next level
4906     TapeErase();        // start with empty tape
4907
4908     if (setup.auto_play_next_level)
4909     {
4910       LoadLevel(level_nr);
4911
4912       SaveLevelSetup_SeriesInfo();
4913     }
4914   }
4915
4916   hi_pos = NewHiScore(last_level_nr);
4917
4918   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4919   {
4920     SetGameStatus(GAME_MODE_SCORES);
4921
4922     DrawHallOfFame(last_level_nr, hi_pos);
4923   }
4924   else if (setup.auto_play_next_level && setup.increment_levels &&
4925            last_level_nr < leveldir_current->last_level &&
4926            !network_playing)
4927   {
4928     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4929   }
4930   else
4931   {
4932     SetGameStatus(GAME_MODE_MAIN);
4933
4934     DrawMainMenu();
4935   }
4936 }
4937
4938 int NewHiScore(int level_nr)
4939 {
4940   int k, l;
4941   int position = -1;
4942   boolean one_score_entry_per_name = !program.many_scores_per_name;
4943
4944   LoadScore(level_nr);
4945
4946   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4947       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4948     return -1;
4949
4950   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4951   {
4952     if (game.score_final > highscore[k].Score)
4953     {
4954       // player has made it to the hall of fame
4955
4956       if (k < MAX_SCORE_ENTRIES - 1)
4957       {
4958         int m = MAX_SCORE_ENTRIES - 1;
4959
4960         if (one_score_entry_per_name)
4961         {
4962           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4963             if (strEqual(setup.player_name, highscore[l].Name))
4964               m = l;
4965
4966           if (m == k)   // player's new highscore overwrites his old one
4967             goto put_into_list;
4968         }
4969
4970         for (l = m; l > k; l--)
4971         {
4972           strcpy(highscore[l].Name, highscore[l - 1].Name);
4973           highscore[l].Score = highscore[l - 1].Score;
4974         }
4975       }
4976
4977       put_into_list:
4978
4979       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4980       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4981       highscore[k].Score = game.score_final;
4982       position = k;
4983
4984       break;
4985     }
4986     else if (one_score_entry_per_name &&
4987              !strncmp(setup.player_name, highscore[k].Name,
4988                       MAX_PLAYER_NAME_LEN))
4989       break;    // player already there with a higher score
4990   }
4991
4992   if (position >= 0) 
4993     SaveScore(level_nr);
4994
4995   return position;
4996 }
4997
4998 static int getElementMoveStepsizeExt(int x, int y, int direction)
4999 {
5000   int element = Feld[x][y];
5001   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5002   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5003   int horiz_move = (dx != 0);
5004   int sign = (horiz_move ? dx : dy);
5005   int step = sign * element_info[element].move_stepsize;
5006
5007   // special values for move stepsize for spring and things on conveyor belt
5008   if (horiz_move)
5009   {
5010     if (CAN_FALL(element) &&
5011         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5012       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5013     else if (element == EL_SPRING)
5014       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5015   }
5016
5017   return step;
5018 }
5019
5020 static int getElementMoveStepsize(int x, int y)
5021 {
5022   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5023 }
5024
5025 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5026 {
5027   if (player->GfxAction != action || player->GfxDir != dir)
5028   {
5029     player->GfxAction = action;
5030     player->GfxDir = dir;
5031     player->Frame = 0;
5032     player->StepFrame = 0;
5033   }
5034 }
5035
5036 static void ResetGfxFrame(int x, int y)
5037 {
5038   // profiling showed that "autotest" spends 10~20% of its time in this function
5039   if (DrawingDeactivatedField())
5040     return;
5041
5042   int element = Feld[x][y];
5043   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5044
5045   if (graphic_info[graphic].anim_global_sync)
5046     GfxFrame[x][y] = FrameCounter;
5047   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5048     GfxFrame[x][y] = CustomValue[x][y];
5049   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5050     GfxFrame[x][y] = element_info[element].collect_score;
5051   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5052     GfxFrame[x][y] = ChangeDelay[x][y];
5053 }
5054
5055 static void ResetGfxAnimation(int x, int y)
5056 {
5057   GfxAction[x][y] = ACTION_DEFAULT;
5058   GfxDir[x][y] = MovDir[x][y];
5059   GfxFrame[x][y] = 0;
5060
5061   ResetGfxFrame(x, y);
5062 }
5063
5064 static void ResetRandomAnimationValue(int x, int y)
5065 {
5066   GfxRandom[x][y] = INIT_GFX_RANDOM();
5067 }
5068
5069 static void InitMovingField(int x, int y, int direction)
5070 {
5071   int element = Feld[x][y];
5072   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5073   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5074   int newx = x + dx;
5075   int newy = y + dy;
5076   boolean is_moving_before, is_moving_after;
5077
5078   // check if element was/is moving or being moved before/after mode change
5079   is_moving_before = (WasJustMoving[x][y] != 0);
5080   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5081
5082   // reset animation only for moving elements which change direction of moving
5083   // or which just started or stopped moving
5084   // (else CEs with property "can move" / "not moving" are reset each frame)
5085   if (is_moving_before != is_moving_after ||
5086       direction != MovDir[x][y])
5087     ResetGfxAnimation(x, y);
5088
5089   MovDir[x][y] = direction;
5090   GfxDir[x][y] = direction;
5091
5092   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5093                      direction == MV_DOWN && CAN_FALL(element) ?
5094                      ACTION_FALLING : ACTION_MOVING);
5095
5096   // this is needed for CEs with property "can move" / "not moving"
5097
5098   if (is_moving_after)
5099   {
5100     if (Feld[newx][newy] == EL_EMPTY)
5101       Feld[newx][newy] = EL_BLOCKED;
5102
5103     MovDir[newx][newy] = MovDir[x][y];
5104
5105     CustomValue[newx][newy] = CustomValue[x][y];
5106
5107     GfxFrame[newx][newy] = GfxFrame[x][y];
5108     GfxRandom[newx][newy] = GfxRandom[x][y];
5109     GfxAction[newx][newy] = GfxAction[x][y];
5110     GfxDir[newx][newy] = GfxDir[x][y];
5111   }
5112 }
5113
5114 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5115 {
5116   int direction = MovDir[x][y];
5117   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5118   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5119
5120   *goes_to_x = newx;
5121   *goes_to_y = newy;
5122 }
5123
5124 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5125 {
5126   int oldx = x, oldy = y;
5127   int direction = MovDir[x][y];
5128
5129   if (direction == MV_LEFT)
5130     oldx++;
5131   else if (direction == MV_RIGHT)
5132     oldx--;
5133   else if (direction == MV_UP)
5134     oldy++;
5135   else if (direction == MV_DOWN)
5136     oldy--;
5137
5138   *comes_from_x = oldx;
5139   *comes_from_y = oldy;
5140 }
5141
5142 static int MovingOrBlocked2Element(int x, int y)
5143 {
5144   int element = Feld[x][y];
5145
5146   if (element == EL_BLOCKED)
5147   {
5148     int oldx, oldy;
5149
5150     Blocked2Moving(x, y, &oldx, &oldy);
5151     return Feld[oldx][oldy];
5152   }
5153   else
5154     return element;
5155 }
5156
5157 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5158 {
5159   // like MovingOrBlocked2Element(), but if element is moving
5160   // and (x,y) is the field the moving element is just leaving,
5161   // return EL_BLOCKED instead of the element value
5162   int element = Feld[x][y];
5163
5164   if (IS_MOVING(x, y))
5165   {
5166     if (element == EL_BLOCKED)
5167     {
5168       int oldx, oldy;
5169
5170       Blocked2Moving(x, y, &oldx, &oldy);
5171       return Feld[oldx][oldy];
5172     }
5173     else
5174       return EL_BLOCKED;
5175   }
5176   else
5177     return element;
5178 }
5179
5180 static void RemoveField(int x, int y)
5181 {
5182   Feld[x][y] = EL_EMPTY;
5183
5184   MovPos[x][y] = 0;
5185   MovDir[x][y] = 0;
5186   MovDelay[x][y] = 0;
5187
5188   CustomValue[x][y] = 0;
5189
5190   AmoebaNr[x][y] = 0;
5191   ChangeDelay[x][y] = 0;
5192   ChangePage[x][y] = -1;
5193   Pushed[x][y] = FALSE;
5194
5195   GfxElement[x][y] = EL_UNDEFINED;
5196   GfxAction[x][y] = ACTION_DEFAULT;
5197   GfxDir[x][y] = MV_NONE;
5198 }
5199
5200 static void RemoveMovingField(int x, int y)
5201 {
5202   int oldx = x, oldy = y, newx = x, newy = y;
5203   int element = Feld[x][y];
5204   int next_element = EL_UNDEFINED;
5205
5206   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5207     return;
5208
5209   if (IS_MOVING(x, y))
5210   {
5211     Moving2Blocked(x, y, &newx, &newy);
5212
5213     if (Feld[newx][newy] != EL_BLOCKED)
5214     {
5215       // element is moving, but target field is not free (blocked), but
5216       // already occupied by something different (example: acid pool);
5217       // in this case, only remove the moving field, but not the target
5218
5219       RemoveField(oldx, oldy);
5220
5221       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5222
5223       TEST_DrawLevelField(oldx, oldy);
5224
5225       return;
5226     }
5227   }
5228   else if (element == EL_BLOCKED)
5229   {
5230     Blocked2Moving(x, y, &oldx, &oldy);
5231     if (!IS_MOVING(oldx, oldy))
5232       return;
5233   }
5234
5235   if (element == EL_BLOCKED &&
5236       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5237        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5238        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5239        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5240        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5241        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5242     next_element = get_next_element(Feld[oldx][oldy]);
5243
5244   RemoveField(oldx, oldy);
5245   RemoveField(newx, newy);
5246
5247   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5248
5249   if (next_element != EL_UNDEFINED)
5250     Feld[oldx][oldy] = next_element;
5251
5252   TEST_DrawLevelField(oldx, oldy);
5253   TEST_DrawLevelField(newx, newy);
5254 }
5255
5256 void DrawDynamite(int x, int y)
5257 {
5258   int sx = SCREENX(x), sy = SCREENY(y);
5259   int graphic = el2img(Feld[x][y]);
5260   int frame;
5261
5262   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5263     return;
5264
5265   if (IS_WALKABLE_INSIDE(Back[x][y]))
5266     return;
5267
5268   if (Back[x][y])
5269     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5270   else if (Store[x][y])
5271     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5272
5273   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5274
5275   if (Back[x][y] || Store[x][y])
5276     DrawGraphicThruMask(sx, sy, graphic, frame);
5277   else
5278     DrawGraphic(sx, sy, graphic, frame);
5279 }
5280
5281 static void CheckDynamite(int x, int y)
5282 {
5283   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5284   {
5285     MovDelay[x][y]--;
5286
5287     if (MovDelay[x][y] != 0)
5288     {
5289       DrawDynamite(x, y);
5290       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5291
5292       return;
5293     }
5294   }
5295
5296   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5297
5298   Bang(x, y);
5299 }
5300
5301 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5302 {
5303   boolean num_checked_players = 0;
5304   int i;
5305
5306   for (i = 0; i < MAX_PLAYERS; i++)
5307   {
5308     if (stored_player[i].active)
5309     {
5310       int sx = stored_player[i].jx;
5311       int sy = stored_player[i].jy;
5312
5313       if (num_checked_players == 0)
5314       {
5315         *sx1 = *sx2 = sx;
5316         *sy1 = *sy2 = sy;
5317       }
5318       else
5319       {
5320         *sx1 = MIN(*sx1, sx);
5321         *sy1 = MIN(*sy1, sy);
5322         *sx2 = MAX(*sx2, sx);
5323         *sy2 = MAX(*sy2, sy);
5324       }
5325
5326       num_checked_players++;
5327     }
5328   }
5329 }
5330
5331 static boolean checkIfAllPlayersFitToScreen_RND(void)
5332 {
5333   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5334
5335   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5336
5337   return (sx2 - sx1 < SCR_FIELDX &&
5338           sy2 - sy1 < SCR_FIELDY);
5339 }
5340
5341 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5342 {
5343   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5344
5345   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5346
5347   *sx = (sx1 + sx2) / 2;
5348   *sy = (sy1 + sy2) / 2;
5349 }
5350
5351 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5352                                boolean center_screen, boolean quick_relocation)
5353 {
5354   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5355   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5356   boolean no_delay = (tape.warp_forward);
5357   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5358   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5359   int new_scroll_x, new_scroll_y;
5360
5361   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5362   {
5363     // case 1: quick relocation inside visible screen (without scrolling)
5364
5365     RedrawPlayfield();
5366
5367     return;
5368   }
5369
5370   if (!level.shifted_relocation || center_screen)
5371   {
5372     // relocation _with_ centering of screen
5373
5374     new_scroll_x = SCROLL_POSITION_X(x);
5375     new_scroll_y = SCROLL_POSITION_Y(y);
5376   }
5377   else
5378   {
5379     // relocation _without_ centering of screen
5380
5381     int center_scroll_x = SCROLL_POSITION_X(old_x);
5382     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5383     int offset_x = x + (scroll_x - center_scroll_x);
5384     int offset_y = y + (scroll_y - center_scroll_y);
5385
5386     // for new screen position, apply previous offset to center position
5387     new_scroll_x = SCROLL_POSITION_X(offset_x);
5388     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5389   }
5390
5391   if (quick_relocation)
5392   {
5393     // case 2: quick relocation (redraw without visible scrolling)
5394
5395     scroll_x = new_scroll_x;
5396     scroll_y = new_scroll_y;
5397
5398     RedrawPlayfield();
5399
5400     return;
5401   }
5402
5403   // case 3: visible relocation (with scrolling to new position)
5404
5405   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5406
5407   SetVideoFrameDelay(wait_delay_value);
5408
5409   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5410   {
5411     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5412     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5413
5414     if (dx == 0 && dy == 0)             // no scrolling needed at all
5415       break;
5416
5417     scroll_x -= dx;
5418     scroll_y -= dy;
5419
5420     // set values for horizontal/vertical screen scrolling (half tile size)
5421     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5422     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5423     int pos_x = dx * TILEX / 2;
5424     int pos_y = dy * TILEY / 2;
5425     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5426     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5427
5428     ScrollLevel(dx, dy);
5429     DrawAllPlayers();
5430
5431     // scroll in two steps of half tile size to make things smoother
5432     BlitScreenToBitmapExt_RND(window, fx, fy);
5433
5434     // scroll second step to align at full tile size
5435     BlitScreenToBitmap(window);
5436   }
5437
5438   DrawAllPlayers();
5439   BackToFront();
5440
5441   SetVideoFrameDelay(frame_delay_value_old);
5442 }
5443
5444 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5445 {
5446   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5447   int player_nr = GET_PLAYER_NR(el_player);
5448   struct PlayerInfo *player = &stored_player[player_nr];
5449   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5450   boolean no_delay = (tape.warp_forward);
5451   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5452   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5453   int old_jx = player->jx;
5454   int old_jy = player->jy;
5455   int old_element = Feld[old_jx][old_jy];
5456   int element = Feld[jx][jy];
5457   boolean player_relocated = (old_jx != jx || old_jy != jy);
5458
5459   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5460   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5461   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5462   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5463   int leave_side_horiz = move_dir_horiz;
5464   int leave_side_vert  = move_dir_vert;
5465   int enter_side = enter_side_horiz | enter_side_vert;
5466   int leave_side = leave_side_horiz | leave_side_vert;
5467
5468   if (player->buried)           // do not reanimate dead player
5469     return;
5470
5471   if (!player_relocated)        // no need to relocate the player
5472     return;
5473
5474   if (IS_PLAYER(jx, jy))        // player already placed at new position
5475   {
5476     RemoveField(jx, jy);        // temporarily remove newly placed player
5477     DrawLevelField(jx, jy);
5478   }
5479
5480   if (player->present)
5481   {
5482     while (player->MovPos)
5483     {
5484       ScrollPlayer(player, SCROLL_GO_ON);
5485       ScrollScreen(NULL, SCROLL_GO_ON);
5486
5487       AdvanceFrameAndPlayerCounters(player->index_nr);
5488
5489       DrawPlayer(player);
5490
5491       BackToFront_WithFrameDelay(wait_delay_value);
5492     }
5493
5494     DrawPlayer(player);         // needed here only to cleanup last field
5495     DrawLevelField(player->jx, player->jy);     // remove player graphic
5496
5497     player->is_moving = FALSE;
5498   }
5499
5500   if (IS_CUSTOM_ELEMENT(old_element))
5501     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5502                                CE_LEFT_BY_PLAYER,
5503                                player->index_bit, leave_side);
5504
5505   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5506                                       CE_PLAYER_LEAVES_X,
5507                                       player->index_bit, leave_side);
5508
5509   Feld[jx][jy] = el_player;
5510   InitPlayerField(jx, jy, el_player, TRUE);
5511
5512   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5513      possible that the relocation target field did not contain a player element,
5514      but a walkable element, to which the new player was relocated -- in this
5515      case, restore that (already initialized!) element on the player field */
5516   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5517   {
5518     Feld[jx][jy] = element;     // restore previously existing element
5519   }
5520
5521   // only visually relocate centered player
5522   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5523                      FALSE, level.instant_relocation);
5524
5525   TestIfPlayerTouchesBadThing(jx, jy);
5526   TestIfPlayerTouchesCustomElement(jx, jy);
5527
5528   if (IS_CUSTOM_ELEMENT(element))
5529     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5530                                player->index_bit, enter_side);
5531
5532   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5533                                       player->index_bit, enter_side);
5534
5535   if (player->is_switching)
5536   {
5537     /* ensure that relocation while still switching an element does not cause
5538        a new element to be treated as also switched directly after relocation
5539        (this is important for teleporter switches that teleport the player to
5540        a place where another teleporter switch is in the same direction, which
5541        would then incorrectly be treated as immediately switched before the
5542        direction key that caused the switch was released) */
5543
5544     player->switch_x += jx - old_jx;
5545     player->switch_y += jy - old_jy;
5546   }
5547 }
5548
5549 static void Explode(int ex, int ey, int phase, int mode)
5550 {
5551   int x, y;
5552   int last_phase;
5553   int border_element;
5554
5555   // !!! eliminate this variable !!!
5556   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5557
5558   if (game.explosions_delayed)
5559   {
5560     ExplodeField[ex][ey] = mode;
5561     return;
5562   }
5563
5564   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5565   {
5566     int center_element = Feld[ex][ey];
5567     int artwork_element, explosion_element;     // set these values later
5568
5569     // remove things displayed in background while burning dynamite
5570     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5571       Back[ex][ey] = 0;
5572
5573     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5574     {
5575       // put moving element to center field (and let it explode there)
5576       center_element = MovingOrBlocked2Element(ex, ey);
5577       RemoveMovingField(ex, ey);
5578       Feld[ex][ey] = center_element;
5579     }
5580
5581     // now "center_element" is finally determined -- set related values now
5582     artwork_element = center_element;           // for custom player artwork
5583     explosion_element = center_element;         // for custom player artwork
5584
5585     if (IS_PLAYER(ex, ey))
5586     {
5587       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5588
5589       artwork_element = stored_player[player_nr].artwork_element;
5590
5591       if (level.use_explosion_element[player_nr])
5592       {
5593         explosion_element = level.explosion_element[player_nr];
5594         artwork_element = explosion_element;
5595       }
5596     }
5597
5598     if (mode == EX_TYPE_NORMAL ||
5599         mode == EX_TYPE_CENTER ||
5600         mode == EX_TYPE_CROSS)
5601       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5602
5603     last_phase = element_info[explosion_element].explosion_delay + 1;
5604
5605     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5606     {
5607       int xx = x - ex + 1;
5608       int yy = y - ey + 1;
5609       int element;
5610
5611       if (!IN_LEV_FIELD(x, y) ||
5612           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5613           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5614         continue;
5615
5616       element = Feld[x][y];
5617
5618       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5619       {
5620         element = MovingOrBlocked2Element(x, y);
5621
5622         if (!IS_EXPLOSION_PROOF(element))
5623           RemoveMovingField(x, y);
5624       }
5625
5626       // indestructible elements can only explode in center (but not flames)
5627       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5628                                            mode == EX_TYPE_BORDER)) ||
5629           element == EL_FLAMES)
5630         continue;
5631
5632       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5633          behaviour, for example when touching a yamyam that explodes to rocks
5634          with active deadly shield, a rock is created under the player !!! */
5635       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5636 #if 0
5637       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5638           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5639            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5640 #else
5641       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5642 #endif
5643       {
5644         if (IS_ACTIVE_BOMB(element))
5645         {
5646           // re-activate things under the bomb like gate or penguin
5647           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5648           Back[x][y] = 0;
5649         }
5650
5651         continue;
5652       }
5653
5654       // save walkable background elements while explosion on same tile
5655       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5656           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5657         Back[x][y] = element;
5658
5659       // ignite explodable elements reached by other explosion
5660       if (element == EL_EXPLOSION)
5661         element = Store2[x][y];
5662
5663       if (AmoebaNr[x][y] &&
5664           (element == EL_AMOEBA_FULL ||
5665            element == EL_BD_AMOEBA ||
5666            element == EL_AMOEBA_GROWING))
5667       {
5668         AmoebaCnt[AmoebaNr[x][y]]--;
5669         AmoebaCnt2[AmoebaNr[x][y]]--;
5670       }
5671
5672       RemoveField(x, y);
5673
5674       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5675       {
5676         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5677
5678         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5679
5680         if (PLAYERINFO(ex, ey)->use_murphy)
5681           Store[x][y] = EL_EMPTY;
5682       }
5683
5684       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5685       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5686       else if (ELEM_IS_PLAYER(center_element))
5687         Store[x][y] = EL_EMPTY;
5688       else if (center_element == EL_YAMYAM)
5689         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5690       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5691         Store[x][y] = element_info[center_element].content.e[xx][yy];
5692 #if 1
5693       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5694       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5695       // otherwise) -- FIX THIS !!!
5696       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5697         Store[x][y] = element_info[element].content.e[1][1];
5698 #else
5699       else if (!CAN_EXPLODE(element))
5700         Store[x][y] = element_info[element].content.e[1][1];
5701 #endif
5702       else
5703         Store[x][y] = EL_EMPTY;
5704
5705       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5706           center_element == EL_AMOEBA_TO_DIAMOND)
5707         Store2[x][y] = element;
5708
5709       Feld[x][y] = EL_EXPLOSION;
5710       GfxElement[x][y] = artwork_element;
5711
5712       ExplodePhase[x][y] = 1;
5713       ExplodeDelay[x][y] = last_phase;
5714
5715       Stop[x][y] = TRUE;
5716     }
5717
5718     if (center_element == EL_YAMYAM)
5719       game.yamyam_content_nr =
5720         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5721
5722     return;
5723   }
5724
5725   if (Stop[ex][ey])
5726     return;
5727
5728   x = ex;
5729   y = ey;
5730
5731   if (phase == 1)
5732     GfxFrame[x][y] = 0;         // restart explosion animation
5733
5734   last_phase = ExplodeDelay[x][y];
5735
5736   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5737
5738   // this can happen if the player leaves an explosion just in time
5739   if (GfxElement[x][y] == EL_UNDEFINED)
5740     GfxElement[x][y] = EL_EMPTY;
5741
5742   border_element = Store2[x][y];
5743   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5744     border_element = StorePlayer[x][y];
5745
5746   if (phase == element_info[border_element].ignition_delay ||
5747       phase == last_phase)
5748   {
5749     boolean border_explosion = FALSE;
5750
5751     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5752         !PLAYER_EXPLOSION_PROTECTED(x, y))
5753     {
5754       KillPlayerUnlessExplosionProtected(x, y);
5755       border_explosion = TRUE;
5756     }
5757     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5758     {
5759       Feld[x][y] = Store2[x][y];
5760       Store2[x][y] = 0;
5761       Bang(x, y);
5762       border_explosion = TRUE;
5763     }
5764     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5765     {
5766       AmoebeUmwandeln(x, y);
5767       Store2[x][y] = 0;
5768       border_explosion = TRUE;
5769     }
5770
5771     // if an element just explodes due to another explosion (chain-reaction),
5772     // do not immediately end the new explosion when it was the last frame of
5773     // the explosion (as it would be done in the following "if"-statement!)
5774     if (border_explosion && phase == last_phase)
5775       return;
5776   }
5777
5778   if (phase == last_phase)
5779   {
5780     int element;
5781
5782     element = Feld[x][y] = Store[x][y];
5783     Store[x][y] = Store2[x][y] = 0;
5784     GfxElement[x][y] = EL_UNDEFINED;
5785
5786     // player can escape from explosions and might therefore be still alive
5787     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5788         element <= EL_PLAYER_IS_EXPLODING_4)
5789     {
5790       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5791       int explosion_element = EL_PLAYER_1 + player_nr;
5792       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5793       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5794
5795       if (level.use_explosion_element[player_nr])
5796         explosion_element = level.explosion_element[player_nr];
5797
5798       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5799                     element_info[explosion_element].content.e[xx][yy]);
5800     }
5801
5802     // restore probably existing indestructible background element
5803     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5804       element = Feld[x][y] = Back[x][y];
5805     Back[x][y] = 0;
5806
5807     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5808     GfxDir[x][y] = MV_NONE;
5809     ChangeDelay[x][y] = 0;
5810     ChangePage[x][y] = -1;
5811
5812     CustomValue[x][y] = 0;
5813
5814     InitField_WithBug2(x, y, FALSE);
5815
5816     TEST_DrawLevelField(x, y);
5817
5818     TestIfElementTouchesCustomElement(x, y);
5819
5820     if (GFX_CRUMBLED(element))
5821       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5822
5823     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5824       StorePlayer[x][y] = 0;
5825
5826     if (ELEM_IS_PLAYER(element))
5827       RelocatePlayer(x, y, element);
5828   }
5829   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5830   {
5831     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5832     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5833
5834     if (phase == delay)
5835       TEST_DrawLevelFieldCrumbled(x, y);
5836
5837     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5838     {
5839       DrawLevelElement(x, y, Back[x][y]);
5840       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5841     }
5842     else if (IS_WALKABLE_UNDER(Back[x][y]))
5843     {
5844       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5845       DrawLevelElementThruMask(x, y, Back[x][y]);
5846     }
5847     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5848       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5849   }
5850 }
5851
5852 static void DynaExplode(int ex, int ey)
5853 {
5854   int i, j;
5855   int dynabomb_element = Feld[ex][ey];
5856   int dynabomb_size = 1;
5857   boolean dynabomb_xl = FALSE;
5858   struct PlayerInfo *player;
5859   static int xy[4][2] =
5860   {
5861     { 0, -1 },
5862     { -1, 0 },
5863     { +1, 0 },
5864     { 0, +1 }
5865   };
5866
5867   if (IS_ACTIVE_BOMB(dynabomb_element))
5868   {
5869     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5870     dynabomb_size = player->dynabomb_size;
5871     dynabomb_xl = player->dynabomb_xl;
5872     player->dynabombs_left++;
5873   }
5874
5875   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5876
5877   for (i = 0; i < NUM_DIRECTIONS; i++)
5878   {
5879     for (j = 1; j <= dynabomb_size; j++)
5880     {
5881       int x = ex + j * xy[i][0];
5882       int y = ey + j * xy[i][1];
5883       int element;
5884
5885       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5886         break;
5887
5888       element = Feld[x][y];
5889
5890       // do not restart explosions of fields with active bombs
5891       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5892         continue;
5893
5894       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5895
5896       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5897           !IS_DIGGABLE(element) && !dynabomb_xl)
5898         break;
5899     }
5900   }
5901 }
5902
5903 void Bang(int x, int y)
5904 {
5905   int element = MovingOrBlocked2Element(x, y);
5906   int explosion_type = EX_TYPE_NORMAL;
5907
5908   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5909   {
5910     struct PlayerInfo *player = PLAYERINFO(x, y);
5911
5912     element = Feld[x][y] = player->initial_element;
5913
5914     if (level.use_explosion_element[player->index_nr])
5915     {
5916       int explosion_element = level.explosion_element[player->index_nr];
5917
5918       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5919         explosion_type = EX_TYPE_CROSS;
5920       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5921         explosion_type = EX_TYPE_CENTER;
5922     }
5923   }
5924
5925   switch (element)
5926   {
5927     case EL_BUG:
5928     case EL_SPACESHIP:
5929     case EL_BD_BUTTERFLY:
5930     case EL_BD_FIREFLY:
5931     case EL_YAMYAM:
5932     case EL_DARK_YAMYAM:
5933     case EL_ROBOT:
5934     case EL_PACMAN:
5935     case EL_MOLE:
5936       RaiseScoreElement(element);
5937       break;
5938
5939     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5940     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5941     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5942     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5943     case EL_DYNABOMB_INCREASE_NUMBER:
5944     case EL_DYNABOMB_INCREASE_SIZE:
5945     case EL_DYNABOMB_INCREASE_POWER:
5946       explosion_type = EX_TYPE_DYNA;
5947       break;
5948
5949     case EL_DC_LANDMINE:
5950       explosion_type = EX_TYPE_CENTER;
5951       break;
5952
5953     case EL_PENGUIN:
5954     case EL_LAMP:
5955     case EL_LAMP_ACTIVE:
5956     case EL_AMOEBA_TO_DIAMOND:
5957       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5958         explosion_type = EX_TYPE_CENTER;
5959       break;
5960
5961     default:
5962       if (element_info[element].explosion_type == EXPLODES_CROSS)
5963         explosion_type = EX_TYPE_CROSS;
5964       else if (element_info[element].explosion_type == EXPLODES_1X1)
5965         explosion_type = EX_TYPE_CENTER;
5966       break;
5967   }
5968
5969   if (explosion_type == EX_TYPE_DYNA)
5970     DynaExplode(x, y);
5971   else
5972     Explode(x, y, EX_PHASE_START, explosion_type);
5973
5974   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5975 }
5976
5977 static void SplashAcid(int x, int y)
5978 {
5979   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5980       (!IN_LEV_FIELD(x - 1, y - 2) ||
5981        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5982     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5983
5984   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5985       (!IN_LEV_FIELD(x + 1, y - 2) ||
5986        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5987     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5988
5989   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5990 }
5991
5992 static void InitBeltMovement(void)
5993 {
5994   static int belt_base_element[4] =
5995   {
5996     EL_CONVEYOR_BELT_1_LEFT,
5997     EL_CONVEYOR_BELT_2_LEFT,
5998     EL_CONVEYOR_BELT_3_LEFT,
5999     EL_CONVEYOR_BELT_4_LEFT
6000   };
6001   static int belt_base_active_element[4] =
6002   {
6003     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6004     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6005     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6006     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6007   };
6008
6009   int x, y, i, j;
6010
6011   // set frame order for belt animation graphic according to belt direction
6012   for (i = 0; i < NUM_BELTS; i++)
6013   {
6014     int belt_nr = i;
6015
6016     for (j = 0; j < NUM_BELT_PARTS; j++)
6017     {
6018       int element = belt_base_active_element[belt_nr] + j;
6019       int graphic_1 = el2img(element);
6020       int graphic_2 = el2panelimg(element);
6021
6022       if (game.belt_dir[i] == MV_LEFT)
6023       {
6024         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6025         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6026       }
6027       else
6028       {
6029         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6030         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6031       }
6032     }
6033   }
6034
6035   SCAN_PLAYFIELD(x, y)
6036   {
6037     int element = Feld[x][y];
6038
6039     for (i = 0; i < NUM_BELTS; i++)
6040     {
6041       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6042       {
6043         int e_belt_nr = getBeltNrFromBeltElement(element);
6044         int belt_nr = i;
6045
6046         if (e_belt_nr == belt_nr)
6047         {
6048           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6049
6050           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6051         }
6052       }
6053     }
6054   }
6055 }
6056
6057 static void ToggleBeltSwitch(int x, int y)
6058 {
6059   static int belt_base_element[4] =
6060   {
6061     EL_CONVEYOR_BELT_1_LEFT,
6062     EL_CONVEYOR_BELT_2_LEFT,
6063     EL_CONVEYOR_BELT_3_LEFT,
6064     EL_CONVEYOR_BELT_4_LEFT
6065   };
6066   static int belt_base_active_element[4] =
6067   {
6068     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6069     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6070     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6071     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6072   };
6073   static int belt_base_switch_element[4] =
6074   {
6075     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6076     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6077     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6078     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6079   };
6080   static int belt_move_dir[4] =
6081   {
6082     MV_LEFT,
6083     MV_NONE,
6084     MV_RIGHT,
6085     MV_NONE,
6086   };
6087
6088   int element = Feld[x][y];
6089   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6090   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6091   int belt_dir = belt_move_dir[belt_dir_nr];
6092   int xx, yy, i;
6093
6094   if (!IS_BELT_SWITCH(element))
6095     return;
6096
6097   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6098   game.belt_dir[belt_nr] = belt_dir;
6099
6100   if (belt_dir_nr == 3)
6101     belt_dir_nr = 1;
6102
6103   // set frame order for belt animation graphic according to belt direction
6104   for (i = 0; i < NUM_BELT_PARTS; i++)
6105   {
6106     int element = belt_base_active_element[belt_nr] + i;
6107     int graphic_1 = el2img(element);
6108     int graphic_2 = el2panelimg(element);
6109
6110     if (belt_dir == MV_LEFT)
6111     {
6112       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6113       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6114     }
6115     else
6116     {
6117       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6118       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6119     }
6120   }
6121
6122   SCAN_PLAYFIELD(xx, yy)
6123   {
6124     int element = Feld[xx][yy];
6125
6126     if (IS_BELT_SWITCH(element))
6127     {
6128       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6129
6130       if (e_belt_nr == belt_nr)
6131       {
6132         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6133         TEST_DrawLevelField(xx, yy);
6134       }
6135     }
6136     else if (IS_BELT(element) && belt_dir != MV_NONE)
6137     {
6138       int e_belt_nr = getBeltNrFromBeltElement(element);
6139
6140       if (e_belt_nr == belt_nr)
6141       {
6142         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6143
6144         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6145         TEST_DrawLevelField(xx, yy);
6146       }
6147     }
6148     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6149     {
6150       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6151
6152       if (e_belt_nr == belt_nr)
6153       {
6154         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6155
6156         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6157         TEST_DrawLevelField(xx, yy);
6158       }
6159     }
6160   }
6161 }
6162
6163 static void ToggleSwitchgateSwitch(int x, int y)
6164 {
6165   int xx, yy;
6166
6167   game.switchgate_pos = !game.switchgate_pos;
6168
6169   SCAN_PLAYFIELD(xx, yy)
6170   {
6171     int element = Feld[xx][yy];
6172
6173     if (element == EL_SWITCHGATE_SWITCH_UP)
6174     {
6175       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6176       TEST_DrawLevelField(xx, yy);
6177     }
6178     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6179     {
6180       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6181       TEST_DrawLevelField(xx, yy);
6182     }
6183     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6184     {
6185       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6186       TEST_DrawLevelField(xx, yy);
6187     }
6188     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6189     {
6190       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6191       TEST_DrawLevelField(xx, yy);
6192     }
6193     else if (element == EL_SWITCHGATE_OPEN ||
6194              element == EL_SWITCHGATE_OPENING)
6195     {
6196       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6197
6198       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6199     }
6200     else if (element == EL_SWITCHGATE_CLOSED ||
6201              element == EL_SWITCHGATE_CLOSING)
6202     {
6203       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6204
6205       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6206     }
6207   }
6208 }
6209
6210 static int getInvisibleActiveFromInvisibleElement(int element)
6211 {
6212   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6213           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6214           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6215           element);
6216 }
6217
6218 static int getInvisibleFromInvisibleActiveElement(int element)
6219 {
6220   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6221           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6222           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6223           element);
6224 }
6225
6226 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6227 {
6228   int x, y;
6229
6230   SCAN_PLAYFIELD(x, y)
6231   {
6232     int element = Feld[x][y];
6233
6234     if (element == EL_LIGHT_SWITCH &&
6235         game.light_time_left > 0)
6236     {
6237       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6238       TEST_DrawLevelField(x, y);
6239     }
6240     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6241              game.light_time_left == 0)
6242     {
6243       Feld[x][y] = EL_LIGHT_SWITCH;
6244       TEST_DrawLevelField(x, y);
6245     }
6246     else if (element == EL_EMC_DRIPPER &&
6247              game.light_time_left > 0)
6248     {
6249       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6250       TEST_DrawLevelField(x, y);
6251     }
6252     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6253              game.light_time_left == 0)
6254     {
6255       Feld[x][y] = EL_EMC_DRIPPER;
6256       TEST_DrawLevelField(x, y);
6257     }
6258     else if (element == EL_INVISIBLE_STEELWALL ||
6259              element == EL_INVISIBLE_WALL ||
6260              element == EL_INVISIBLE_SAND)
6261     {
6262       if (game.light_time_left > 0)
6263         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6264
6265       TEST_DrawLevelField(x, y);
6266
6267       // uncrumble neighbour fields, if needed
6268       if (element == EL_INVISIBLE_SAND)
6269         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6270     }
6271     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6272              element == EL_INVISIBLE_WALL_ACTIVE ||
6273              element == EL_INVISIBLE_SAND_ACTIVE)
6274     {
6275       if (game.light_time_left == 0)
6276         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6277
6278       TEST_DrawLevelField(x, y);
6279
6280       // re-crumble neighbour fields, if needed
6281       if (element == EL_INVISIBLE_SAND)
6282         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6283     }
6284   }
6285 }
6286
6287 static void RedrawAllInvisibleElementsForLenses(void)
6288 {
6289   int x, y;
6290
6291   SCAN_PLAYFIELD(x, y)
6292   {
6293     int element = Feld[x][y];
6294
6295     if (element == EL_EMC_DRIPPER &&
6296         game.lenses_time_left > 0)
6297     {
6298       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6299       TEST_DrawLevelField(x, y);
6300     }
6301     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6302              game.lenses_time_left == 0)
6303     {
6304       Feld[x][y] = EL_EMC_DRIPPER;
6305       TEST_DrawLevelField(x, y);
6306     }
6307     else if (element == EL_INVISIBLE_STEELWALL ||
6308              element == EL_INVISIBLE_WALL ||
6309              element == EL_INVISIBLE_SAND)
6310     {
6311       if (game.lenses_time_left > 0)
6312         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6313
6314       TEST_DrawLevelField(x, y);
6315
6316       // uncrumble neighbour fields, if needed
6317       if (element == EL_INVISIBLE_SAND)
6318         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6319     }
6320     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6321              element == EL_INVISIBLE_WALL_ACTIVE ||
6322              element == EL_INVISIBLE_SAND_ACTIVE)
6323     {
6324       if (game.lenses_time_left == 0)
6325         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6326
6327       TEST_DrawLevelField(x, y);
6328
6329       // re-crumble neighbour fields, if needed
6330       if (element == EL_INVISIBLE_SAND)
6331         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6332     }
6333   }
6334 }
6335
6336 static void RedrawAllInvisibleElementsForMagnifier(void)
6337 {
6338   int x, y;
6339
6340   SCAN_PLAYFIELD(x, y)
6341   {
6342     int element = Feld[x][y];
6343
6344     if (element == EL_EMC_FAKE_GRASS &&
6345         game.magnify_time_left > 0)
6346     {
6347       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6348       TEST_DrawLevelField(x, y);
6349     }
6350     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6351              game.magnify_time_left == 0)
6352     {
6353       Feld[x][y] = EL_EMC_FAKE_GRASS;
6354       TEST_DrawLevelField(x, y);
6355     }
6356     else if (IS_GATE_GRAY(element) &&
6357              game.magnify_time_left > 0)
6358     {
6359       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6360                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6361                     IS_EM_GATE_GRAY(element) ?
6362                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6363                     IS_EMC_GATE_GRAY(element) ?
6364                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6365                     IS_DC_GATE_GRAY(element) ?
6366                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6367                     element);
6368       TEST_DrawLevelField(x, y);
6369     }
6370     else if (IS_GATE_GRAY_ACTIVE(element) &&
6371              game.magnify_time_left == 0)
6372     {
6373       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6374                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6375                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6376                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6377                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6378                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6379                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6380                     EL_DC_GATE_WHITE_GRAY :
6381                     element);
6382       TEST_DrawLevelField(x, y);
6383     }
6384   }
6385 }
6386
6387 static void ToggleLightSwitch(int x, int y)
6388 {
6389   int element = Feld[x][y];
6390
6391   game.light_time_left =
6392     (element == EL_LIGHT_SWITCH ?
6393      level.time_light * FRAMES_PER_SECOND : 0);
6394
6395   RedrawAllLightSwitchesAndInvisibleElements();
6396 }
6397
6398 static void ActivateTimegateSwitch(int x, int y)
6399 {
6400   int xx, yy;
6401
6402   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6403
6404   SCAN_PLAYFIELD(xx, yy)
6405   {
6406     int element = Feld[xx][yy];
6407
6408     if (element == EL_TIMEGATE_CLOSED ||
6409         element == EL_TIMEGATE_CLOSING)
6410     {
6411       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6412       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6413     }
6414
6415     /*
6416     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6417     {
6418       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6419       TEST_DrawLevelField(xx, yy);
6420     }
6421     */
6422
6423   }
6424
6425   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6426                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6427 }
6428
6429 static void Impact(int x, int y)
6430 {
6431   boolean last_line = (y == lev_fieldy - 1);
6432   boolean object_hit = FALSE;
6433   boolean impact = (last_line || object_hit);
6434   int element = Feld[x][y];
6435   int smashed = EL_STEELWALL;
6436
6437   if (!last_line)       // check if element below was hit
6438   {
6439     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6440       return;
6441
6442     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6443                                          MovDir[x][y + 1] != MV_DOWN ||
6444                                          MovPos[x][y + 1] <= TILEY / 2));
6445
6446     // do not smash moving elements that left the smashed field in time
6447     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6448         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6449       object_hit = FALSE;
6450
6451 #if USE_QUICKSAND_IMPACT_BUGFIX
6452     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6453     {
6454       RemoveMovingField(x, y + 1);
6455       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6456       Feld[x][y + 2] = EL_ROCK;
6457       TEST_DrawLevelField(x, y + 2);
6458
6459       object_hit = TRUE;
6460     }
6461
6462     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6463     {
6464       RemoveMovingField(x, y + 1);
6465       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6466       Feld[x][y + 2] = EL_ROCK;
6467       TEST_DrawLevelField(x, y + 2);
6468
6469       object_hit = TRUE;
6470     }
6471 #endif
6472
6473     if (object_hit)
6474       smashed = MovingOrBlocked2Element(x, y + 1);
6475
6476     impact = (last_line || object_hit);
6477   }
6478
6479   if (!last_line && smashed == EL_ACID) // element falls into acid
6480   {
6481     SplashAcid(x, y + 1);
6482     return;
6483   }
6484
6485   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6486   // only reset graphic animation if graphic really changes after impact
6487   if (impact &&
6488       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6489   {
6490     ResetGfxAnimation(x, y);
6491     TEST_DrawLevelField(x, y);
6492   }
6493
6494   if (impact && CAN_EXPLODE_IMPACT(element))
6495   {
6496     Bang(x, y);
6497     return;
6498   }
6499   else if (impact && element == EL_PEARL &&
6500            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6501   {
6502     ResetGfxAnimation(x, y);
6503
6504     Feld[x][y] = EL_PEARL_BREAKING;
6505     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6506     return;
6507   }
6508   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6509   {
6510     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6511
6512     return;
6513   }
6514
6515   if (impact && element == EL_AMOEBA_DROP)
6516   {
6517     if (object_hit && IS_PLAYER(x, y + 1))
6518       KillPlayerUnlessEnemyProtected(x, y + 1);
6519     else if (object_hit && smashed == EL_PENGUIN)
6520       Bang(x, y + 1);
6521     else
6522     {
6523       Feld[x][y] = EL_AMOEBA_GROWING;
6524       Store[x][y] = EL_AMOEBA_WET;
6525
6526       ResetRandomAnimationValue(x, y);
6527     }
6528     return;
6529   }
6530
6531   if (object_hit)               // check which object was hit
6532   {
6533     if ((CAN_PASS_MAGIC_WALL(element) && 
6534          (smashed == EL_MAGIC_WALL ||
6535           smashed == EL_BD_MAGIC_WALL)) ||
6536         (CAN_PASS_DC_MAGIC_WALL(element) &&
6537          smashed == EL_DC_MAGIC_WALL))
6538     {
6539       int xx, yy;
6540       int activated_magic_wall =
6541         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6542          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6543          EL_DC_MAGIC_WALL_ACTIVE);
6544
6545       // activate magic wall / mill
6546       SCAN_PLAYFIELD(xx, yy)
6547       {
6548         if (Feld[xx][yy] == smashed)
6549           Feld[xx][yy] = activated_magic_wall;
6550       }
6551
6552       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6553       game.magic_wall_active = TRUE;
6554
6555       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6556                             SND_MAGIC_WALL_ACTIVATING :
6557                             smashed == EL_BD_MAGIC_WALL ?
6558                             SND_BD_MAGIC_WALL_ACTIVATING :
6559                             SND_DC_MAGIC_WALL_ACTIVATING));
6560     }
6561
6562     if (IS_PLAYER(x, y + 1))
6563     {
6564       if (CAN_SMASH_PLAYER(element))
6565       {
6566         KillPlayerUnlessEnemyProtected(x, y + 1);
6567         return;
6568       }
6569     }
6570     else if (smashed == EL_PENGUIN)
6571     {
6572       if (CAN_SMASH_PLAYER(element))
6573       {
6574         Bang(x, y + 1);
6575         return;
6576       }
6577     }
6578     else if (element == EL_BD_DIAMOND)
6579     {
6580       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6581       {
6582         Bang(x, y + 1);
6583         return;
6584       }
6585     }
6586     else if (((element == EL_SP_INFOTRON ||
6587                element == EL_SP_ZONK) &&
6588               (smashed == EL_SP_SNIKSNAK ||
6589                smashed == EL_SP_ELECTRON ||
6590                smashed == EL_SP_DISK_ORANGE)) ||
6591              (element == EL_SP_INFOTRON &&
6592               smashed == EL_SP_DISK_YELLOW))
6593     {
6594       Bang(x, y + 1);
6595       return;
6596     }
6597     else if (CAN_SMASH_EVERYTHING(element))
6598     {
6599       if (IS_CLASSIC_ENEMY(smashed) ||
6600           CAN_EXPLODE_SMASHED(smashed))
6601       {
6602         Bang(x, y + 1);
6603         return;
6604       }
6605       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6606       {
6607         if (smashed == EL_LAMP ||
6608             smashed == EL_LAMP_ACTIVE)
6609         {
6610           Bang(x, y + 1);
6611           return;
6612         }
6613         else if (smashed == EL_NUT)
6614         {
6615           Feld[x][y + 1] = EL_NUT_BREAKING;
6616           PlayLevelSound(x, y, SND_NUT_BREAKING);
6617           RaiseScoreElement(EL_NUT);
6618           return;
6619         }
6620         else if (smashed == EL_PEARL)
6621         {
6622           ResetGfxAnimation(x, y);
6623
6624           Feld[x][y + 1] = EL_PEARL_BREAKING;
6625           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6626           return;
6627         }
6628         else if (smashed == EL_DIAMOND)
6629         {
6630           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6631           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6632           return;
6633         }
6634         else if (IS_BELT_SWITCH(smashed))
6635         {
6636           ToggleBeltSwitch(x, y + 1);
6637         }
6638         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6639                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6640                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6641                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6642         {
6643           ToggleSwitchgateSwitch(x, y + 1);
6644         }
6645         else if (smashed == EL_LIGHT_SWITCH ||
6646                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6647         {
6648           ToggleLightSwitch(x, y + 1);
6649         }
6650         else
6651         {
6652           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6653
6654           CheckElementChangeBySide(x, y + 1, smashed, element,
6655                                    CE_SWITCHED, CH_SIDE_TOP);
6656           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6657                                             CH_SIDE_TOP);
6658         }
6659       }
6660       else
6661       {
6662         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6663       }
6664     }
6665   }
6666
6667   // play sound of magic wall / mill
6668   if (!last_line &&
6669       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6670        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6671        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6672   {
6673     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6674       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6675     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6676       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6677     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6678       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6679
6680     return;
6681   }
6682
6683   // play sound of object that hits the ground
6684   if (last_line || object_hit)
6685     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6686 }
6687
6688 static void TurnRoundExt(int x, int y)
6689 {
6690   static struct
6691   {
6692     int dx, dy;
6693   } move_xy[] =
6694   {
6695     {  0,  0 },
6696     { -1,  0 },
6697     { +1,  0 },
6698     {  0,  0 },
6699     {  0, -1 },
6700     {  0,  0 }, { 0, 0 }, { 0, 0 },
6701     {  0, +1 }
6702   };
6703   static struct
6704   {
6705     int left, right, back;
6706   } turn[] =
6707   {
6708     { 0,        0,              0        },
6709     { MV_DOWN,  MV_UP,          MV_RIGHT },
6710     { MV_UP,    MV_DOWN,        MV_LEFT  },
6711     { 0,        0,              0        },
6712     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6713     { 0,        0,              0        },
6714     { 0,        0,              0        },
6715     { 0,        0,              0        },
6716     { MV_RIGHT, MV_LEFT,        MV_UP    }
6717   };
6718
6719   int element = Feld[x][y];
6720   int move_pattern = element_info[element].move_pattern;
6721
6722   int old_move_dir = MovDir[x][y];
6723   int left_dir  = turn[old_move_dir].left;
6724   int right_dir = turn[old_move_dir].right;
6725   int back_dir  = turn[old_move_dir].back;
6726
6727   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6728   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6729   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6730   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6731
6732   int left_x  = x + left_dx,  left_y  = y + left_dy;
6733   int right_x = x + right_dx, right_y = y + right_dy;
6734   int move_x  = x + move_dx,  move_y  = y + move_dy;
6735
6736   int xx, yy;
6737
6738   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6739   {
6740     TestIfBadThingTouchesOtherBadThing(x, y);
6741
6742     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6743       MovDir[x][y] = right_dir;
6744     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6745       MovDir[x][y] = left_dir;
6746
6747     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6748       MovDelay[x][y] = 9;
6749     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6750       MovDelay[x][y] = 1;
6751   }
6752   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6753   {
6754     TestIfBadThingTouchesOtherBadThing(x, y);
6755
6756     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6757       MovDir[x][y] = left_dir;
6758     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6759       MovDir[x][y] = right_dir;
6760
6761     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6762       MovDelay[x][y] = 9;
6763     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6764       MovDelay[x][y] = 1;
6765   }
6766   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6767   {
6768     TestIfBadThingTouchesOtherBadThing(x, y);
6769
6770     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6771       MovDir[x][y] = left_dir;
6772     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6773       MovDir[x][y] = right_dir;
6774
6775     if (MovDir[x][y] != old_move_dir)
6776       MovDelay[x][y] = 9;
6777   }
6778   else if (element == EL_YAMYAM)
6779   {
6780     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6781     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6782
6783     if (can_turn_left && can_turn_right)
6784       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6785     else if (can_turn_left)
6786       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6787     else if (can_turn_right)
6788       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6789     else
6790       MovDir[x][y] = back_dir;
6791
6792     MovDelay[x][y] = 16 + 16 * RND(3);
6793   }
6794   else if (element == EL_DARK_YAMYAM)
6795   {
6796     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6797                                                          left_x, left_y);
6798     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6799                                                          right_x, right_y);
6800
6801     if (can_turn_left && can_turn_right)
6802       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6803     else if (can_turn_left)
6804       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6805     else if (can_turn_right)
6806       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6807     else
6808       MovDir[x][y] = back_dir;
6809
6810     MovDelay[x][y] = 16 + 16 * RND(3);
6811   }
6812   else if (element == EL_PACMAN)
6813   {
6814     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6815     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6816
6817     if (can_turn_left && can_turn_right)
6818       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6819     else if (can_turn_left)
6820       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6821     else if (can_turn_right)
6822       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6823     else
6824       MovDir[x][y] = back_dir;
6825
6826     MovDelay[x][y] = 6 + RND(40);
6827   }
6828   else if (element == EL_PIG)
6829   {
6830     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6831     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6832     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6833     boolean should_turn_left, should_turn_right, should_move_on;
6834     int rnd_value = 24;
6835     int rnd = RND(rnd_value);
6836
6837     should_turn_left = (can_turn_left &&
6838                         (!can_move_on ||
6839                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6840                                                    y + back_dy + left_dy)));
6841     should_turn_right = (can_turn_right &&
6842                          (!can_move_on ||
6843                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6844                                                     y + back_dy + right_dy)));
6845     should_move_on = (can_move_on &&
6846                       (!can_turn_left ||
6847                        !can_turn_right ||
6848                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6849                                                  y + move_dy + left_dy) ||
6850                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6851                                                  y + move_dy + right_dy)));
6852
6853     if (should_turn_left || should_turn_right || should_move_on)
6854     {
6855       if (should_turn_left && should_turn_right && should_move_on)
6856         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6857                         rnd < 2 * rnd_value / 3 ? right_dir :
6858                         old_move_dir);
6859       else if (should_turn_left && should_turn_right)
6860         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6861       else if (should_turn_left && should_move_on)
6862         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6863       else if (should_turn_right && should_move_on)
6864         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6865       else if (should_turn_left)
6866         MovDir[x][y] = left_dir;
6867       else if (should_turn_right)
6868         MovDir[x][y] = right_dir;
6869       else if (should_move_on)
6870         MovDir[x][y] = old_move_dir;
6871     }
6872     else if (can_move_on && rnd > rnd_value / 8)
6873       MovDir[x][y] = old_move_dir;
6874     else if (can_turn_left && can_turn_right)
6875       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6876     else if (can_turn_left && rnd > rnd_value / 8)
6877       MovDir[x][y] = left_dir;
6878     else if (can_turn_right && rnd > rnd_value/8)
6879       MovDir[x][y] = right_dir;
6880     else
6881       MovDir[x][y] = back_dir;
6882
6883     xx = x + move_xy[MovDir[x][y]].dx;
6884     yy = y + move_xy[MovDir[x][y]].dy;
6885
6886     if (!IN_LEV_FIELD(xx, yy) ||
6887         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6888       MovDir[x][y] = old_move_dir;
6889
6890     MovDelay[x][y] = 0;
6891   }
6892   else if (element == EL_DRAGON)
6893   {
6894     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6895     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6896     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6897     int rnd_value = 24;
6898     int rnd = RND(rnd_value);
6899
6900     if (can_move_on && rnd > rnd_value / 8)
6901       MovDir[x][y] = old_move_dir;
6902     else if (can_turn_left && can_turn_right)
6903       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6904     else if (can_turn_left && rnd > rnd_value / 8)
6905       MovDir[x][y] = left_dir;
6906     else if (can_turn_right && rnd > rnd_value / 8)
6907       MovDir[x][y] = right_dir;
6908     else
6909       MovDir[x][y] = back_dir;
6910
6911     xx = x + move_xy[MovDir[x][y]].dx;
6912     yy = y + move_xy[MovDir[x][y]].dy;
6913
6914     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6915       MovDir[x][y] = old_move_dir;
6916
6917     MovDelay[x][y] = 0;
6918   }
6919   else if (element == EL_MOLE)
6920   {
6921     boolean can_move_on =
6922       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6923                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6924                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6925     if (!can_move_on)
6926     {
6927       boolean can_turn_left =
6928         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6929                               IS_AMOEBOID(Feld[left_x][left_y])));
6930
6931       boolean can_turn_right =
6932         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6933                               IS_AMOEBOID(Feld[right_x][right_y])));
6934
6935       if (can_turn_left && can_turn_right)
6936         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6937       else if (can_turn_left)
6938         MovDir[x][y] = left_dir;
6939       else
6940         MovDir[x][y] = right_dir;
6941     }
6942
6943     if (MovDir[x][y] != old_move_dir)
6944       MovDelay[x][y] = 9;
6945   }
6946   else if (element == EL_BALLOON)
6947   {
6948     MovDir[x][y] = game.wind_direction;
6949     MovDelay[x][y] = 0;
6950   }
6951   else if (element == EL_SPRING)
6952   {
6953     if (MovDir[x][y] & MV_HORIZONTAL)
6954     {
6955       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6956           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6957       {
6958         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6959         ResetGfxAnimation(move_x, move_y);
6960         TEST_DrawLevelField(move_x, move_y);
6961
6962         MovDir[x][y] = back_dir;
6963       }
6964       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6965                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6966         MovDir[x][y] = MV_NONE;
6967     }
6968
6969     MovDelay[x][y] = 0;
6970   }
6971   else if (element == EL_ROBOT ||
6972            element == EL_SATELLITE ||
6973            element == EL_PENGUIN ||
6974            element == EL_EMC_ANDROID)
6975   {
6976     int attr_x = -1, attr_y = -1;
6977
6978     if (game.all_players_gone)
6979     {
6980       attr_x = game.exit_x;
6981       attr_y = game.exit_y;
6982     }
6983     else
6984     {
6985       int i;
6986
6987       for (i = 0; i < MAX_PLAYERS; i++)
6988       {
6989         struct PlayerInfo *player = &stored_player[i];
6990         int jx = player->jx, jy = player->jy;
6991
6992         if (!player->active)
6993           continue;
6994
6995         if (attr_x == -1 ||
6996             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6997         {
6998           attr_x = jx;
6999           attr_y = jy;
7000         }
7001       }
7002     }
7003
7004     if (element == EL_ROBOT &&
7005         game.robot_wheel_x >= 0 &&
7006         game.robot_wheel_y >= 0 &&
7007         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7008          game.engine_version < VERSION_IDENT(3,1,0,0)))
7009     {
7010       attr_x = game.robot_wheel_x;
7011       attr_y = game.robot_wheel_y;
7012     }
7013
7014     if (element == EL_PENGUIN)
7015     {
7016       int i;
7017       static int xy[4][2] =
7018       {
7019         { 0, -1 },
7020         { -1, 0 },
7021         { +1, 0 },
7022         { 0, +1 }
7023       };
7024
7025       for (i = 0; i < NUM_DIRECTIONS; i++)
7026       {
7027         int ex = x + xy[i][0];
7028         int ey = y + xy[i][1];
7029
7030         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7031                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7032                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7033                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7034         {
7035           attr_x = ex;
7036           attr_y = ey;
7037           break;
7038         }
7039       }
7040     }
7041
7042     MovDir[x][y] = MV_NONE;
7043     if (attr_x < x)
7044       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7045     else if (attr_x > x)
7046       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7047     if (attr_y < y)
7048       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7049     else if (attr_y > y)
7050       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7051
7052     if (element == EL_ROBOT)
7053     {
7054       int newx, newy;
7055
7056       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7057         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7058       Moving2Blocked(x, y, &newx, &newy);
7059
7060       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7061         MovDelay[x][y] = 8 + 8 * !RND(3);
7062       else
7063         MovDelay[x][y] = 16;
7064     }
7065     else if (element == EL_PENGUIN)
7066     {
7067       int newx, newy;
7068
7069       MovDelay[x][y] = 1;
7070
7071       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7072       {
7073         boolean first_horiz = RND(2);
7074         int new_move_dir = MovDir[x][y];
7075
7076         MovDir[x][y] =
7077           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7078         Moving2Blocked(x, y, &newx, &newy);
7079
7080         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7081           return;
7082
7083         MovDir[x][y] =
7084           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7085         Moving2Blocked(x, y, &newx, &newy);
7086
7087         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7088           return;
7089
7090         MovDir[x][y] = old_move_dir;
7091         return;
7092       }
7093     }
7094     else if (element == EL_SATELLITE)
7095     {
7096       int newx, newy;
7097
7098       MovDelay[x][y] = 1;
7099
7100       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7101       {
7102         boolean first_horiz = RND(2);
7103         int new_move_dir = MovDir[x][y];
7104
7105         MovDir[x][y] =
7106           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7107         Moving2Blocked(x, y, &newx, &newy);
7108
7109         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7110           return;
7111
7112         MovDir[x][y] =
7113           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7114         Moving2Blocked(x, y, &newx, &newy);
7115
7116         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7117           return;
7118
7119         MovDir[x][y] = old_move_dir;
7120         return;
7121       }
7122     }
7123     else if (element == EL_EMC_ANDROID)
7124     {
7125       static int check_pos[16] =
7126       {
7127         -1,             //  0 => (invalid)
7128         7,              //  1 => MV_LEFT
7129         3,              //  2 => MV_RIGHT
7130         -1,             //  3 => (invalid)
7131         1,              //  4 =>            MV_UP
7132         0,              //  5 => MV_LEFT  | MV_UP
7133         2,              //  6 => MV_RIGHT | MV_UP
7134         -1,             //  7 => (invalid)
7135         5,              //  8 =>            MV_DOWN
7136         6,              //  9 => MV_LEFT  | MV_DOWN
7137         4,              // 10 => MV_RIGHT | MV_DOWN
7138         -1,             // 11 => (invalid)
7139         -1,             // 12 => (invalid)
7140         -1,             // 13 => (invalid)
7141         -1,             // 14 => (invalid)
7142         -1,             // 15 => (invalid)
7143       };
7144       static struct
7145       {
7146         int dx, dy;
7147         int dir;
7148       } check_xy[8] =
7149       {
7150         { -1, -1,       MV_LEFT  | MV_UP   },
7151         {  0, -1,                  MV_UP   },
7152         { +1, -1,       MV_RIGHT | MV_UP   },
7153         { +1,  0,       MV_RIGHT           },
7154         { +1, +1,       MV_RIGHT | MV_DOWN },
7155         {  0, +1,                  MV_DOWN },
7156         { -1, +1,       MV_LEFT  | MV_DOWN },
7157         { -1,  0,       MV_LEFT            },
7158       };
7159       int start_pos, check_order;
7160       boolean can_clone = FALSE;
7161       int i;
7162
7163       // check if there is any free field around current position
7164       for (i = 0; i < 8; i++)
7165       {
7166         int newx = x + check_xy[i].dx;
7167         int newy = y + check_xy[i].dy;
7168
7169         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7170         {
7171           can_clone = TRUE;
7172
7173           break;
7174         }
7175       }
7176
7177       if (can_clone)            // randomly find an element to clone
7178       {
7179         can_clone = FALSE;
7180
7181         start_pos = check_pos[RND(8)];
7182         check_order = (RND(2) ? -1 : +1);
7183
7184         for (i = 0; i < 8; i++)
7185         {
7186           int pos_raw = start_pos + i * check_order;
7187           int pos = (pos_raw + 8) % 8;
7188           int newx = x + check_xy[pos].dx;
7189           int newy = y + check_xy[pos].dy;
7190
7191           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7192           {
7193             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7194             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7195
7196             Store[x][y] = Feld[newx][newy];
7197
7198             can_clone = TRUE;
7199
7200             break;
7201           }
7202         }
7203       }
7204
7205       if (can_clone)            // randomly find a direction to move
7206       {
7207         can_clone = FALSE;
7208
7209         start_pos = check_pos[RND(8)];
7210         check_order = (RND(2) ? -1 : +1);
7211
7212         for (i = 0; i < 8; i++)
7213         {
7214           int pos_raw = start_pos + i * check_order;
7215           int pos = (pos_raw + 8) % 8;
7216           int newx = x + check_xy[pos].dx;
7217           int newy = y + check_xy[pos].dy;
7218           int new_move_dir = check_xy[pos].dir;
7219
7220           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7221           {
7222             MovDir[x][y] = new_move_dir;
7223             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7224
7225             can_clone = TRUE;
7226
7227             break;
7228           }
7229         }
7230       }
7231
7232       if (can_clone)            // cloning and moving successful
7233         return;
7234
7235       // cannot clone -- try to move towards player
7236
7237       start_pos = check_pos[MovDir[x][y] & 0x0f];
7238       check_order = (RND(2) ? -1 : +1);
7239
7240       for (i = 0; i < 3; i++)
7241       {
7242         // first check start_pos, then previous/next or (next/previous) pos
7243         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7244         int pos = (pos_raw + 8) % 8;
7245         int newx = x + check_xy[pos].dx;
7246         int newy = y + check_xy[pos].dy;
7247         int new_move_dir = check_xy[pos].dir;
7248
7249         if (IS_PLAYER(newx, newy))
7250           break;
7251
7252         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7253         {
7254           MovDir[x][y] = new_move_dir;
7255           MovDelay[x][y] = level.android_move_time * 8 + 1;
7256
7257           break;
7258         }
7259       }
7260     }
7261   }
7262   else if (move_pattern == MV_TURNING_LEFT ||
7263            move_pattern == MV_TURNING_RIGHT ||
7264            move_pattern == MV_TURNING_LEFT_RIGHT ||
7265            move_pattern == MV_TURNING_RIGHT_LEFT ||
7266            move_pattern == MV_TURNING_RANDOM ||
7267            move_pattern == MV_ALL_DIRECTIONS)
7268   {
7269     boolean can_turn_left =
7270       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7271     boolean can_turn_right =
7272       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7273
7274     if (element_info[element].move_stepsize == 0)       // "not moving"
7275       return;
7276
7277     if (move_pattern == MV_TURNING_LEFT)
7278       MovDir[x][y] = left_dir;
7279     else if (move_pattern == MV_TURNING_RIGHT)
7280       MovDir[x][y] = right_dir;
7281     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7282       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7283     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7284       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7285     else if (move_pattern == MV_TURNING_RANDOM)
7286       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7287                       can_turn_right && !can_turn_left ? right_dir :
7288                       RND(2) ? left_dir : right_dir);
7289     else if (can_turn_left && can_turn_right)
7290       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7291     else if (can_turn_left)
7292       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7293     else if (can_turn_right)
7294       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7295     else
7296       MovDir[x][y] = back_dir;
7297
7298     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7299   }
7300   else if (move_pattern == MV_HORIZONTAL ||
7301            move_pattern == MV_VERTICAL)
7302   {
7303     if (move_pattern & old_move_dir)
7304       MovDir[x][y] = back_dir;
7305     else if (move_pattern == MV_HORIZONTAL)
7306       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7307     else if (move_pattern == MV_VERTICAL)
7308       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7309
7310     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7311   }
7312   else if (move_pattern & MV_ANY_DIRECTION)
7313   {
7314     MovDir[x][y] = move_pattern;
7315     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7316   }
7317   else if (move_pattern & MV_WIND_DIRECTION)
7318   {
7319     MovDir[x][y] = game.wind_direction;
7320     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7321   }
7322   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7323   {
7324     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7325       MovDir[x][y] = left_dir;
7326     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7327       MovDir[x][y] = right_dir;
7328
7329     if (MovDir[x][y] != old_move_dir)
7330       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7331   }
7332   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7333   {
7334     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7335       MovDir[x][y] = right_dir;
7336     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7337       MovDir[x][y] = left_dir;
7338
7339     if (MovDir[x][y] != old_move_dir)
7340       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7341   }
7342   else if (move_pattern == MV_TOWARDS_PLAYER ||
7343            move_pattern == MV_AWAY_FROM_PLAYER)
7344   {
7345     int attr_x = -1, attr_y = -1;
7346     int newx, newy;
7347     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7348
7349     if (game.all_players_gone)
7350     {
7351       attr_x = game.exit_x;
7352       attr_y = game.exit_y;
7353     }
7354     else
7355     {
7356       int i;
7357
7358       for (i = 0; i < MAX_PLAYERS; i++)
7359       {
7360         struct PlayerInfo *player = &stored_player[i];
7361         int jx = player->jx, jy = player->jy;
7362
7363         if (!player->active)
7364           continue;
7365
7366         if (attr_x == -1 ||
7367             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7368         {
7369           attr_x = jx;
7370           attr_y = jy;
7371         }
7372       }
7373     }
7374
7375     MovDir[x][y] = MV_NONE;
7376     if (attr_x < x)
7377       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7378     else if (attr_x > x)
7379       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7380     if (attr_y < y)
7381       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7382     else if (attr_y > y)
7383       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7384
7385     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7386
7387     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7388     {
7389       boolean first_horiz = RND(2);
7390       int new_move_dir = MovDir[x][y];
7391
7392       if (element_info[element].move_stepsize == 0)     // "not moving"
7393       {
7394         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7395         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7396
7397         return;
7398       }
7399
7400       MovDir[x][y] =
7401         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7402       Moving2Blocked(x, y, &newx, &newy);
7403
7404       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7405         return;
7406
7407       MovDir[x][y] =
7408         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7409       Moving2Blocked(x, y, &newx, &newy);
7410
7411       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7412         return;
7413
7414       MovDir[x][y] = old_move_dir;
7415     }
7416   }
7417   else if (move_pattern == MV_WHEN_PUSHED ||
7418            move_pattern == MV_WHEN_DROPPED)
7419   {
7420     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7421       MovDir[x][y] = MV_NONE;
7422
7423     MovDelay[x][y] = 0;
7424   }
7425   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7426   {
7427     static int test_xy[7][2] =
7428     {
7429       { 0, -1 },
7430       { -1, 0 },
7431       { +1, 0 },
7432       { 0, +1 },
7433       { 0, -1 },
7434       { -1, 0 },
7435       { +1, 0 },
7436     };
7437     static int test_dir[7] =
7438     {
7439       MV_UP,
7440       MV_LEFT,
7441       MV_RIGHT,
7442       MV_DOWN,
7443       MV_UP,
7444       MV_LEFT,
7445       MV_RIGHT,
7446     };
7447     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7448     int move_preference = -1000000;     // start with very low preference
7449     int new_move_dir = MV_NONE;
7450     int start_test = RND(4);
7451     int i;
7452
7453     for (i = 0; i < NUM_DIRECTIONS; i++)
7454     {
7455       int move_dir = test_dir[start_test + i];
7456       int move_dir_preference;
7457
7458       xx = x + test_xy[start_test + i][0];
7459       yy = y + test_xy[start_test + i][1];
7460
7461       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7462           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7463       {
7464         new_move_dir = move_dir;
7465
7466         break;
7467       }
7468
7469       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7470         continue;
7471
7472       move_dir_preference = -1 * RunnerVisit[xx][yy];
7473       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7474         move_dir_preference = PlayerVisit[xx][yy];
7475
7476       if (move_dir_preference > move_preference)
7477       {
7478         // prefer field that has not been visited for the longest time
7479         move_preference = move_dir_preference;
7480         new_move_dir = move_dir;
7481       }
7482       else if (move_dir_preference == move_preference &&
7483                move_dir == old_move_dir)
7484       {
7485         // prefer last direction when all directions are preferred equally
7486         move_preference = move_dir_preference;
7487         new_move_dir = move_dir;
7488       }
7489     }
7490
7491     MovDir[x][y] = new_move_dir;
7492     if (old_move_dir != new_move_dir)
7493       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7494   }
7495 }
7496
7497 static void TurnRound(int x, int y)
7498 {
7499   int direction = MovDir[x][y];
7500
7501   TurnRoundExt(x, y);
7502
7503   GfxDir[x][y] = MovDir[x][y];
7504
7505   if (direction != MovDir[x][y])
7506     GfxFrame[x][y] = 0;
7507
7508   if (MovDelay[x][y])
7509     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7510
7511   ResetGfxFrame(x, y);
7512 }
7513
7514 static boolean JustBeingPushed(int x, int y)
7515 {
7516   int i;
7517
7518   for (i = 0; i < MAX_PLAYERS; i++)
7519   {
7520     struct PlayerInfo *player = &stored_player[i];
7521
7522     if (player->active && player->is_pushing && player->MovPos)
7523     {
7524       int next_jx = player->jx + (player->jx - player->last_jx);
7525       int next_jy = player->jy + (player->jy - player->last_jy);
7526
7527       if (x == next_jx && y == next_jy)
7528         return TRUE;
7529     }
7530   }
7531
7532   return FALSE;
7533 }
7534
7535 static void StartMoving(int x, int y)
7536 {
7537   boolean started_moving = FALSE;       // some elements can fall _and_ move
7538   int element = Feld[x][y];
7539
7540   if (Stop[x][y])
7541     return;
7542
7543   if (MovDelay[x][y] == 0)
7544     GfxAction[x][y] = ACTION_DEFAULT;
7545
7546   if (CAN_FALL(element) && y < lev_fieldy - 1)
7547   {
7548     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7549         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7550       if (JustBeingPushed(x, y))
7551         return;
7552
7553     if (element == EL_QUICKSAND_FULL)
7554     {
7555       if (IS_FREE(x, y + 1))
7556       {
7557         InitMovingField(x, y, MV_DOWN);
7558         started_moving = TRUE;
7559
7560         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7561 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7562         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7563           Store[x][y] = EL_ROCK;
7564 #else
7565         Store[x][y] = EL_ROCK;
7566 #endif
7567
7568         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7569       }
7570       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7571       {
7572         if (!MovDelay[x][y])
7573         {
7574           MovDelay[x][y] = TILEY + 1;
7575
7576           ResetGfxAnimation(x, y);
7577           ResetGfxAnimation(x, y + 1);
7578         }
7579
7580         if (MovDelay[x][y])
7581         {
7582           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7583           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7584
7585           MovDelay[x][y]--;
7586           if (MovDelay[x][y])
7587             return;
7588         }
7589
7590         Feld[x][y] = EL_QUICKSAND_EMPTY;
7591         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7592         Store[x][y + 1] = Store[x][y];
7593         Store[x][y] = 0;
7594
7595         PlayLevelSoundAction(x, y, ACTION_FILLING);
7596       }
7597       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7598       {
7599         if (!MovDelay[x][y])
7600         {
7601           MovDelay[x][y] = TILEY + 1;
7602
7603           ResetGfxAnimation(x, y);
7604           ResetGfxAnimation(x, y + 1);
7605         }
7606
7607         if (MovDelay[x][y])
7608         {
7609           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7610           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7611
7612           MovDelay[x][y]--;
7613           if (MovDelay[x][y])
7614             return;
7615         }
7616
7617         Feld[x][y] = EL_QUICKSAND_EMPTY;
7618         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7619         Store[x][y + 1] = Store[x][y];
7620         Store[x][y] = 0;
7621
7622         PlayLevelSoundAction(x, y, ACTION_FILLING);
7623       }
7624     }
7625     else if (element == EL_QUICKSAND_FAST_FULL)
7626     {
7627       if (IS_FREE(x, y + 1))
7628       {
7629         InitMovingField(x, y, MV_DOWN);
7630         started_moving = TRUE;
7631
7632         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7633 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7634         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7635           Store[x][y] = EL_ROCK;
7636 #else
7637         Store[x][y] = EL_ROCK;
7638 #endif
7639
7640         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7641       }
7642       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7643       {
7644         if (!MovDelay[x][y])
7645         {
7646           MovDelay[x][y] = TILEY + 1;
7647
7648           ResetGfxAnimation(x, y);
7649           ResetGfxAnimation(x, y + 1);
7650         }
7651
7652         if (MovDelay[x][y])
7653         {
7654           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7655           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7656
7657           MovDelay[x][y]--;
7658           if (MovDelay[x][y])
7659             return;
7660         }
7661
7662         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7663         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7664         Store[x][y + 1] = Store[x][y];
7665         Store[x][y] = 0;
7666
7667         PlayLevelSoundAction(x, y, ACTION_FILLING);
7668       }
7669       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7670       {
7671         if (!MovDelay[x][y])
7672         {
7673           MovDelay[x][y] = TILEY + 1;
7674
7675           ResetGfxAnimation(x, y);
7676           ResetGfxAnimation(x, y + 1);
7677         }
7678
7679         if (MovDelay[x][y])
7680         {
7681           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7682           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7683
7684           MovDelay[x][y]--;
7685           if (MovDelay[x][y])
7686             return;
7687         }
7688
7689         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7690         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7691         Store[x][y + 1] = Store[x][y];
7692         Store[x][y] = 0;
7693
7694         PlayLevelSoundAction(x, y, ACTION_FILLING);
7695       }
7696     }
7697     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7698              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7699     {
7700       InitMovingField(x, y, MV_DOWN);
7701       started_moving = TRUE;
7702
7703       Feld[x][y] = EL_QUICKSAND_FILLING;
7704       Store[x][y] = element;
7705
7706       PlayLevelSoundAction(x, y, ACTION_FILLING);
7707     }
7708     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7709              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7710     {
7711       InitMovingField(x, y, MV_DOWN);
7712       started_moving = TRUE;
7713
7714       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7715       Store[x][y] = element;
7716
7717       PlayLevelSoundAction(x, y, ACTION_FILLING);
7718     }
7719     else if (element == EL_MAGIC_WALL_FULL)
7720     {
7721       if (IS_FREE(x, y + 1))
7722       {
7723         InitMovingField(x, y, MV_DOWN);
7724         started_moving = TRUE;
7725
7726         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7727         Store[x][y] = EL_CHANGED(Store[x][y]);
7728       }
7729       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7730       {
7731         if (!MovDelay[x][y])
7732           MovDelay[x][y] = TILEY / 4 + 1;
7733
7734         if (MovDelay[x][y])
7735         {
7736           MovDelay[x][y]--;
7737           if (MovDelay[x][y])
7738             return;
7739         }
7740
7741         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7742         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7743         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7744         Store[x][y] = 0;
7745       }
7746     }
7747     else if (element == EL_BD_MAGIC_WALL_FULL)
7748     {
7749       if (IS_FREE(x, y + 1))
7750       {
7751         InitMovingField(x, y, MV_DOWN);
7752         started_moving = TRUE;
7753
7754         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7755         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7756       }
7757       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7758       {
7759         if (!MovDelay[x][y])
7760           MovDelay[x][y] = TILEY / 4 + 1;
7761
7762         if (MovDelay[x][y])
7763         {
7764           MovDelay[x][y]--;
7765           if (MovDelay[x][y])
7766             return;
7767         }
7768
7769         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7770         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7771         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7772         Store[x][y] = 0;
7773       }
7774     }
7775     else if (element == EL_DC_MAGIC_WALL_FULL)
7776     {
7777       if (IS_FREE(x, y + 1))
7778       {
7779         InitMovingField(x, y, MV_DOWN);
7780         started_moving = TRUE;
7781
7782         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7783         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7784       }
7785       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7786       {
7787         if (!MovDelay[x][y])
7788           MovDelay[x][y] = TILEY / 4 + 1;
7789
7790         if (MovDelay[x][y])
7791         {
7792           MovDelay[x][y]--;
7793           if (MovDelay[x][y])
7794             return;
7795         }
7796
7797         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7798         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7799         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7800         Store[x][y] = 0;
7801       }
7802     }
7803     else if ((CAN_PASS_MAGIC_WALL(element) &&
7804               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7805                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7806              (CAN_PASS_DC_MAGIC_WALL(element) &&
7807               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7808
7809     {
7810       InitMovingField(x, y, MV_DOWN);
7811       started_moving = TRUE;
7812
7813       Feld[x][y] =
7814         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7815          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7816          EL_DC_MAGIC_WALL_FILLING);
7817       Store[x][y] = element;
7818     }
7819     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7820     {
7821       SplashAcid(x, y + 1);
7822
7823       InitMovingField(x, y, MV_DOWN);
7824       started_moving = TRUE;
7825
7826       Store[x][y] = EL_ACID;
7827     }
7828     else if (
7829              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7830               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7831              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7832               CAN_FALL(element) && WasJustFalling[x][y] &&
7833               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7834
7835              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7836               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7837               (Feld[x][y + 1] == EL_BLOCKED)))
7838     {
7839       /* this is needed for a special case not covered by calling "Impact()"
7840          from "ContinueMoving()": if an element moves to a tile directly below
7841          another element which was just falling on that tile (which was empty
7842          in the previous frame), the falling element above would just stop
7843          instead of smashing the element below (in previous version, the above
7844          element was just checked for "moving" instead of "falling", resulting
7845          in incorrect smashes caused by horizontal movement of the above
7846          element; also, the case of the player being the element to smash was
7847          simply not covered here... :-/ ) */
7848
7849       CheckCollision[x][y] = 0;
7850       CheckImpact[x][y] = 0;
7851
7852       Impact(x, y);
7853     }
7854     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7855     {
7856       if (MovDir[x][y] == MV_NONE)
7857       {
7858         InitMovingField(x, y, MV_DOWN);
7859         started_moving = TRUE;
7860       }
7861     }
7862     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7863     {
7864       if (WasJustFalling[x][y]) // prevent animation from being restarted
7865         MovDir[x][y] = MV_DOWN;
7866
7867       InitMovingField(x, y, MV_DOWN);
7868       started_moving = TRUE;
7869     }
7870     else if (element == EL_AMOEBA_DROP)
7871     {
7872       Feld[x][y] = EL_AMOEBA_GROWING;
7873       Store[x][y] = EL_AMOEBA_WET;
7874     }
7875     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7876               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7877              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7878              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7879     {
7880       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7881                                 (IS_FREE(x - 1, y + 1) ||
7882                                  Feld[x - 1][y + 1] == EL_ACID));
7883       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7884                                 (IS_FREE(x + 1, y + 1) ||
7885                                  Feld[x + 1][y + 1] == EL_ACID));
7886       boolean can_fall_any  = (can_fall_left || can_fall_right);
7887       boolean can_fall_both = (can_fall_left && can_fall_right);
7888       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7889
7890       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7891       {
7892         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7893           can_fall_right = FALSE;
7894         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7895           can_fall_left = FALSE;
7896         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7897           can_fall_right = FALSE;
7898         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7899           can_fall_left = FALSE;
7900
7901         can_fall_any  = (can_fall_left || can_fall_right);
7902         can_fall_both = FALSE;
7903       }
7904
7905       if (can_fall_both)
7906       {
7907         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7908           can_fall_right = FALSE;       // slip down on left side
7909         else
7910           can_fall_left = !(can_fall_right = RND(2));
7911
7912         can_fall_both = FALSE;
7913       }
7914
7915       if (can_fall_any)
7916       {
7917         // if not determined otherwise, prefer left side for slipping down
7918         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7919         started_moving = TRUE;
7920       }
7921     }
7922     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7923     {
7924       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7925       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7926       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7927       int belt_dir = game.belt_dir[belt_nr];
7928
7929       if ((belt_dir == MV_LEFT  && left_is_free) ||
7930           (belt_dir == MV_RIGHT && right_is_free))
7931       {
7932         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7933
7934         InitMovingField(x, y, belt_dir);
7935         started_moving = TRUE;
7936
7937         Pushed[x][y] = TRUE;
7938         Pushed[nextx][y] = TRUE;
7939
7940         GfxAction[x][y] = ACTION_DEFAULT;
7941       }
7942       else
7943       {
7944         MovDir[x][y] = 0;       // if element was moving, stop it
7945       }
7946     }
7947   }
7948
7949   // not "else if" because of elements that can fall and move (EL_SPRING)
7950   if (CAN_MOVE(element) && !started_moving)
7951   {
7952     int move_pattern = element_info[element].move_pattern;
7953     int newx, newy;
7954
7955     Moving2Blocked(x, y, &newx, &newy);
7956
7957     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7958       return;
7959
7960     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7961         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7962     {
7963       WasJustMoving[x][y] = 0;
7964       CheckCollision[x][y] = 0;
7965
7966       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7967
7968       if (Feld[x][y] != element)        // element has changed
7969         return;
7970     }
7971
7972     if (!MovDelay[x][y])        // start new movement phase
7973     {
7974       // all objects that can change their move direction after each step
7975       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7976
7977       if (element != EL_YAMYAM &&
7978           element != EL_DARK_YAMYAM &&
7979           element != EL_PACMAN &&
7980           !(move_pattern & MV_ANY_DIRECTION) &&
7981           move_pattern != MV_TURNING_LEFT &&
7982           move_pattern != MV_TURNING_RIGHT &&
7983           move_pattern != MV_TURNING_LEFT_RIGHT &&
7984           move_pattern != MV_TURNING_RIGHT_LEFT &&
7985           move_pattern != MV_TURNING_RANDOM)
7986       {
7987         TurnRound(x, y);
7988
7989         if (MovDelay[x][y] && (element == EL_BUG ||
7990                                element == EL_SPACESHIP ||
7991                                element == EL_SP_SNIKSNAK ||
7992                                element == EL_SP_ELECTRON ||
7993                                element == EL_MOLE))
7994           TEST_DrawLevelField(x, y);
7995       }
7996     }
7997
7998     if (MovDelay[x][y])         // wait some time before next movement
7999     {
8000       MovDelay[x][y]--;
8001
8002       if (element == EL_ROBOT ||
8003           element == EL_YAMYAM ||
8004           element == EL_DARK_YAMYAM)
8005       {
8006         DrawLevelElementAnimationIfNeeded(x, y, element);
8007         PlayLevelSoundAction(x, y, ACTION_WAITING);
8008       }
8009       else if (element == EL_SP_ELECTRON)
8010         DrawLevelElementAnimationIfNeeded(x, y, element);
8011       else if (element == EL_DRAGON)
8012       {
8013         int i;
8014         int dir = MovDir[x][y];
8015         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8016         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8017         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8018                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8019                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8020                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8021         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8022
8023         GfxAction[x][y] = ACTION_ATTACKING;
8024
8025         if (IS_PLAYER(x, y))
8026           DrawPlayerField(x, y);
8027         else
8028           TEST_DrawLevelField(x, y);
8029
8030         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8031
8032         for (i = 1; i <= 3; i++)
8033         {
8034           int xx = x + i * dx;
8035           int yy = y + i * dy;
8036           int sx = SCREENX(xx);
8037           int sy = SCREENY(yy);
8038           int flame_graphic = graphic + (i - 1);
8039
8040           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8041             break;
8042
8043           if (MovDelay[x][y])
8044           {
8045             int flamed = MovingOrBlocked2Element(xx, yy);
8046
8047             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8048               Bang(xx, yy);
8049             else
8050               RemoveMovingField(xx, yy);
8051
8052             ChangeDelay[xx][yy] = 0;
8053
8054             Feld[xx][yy] = EL_FLAMES;
8055
8056             if (IN_SCR_FIELD(sx, sy))
8057             {
8058               TEST_DrawLevelFieldCrumbled(xx, yy);
8059               DrawGraphic(sx, sy, flame_graphic, frame);
8060             }
8061           }
8062           else
8063           {
8064             if (Feld[xx][yy] == EL_FLAMES)
8065               Feld[xx][yy] = EL_EMPTY;
8066             TEST_DrawLevelField(xx, yy);
8067           }
8068         }
8069       }
8070
8071       if (MovDelay[x][y])       // element still has to wait some time
8072       {
8073         PlayLevelSoundAction(x, y, ACTION_WAITING);
8074
8075         return;
8076       }
8077     }
8078
8079     // now make next step
8080
8081     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8082
8083     if (DONT_COLLIDE_WITH(element) &&
8084         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8085         !PLAYER_ENEMY_PROTECTED(newx, newy))
8086     {
8087       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8088
8089       return;
8090     }
8091
8092     else if (CAN_MOVE_INTO_ACID(element) &&
8093              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8094              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8095              (MovDir[x][y] == MV_DOWN ||
8096               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8097     {
8098       SplashAcid(newx, newy);
8099       Store[x][y] = EL_ACID;
8100     }
8101     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8102     {
8103       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8104           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8105           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8106           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8107       {
8108         RemoveField(x, y);
8109         TEST_DrawLevelField(x, y);
8110
8111         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8112         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8113           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8114
8115         game.friends_still_needed--;
8116         if (!game.friends_still_needed &&
8117             !game.GameOver &&
8118             game.all_players_gone)
8119           LevelSolved();
8120
8121         return;
8122       }
8123       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8124       {
8125         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8126           TEST_DrawLevelField(newx, newy);
8127         else
8128           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8129       }
8130       else if (!IS_FREE(newx, newy))
8131       {
8132         GfxAction[x][y] = ACTION_WAITING;
8133
8134         if (IS_PLAYER(x, y))
8135           DrawPlayerField(x, y);
8136         else
8137           TEST_DrawLevelField(x, y);
8138
8139         return;
8140       }
8141     }
8142     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8143     {
8144       if (IS_FOOD_PIG(Feld[newx][newy]))
8145       {
8146         if (IS_MOVING(newx, newy))
8147           RemoveMovingField(newx, newy);
8148         else
8149         {
8150           Feld[newx][newy] = EL_EMPTY;
8151           TEST_DrawLevelField(newx, newy);
8152         }
8153
8154         PlayLevelSound(x, y, SND_PIG_DIGGING);
8155       }
8156       else if (!IS_FREE(newx, newy))
8157       {
8158         if (IS_PLAYER(x, y))
8159           DrawPlayerField(x, y);
8160         else
8161           TEST_DrawLevelField(x, y);
8162
8163         return;
8164       }
8165     }
8166     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8167     {
8168       if (Store[x][y] != EL_EMPTY)
8169       {
8170         boolean can_clone = FALSE;
8171         int xx, yy;
8172
8173         // check if element to clone is still there
8174         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8175         {
8176           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8177           {
8178             can_clone = TRUE;
8179
8180             break;
8181           }
8182         }
8183
8184         // cannot clone or target field not free anymore -- do not clone
8185         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8186           Store[x][y] = EL_EMPTY;
8187       }
8188
8189       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8190       {
8191         if (IS_MV_DIAGONAL(MovDir[x][y]))
8192         {
8193           int diagonal_move_dir = MovDir[x][y];
8194           int stored = Store[x][y];
8195           int change_delay = 8;
8196           int graphic;
8197
8198           // android is moving diagonally
8199
8200           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8201
8202           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8203           GfxElement[x][y] = EL_EMC_ANDROID;
8204           GfxAction[x][y] = ACTION_SHRINKING;
8205           GfxDir[x][y] = diagonal_move_dir;
8206           ChangeDelay[x][y] = change_delay;
8207
8208           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8209                                    GfxDir[x][y]);
8210
8211           DrawLevelGraphicAnimation(x, y, graphic);
8212           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8213
8214           if (Feld[newx][newy] == EL_ACID)
8215           {
8216             SplashAcid(newx, newy);
8217
8218             return;
8219           }
8220
8221           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8222
8223           Store[newx][newy] = EL_EMC_ANDROID;
8224           GfxElement[newx][newy] = EL_EMC_ANDROID;
8225           GfxAction[newx][newy] = ACTION_GROWING;
8226           GfxDir[newx][newy] = diagonal_move_dir;
8227           ChangeDelay[newx][newy] = change_delay;
8228
8229           graphic = el_act_dir2img(GfxElement[newx][newy],
8230                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8231
8232           DrawLevelGraphicAnimation(newx, newy, graphic);
8233           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8234
8235           return;
8236         }
8237         else
8238         {
8239           Feld[newx][newy] = EL_EMPTY;
8240           TEST_DrawLevelField(newx, newy);
8241
8242           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8243         }
8244       }
8245       else if (!IS_FREE(newx, newy))
8246       {
8247         return;
8248       }
8249     }
8250     else if (IS_CUSTOM_ELEMENT(element) &&
8251              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8252     {
8253       if (!DigFieldByCE(newx, newy, element))
8254         return;
8255
8256       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8257       {
8258         RunnerVisit[x][y] = FrameCounter;
8259         PlayerVisit[x][y] /= 8;         // expire player visit path
8260       }
8261     }
8262     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8263     {
8264       if (!IS_FREE(newx, newy))
8265       {
8266         if (IS_PLAYER(x, y))
8267           DrawPlayerField(x, y);
8268         else
8269           TEST_DrawLevelField(x, y);
8270
8271         return;
8272       }
8273       else
8274       {
8275         boolean wanna_flame = !RND(10);
8276         int dx = newx - x, dy = newy - y;
8277         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8278         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8279         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8280                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8281         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8282                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8283
8284         if ((wanna_flame ||
8285              IS_CLASSIC_ENEMY(element1) ||
8286              IS_CLASSIC_ENEMY(element2)) &&
8287             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8288             element1 != EL_FLAMES && element2 != EL_FLAMES)
8289         {
8290           ResetGfxAnimation(x, y);
8291           GfxAction[x][y] = ACTION_ATTACKING;
8292
8293           if (IS_PLAYER(x, y))
8294             DrawPlayerField(x, y);
8295           else
8296             TEST_DrawLevelField(x, y);
8297
8298           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8299
8300           MovDelay[x][y] = 50;
8301
8302           Feld[newx][newy] = EL_FLAMES;
8303           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8304             Feld[newx1][newy1] = EL_FLAMES;
8305           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8306             Feld[newx2][newy2] = EL_FLAMES;
8307
8308           return;
8309         }
8310       }
8311     }
8312     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8313              Feld[newx][newy] == EL_DIAMOND)
8314     {
8315       if (IS_MOVING(newx, newy))
8316         RemoveMovingField(newx, newy);
8317       else
8318       {
8319         Feld[newx][newy] = EL_EMPTY;
8320         TEST_DrawLevelField(newx, newy);
8321       }
8322
8323       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8324     }
8325     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8326              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8327     {
8328       if (AmoebaNr[newx][newy])
8329       {
8330         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8331         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8332             Feld[newx][newy] == EL_BD_AMOEBA)
8333           AmoebaCnt[AmoebaNr[newx][newy]]--;
8334       }
8335
8336       if (IS_MOVING(newx, newy))
8337       {
8338         RemoveMovingField(newx, newy);
8339       }
8340       else
8341       {
8342         Feld[newx][newy] = EL_EMPTY;
8343         TEST_DrawLevelField(newx, newy);
8344       }
8345
8346       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8347     }
8348     else if ((element == EL_PACMAN || element == EL_MOLE)
8349              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8350     {
8351       if (AmoebaNr[newx][newy])
8352       {
8353         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8354         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8355             Feld[newx][newy] == EL_BD_AMOEBA)
8356           AmoebaCnt[AmoebaNr[newx][newy]]--;
8357       }
8358
8359       if (element == EL_MOLE)
8360       {
8361         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8362         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8363
8364         ResetGfxAnimation(x, y);
8365         GfxAction[x][y] = ACTION_DIGGING;
8366         TEST_DrawLevelField(x, y);
8367
8368         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8369
8370         return;                         // wait for shrinking amoeba
8371       }
8372       else      // element == EL_PACMAN
8373       {
8374         Feld[newx][newy] = EL_EMPTY;
8375         TEST_DrawLevelField(newx, newy);
8376         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8377       }
8378     }
8379     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8380              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8381               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8382     {
8383       // wait for shrinking amoeba to completely disappear
8384       return;
8385     }
8386     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8387     {
8388       // object was running against a wall
8389
8390       TurnRound(x, y);
8391
8392       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8393         DrawLevelElementAnimation(x, y, element);
8394
8395       if (DONT_TOUCH(element))
8396         TestIfBadThingTouchesPlayer(x, y);
8397
8398       return;
8399     }
8400
8401     InitMovingField(x, y, MovDir[x][y]);
8402
8403     PlayLevelSoundAction(x, y, ACTION_MOVING);
8404   }
8405
8406   if (MovDir[x][y])
8407     ContinueMoving(x, y);
8408 }
8409
8410 void ContinueMoving(int x, int y)
8411 {
8412   int element = Feld[x][y];
8413   struct ElementInfo *ei = &element_info[element];
8414   int direction = MovDir[x][y];
8415   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8416   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8417   int newx = x + dx, newy = y + dy;
8418   int stored = Store[x][y];
8419   int stored_new = Store[newx][newy];
8420   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8421   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8422   boolean last_line = (newy == lev_fieldy - 1);
8423
8424   MovPos[x][y] += getElementMoveStepsize(x, y);
8425
8426   if (pushed_by_player) // special case: moving object pushed by player
8427     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8428
8429   if (ABS(MovPos[x][y]) < TILEX)
8430   {
8431     TEST_DrawLevelField(x, y);
8432
8433     return;     // element is still moving
8434   }
8435
8436   // element reached destination field
8437
8438   Feld[x][y] = EL_EMPTY;
8439   Feld[newx][newy] = element;
8440   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8441
8442   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8443   {
8444     element = Feld[newx][newy] = EL_ACID;
8445   }
8446   else if (element == EL_MOLE)
8447   {
8448     Feld[x][y] = EL_SAND;
8449
8450     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8451   }
8452   else if (element == EL_QUICKSAND_FILLING)
8453   {
8454     element = Feld[newx][newy] = get_next_element(element);
8455     Store[newx][newy] = Store[x][y];
8456   }
8457   else if (element == EL_QUICKSAND_EMPTYING)
8458   {
8459     Feld[x][y] = get_next_element(element);
8460     element = Feld[newx][newy] = Store[x][y];
8461   }
8462   else if (element == EL_QUICKSAND_FAST_FILLING)
8463   {
8464     element = Feld[newx][newy] = get_next_element(element);
8465     Store[newx][newy] = Store[x][y];
8466   }
8467   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8468   {
8469     Feld[x][y] = get_next_element(element);
8470     element = Feld[newx][newy] = Store[x][y];
8471   }
8472   else if (element == EL_MAGIC_WALL_FILLING)
8473   {
8474     element = Feld[newx][newy] = get_next_element(element);
8475     if (!game.magic_wall_active)
8476       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8477     Store[newx][newy] = Store[x][y];
8478   }
8479   else if (element == EL_MAGIC_WALL_EMPTYING)
8480   {
8481     Feld[x][y] = get_next_element(element);
8482     if (!game.magic_wall_active)
8483       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8484     element = Feld[newx][newy] = Store[x][y];
8485
8486     InitField(newx, newy, FALSE);
8487   }
8488   else if (element == EL_BD_MAGIC_WALL_FILLING)
8489   {
8490     element = Feld[newx][newy] = get_next_element(element);
8491     if (!game.magic_wall_active)
8492       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8493     Store[newx][newy] = Store[x][y];
8494   }
8495   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8496   {
8497     Feld[x][y] = get_next_element(element);
8498     if (!game.magic_wall_active)
8499       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8500     element = Feld[newx][newy] = Store[x][y];
8501
8502     InitField(newx, newy, FALSE);
8503   }
8504   else if (element == EL_DC_MAGIC_WALL_FILLING)
8505   {
8506     element = Feld[newx][newy] = get_next_element(element);
8507     if (!game.magic_wall_active)
8508       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8509     Store[newx][newy] = Store[x][y];
8510   }
8511   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8512   {
8513     Feld[x][y] = get_next_element(element);
8514     if (!game.magic_wall_active)
8515       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8516     element = Feld[newx][newy] = Store[x][y];
8517
8518     InitField(newx, newy, FALSE);
8519   }
8520   else if (element == EL_AMOEBA_DROPPING)
8521   {
8522     Feld[x][y] = get_next_element(element);
8523     element = Feld[newx][newy] = Store[x][y];
8524   }
8525   else if (element == EL_SOKOBAN_OBJECT)
8526   {
8527     if (Back[x][y])
8528       Feld[x][y] = Back[x][y];
8529
8530     if (Back[newx][newy])
8531       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8532
8533     Back[x][y] = Back[newx][newy] = 0;
8534   }
8535
8536   Store[x][y] = EL_EMPTY;
8537   MovPos[x][y] = 0;
8538   MovDir[x][y] = 0;
8539   MovDelay[x][y] = 0;
8540
8541   MovDelay[newx][newy] = 0;
8542
8543   if (CAN_CHANGE_OR_HAS_ACTION(element))
8544   {
8545     // copy element change control values to new field
8546     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8547     ChangePage[newx][newy]  = ChangePage[x][y];
8548     ChangeCount[newx][newy] = ChangeCount[x][y];
8549     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8550   }
8551
8552   CustomValue[newx][newy] = CustomValue[x][y];
8553
8554   ChangeDelay[x][y] = 0;
8555   ChangePage[x][y] = -1;
8556   ChangeCount[x][y] = 0;
8557   ChangeEvent[x][y] = -1;
8558
8559   CustomValue[x][y] = 0;
8560
8561   // copy animation control values to new field
8562   GfxFrame[newx][newy]  = GfxFrame[x][y];
8563   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8564   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8565   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8566
8567   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8568
8569   // some elements can leave other elements behind after moving
8570   if (ei->move_leave_element != EL_EMPTY &&
8571       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8572       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8573   {
8574     int move_leave_element = ei->move_leave_element;
8575
8576     // this makes it possible to leave the removed element again
8577     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8578       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8579
8580     Feld[x][y] = move_leave_element;
8581
8582     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8583       MovDir[x][y] = direction;
8584
8585     InitField(x, y, FALSE);
8586
8587     if (GFX_CRUMBLED(Feld[x][y]))
8588       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8589
8590     if (ELEM_IS_PLAYER(move_leave_element))
8591       RelocatePlayer(x, y, move_leave_element);
8592   }
8593
8594   // do this after checking for left-behind element
8595   ResetGfxAnimation(x, y);      // reset animation values for old field
8596
8597   if (!CAN_MOVE(element) ||
8598       (CAN_FALL(element) && direction == MV_DOWN &&
8599        (element == EL_SPRING ||
8600         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8601         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8602     GfxDir[x][y] = MovDir[newx][newy] = 0;
8603
8604   TEST_DrawLevelField(x, y);
8605   TEST_DrawLevelField(newx, newy);
8606
8607   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8608
8609   // prevent pushed element from moving on in pushed direction
8610   if (pushed_by_player && CAN_MOVE(element) &&
8611       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8612       !(element_info[element].move_pattern & direction))
8613     TurnRound(newx, newy);
8614
8615   // prevent elements on conveyor belt from moving on in last direction
8616   if (pushed_by_conveyor && CAN_FALL(element) &&
8617       direction & MV_HORIZONTAL)
8618     MovDir[newx][newy] = 0;
8619
8620   if (!pushed_by_player)
8621   {
8622     int nextx = newx + dx, nexty = newy + dy;
8623     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8624
8625     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8626
8627     if (CAN_FALL(element) && direction == MV_DOWN)
8628       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8629
8630     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8631       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8632
8633     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8634       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8635   }
8636
8637   if (DONT_TOUCH(element))      // object may be nasty to player or others
8638   {
8639     TestIfBadThingTouchesPlayer(newx, newy);
8640     TestIfBadThingTouchesFriend(newx, newy);
8641
8642     if (!IS_CUSTOM_ELEMENT(element))
8643       TestIfBadThingTouchesOtherBadThing(newx, newy);
8644   }
8645   else if (element == EL_PENGUIN)
8646     TestIfFriendTouchesBadThing(newx, newy);
8647
8648   if (DONT_GET_HIT_BY(element))
8649   {
8650     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8651   }
8652
8653   // give the player one last chance (one more frame) to move away
8654   if (CAN_FALL(element) && direction == MV_DOWN &&
8655       (last_line || (!IS_FREE(x, newy + 1) &&
8656                      (!IS_PLAYER(x, newy + 1) ||
8657                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8658     Impact(x, newy);
8659
8660   if (pushed_by_player && !game.use_change_when_pushing_bug)
8661   {
8662     int push_side = MV_DIR_OPPOSITE(direction);
8663     struct PlayerInfo *player = PLAYERINFO(x, y);
8664
8665     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8666                                player->index_bit, push_side);
8667     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8668                                         player->index_bit, push_side);
8669   }
8670
8671   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8672     MovDelay[newx][newy] = 1;
8673
8674   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8675
8676   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8677   TestIfElementHitsCustomElement(newx, newy, direction);
8678   TestIfPlayerTouchesCustomElement(newx, newy);
8679   TestIfElementTouchesCustomElement(newx, newy);
8680
8681   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8682       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8683     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8684                              MV_DIR_OPPOSITE(direction));
8685 }
8686
8687 int AmoebeNachbarNr(int ax, int ay)
8688 {
8689   int i;
8690   int element = Feld[ax][ay];
8691   int group_nr = 0;
8692   static int xy[4][2] =
8693   {
8694     { 0, -1 },
8695     { -1, 0 },
8696     { +1, 0 },
8697     { 0, +1 }
8698   };
8699
8700   for (i = 0; i < NUM_DIRECTIONS; i++)
8701   {
8702     int x = ax + xy[i][0];
8703     int y = ay + xy[i][1];
8704
8705     if (!IN_LEV_FIELD(x, y))
8706       continue;
8707
8708     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8709       group_nr = AmoebaNr[x][y];
8710   }
8711
8712   return group_nr;
8713 }
8714
8715 static void AmoebenVereinigen(int ax, int ay)
8716 {
8717   int i, x, y, xx, yy;
8718   int new_group_nr = AmoebaNr[ax][ay];
8719   static int xy[4][2] =
8720   {
8721     { 0, -1 },
8722     { -1, 0 },
8723     { +1, 0 },
8724     { 0, +1 }
8725   };
8726
8727   if (new_group_nr == 0)
8728     return;
8729
8730   for (i = 0; i < NUM_DIRECTIONS; i++)
8731   {
8732     x = ax + xy[i][0];
8733     y = ay + xy[i][1];
8734
8735     if (!IN_LEV_FIELD(x, y))
8736       continue;
8737
8738     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8739          Feld[x][y] == EL_BD_AMOEBA ||
8740          Feld[x][y] == EL_AMOEBA_DEAD) &&
8741         AmoebaNr[x][y] != new_group_nr)
8742     {
8743       int old_group_nr = AmoebaNr[x][y];
8744
8745       if (old_group_nr == 0)
8746         return;
8747
8748       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8749       AmoebaCnt[old_group_nr] = 0;
8750       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8751       AmoebaCnt2[old_group_nr] = 0;
8752
8753       SCAN_PLAYFIELD(xx, yy)
8754       {
8755         if (AmoebaNr[xx][yy] == old_group_nr)
8756           AmoebaNr[xx][yy] = new_group_nr;
8757       }
8758     }
8759   }
8760 }
8761
8762 void AmoebeUmwandeln(int ax, int ay)
8763 {
8764   int i, x, y;
8765
8766   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8767   {
8768     int group_nr = AmoebaNr[ax][ay];
8769
8770 #ifdef DEBUG
8771     if (group_nr == 0)
8772     {
8773       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8774       printf("AmoebeUmwandeln(): This should never happen!\n");
8775       return;
8776     }
8777 #endif
8778
8779     SCAN_PLAYFIELD(x, y)
8780     {
8781       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8782       {
8783         AmoebaNr[x][y] = 0;
8784         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8785       }
8786     }
8787
8788     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8789                             SND_AMOEBA_TURNING_TO_GEM :
8790                             SND_AMOEBA_TURNING_TO_ROCK));
8791     Bang(ax, ay);
8792   }
8793   else
8794   {
8795     static int xy[4][2] =
8796     {
8797       { 0, -1 },
8798       { -1, 0 },
8799       { +1, 0 },
8800       { 0, +1 }
8801     };
8802
8803     for (i = 0; i < NUM_DIRECTIONS; i++)
8804     {
8805       x = ax + xy[i][0];
8806       y = ay + xy[i][1];
8807
8808       if (!IN_LEV_FIELD(x, y))
8809         continue;
8810
8811       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8812       {
8813         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8814                               SND_AMOEBA_TURNING_TO_GEM :
8815                               SND_AMOEBA_TURNING_TO_ROCK));
8816         Bang(x, y);
8817       }
8818     }
8819   }
8820 }
8821
8822 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8823 {
8824   int x, y;
8825   int group_nr = AmoebaNr[ax][ay];
8826   boolean done = FALSE;
8827
8828 #ifdef DEBUG
8829   if (group_nr == 0)
8830   {
8831     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8832     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8833     return;
8834   }
8835 #endif
8836
8837   SCAN_PLAYFIELD(x, y)
8838   {
8839     if (AmoebaNr[x][y] == group_nr &&
8840         (Feld[x][y] == EL_AMOEBA_DEAD ||
8841          Feld[x][y] == EL_BD_AMOEBA ||
8842          Feld[x][y] == EL_AMOEBA_GROWING))
8843     {
8844       AmoebaNr[x][y] = 0;
8845       Feld[x][y] = new_element;
8846       InitField(x, y, FALSE);
8847       TEST_DrawLevelField(x, y);
8848       done = TRUE;
8849     }
8850   }
8851
8852   if (done)
8853     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8854                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8855                             SND_BD_AMOEBA_TURNING_TO_GEM));
8856 }
8857
8858 static void AmoebeWaechst(int x, int y)
8859 {
8860   static unsigned int sound_delay = 0;
8861   static unsigned int sound_delay_value = 0;
8862
8863   if (!MovDelay[x][y])          // start new growing cycle
8864   {
8865     MovDelay[x][y] = 7;
8866
8867     if (DelayReached(&sound_delay, sound_delay_value))
8868     {
8869       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8870       sound_delay_value = 30;
8871     }
8872   }
8873
8874   if (MovDelay[x][y])           // wait some time before growing bigger
8875   {
8876     MovDelay[x][y]--;
8877     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8878     {
8879       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8880                                            6 - MovDelay[x][y]);
8881
8882       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8883     }
8884
8885     if (!MovDelay[x][y])
8886     {
8887       Feld[x][y] = Store[x][y];
8888       Store[x][y] = 0;
8889       TEST_DrawLevelField(x, y);
8890     }
8891   }
8892 }
8893
8894 static void AmoebaDisappearing(int x, int y)
8895 {
8896   static unsigned int sound_delay = 0;
8897   static unsigned int sound_delay_value = 0;
8898
8899   if (!MovDelay[x][y])          // start new shrinking cycle
8900   {
8901     MovDelay[x][y] = 7;
8902
8903     if (DelayReached(&sound_delay, sound_delay_value))
8904       sound_delay_value = 30;
8905   }
8906
8907   if (MovDelay[x][y])           // wait some time before shrinking
8908   {
8909     MovDelay[x][y]--;
8910     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8911     {
8912       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8913                                            6 - MovDelay[x][y]);
8914
8915       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8916     }
8917
8918     if (!MovDelay[x][y])
8919     {
8920       Feld[x][y] = EL_EMPTY;
8921       TEST_DrawLevelField(x, y);
8922
8923       // don't let mole enter this field in this cycle;
8924       // (give priority to objects falling to this field from above)
8925       Stop[x][y] = TRUE;
8926     }
8927   }
8928 }
8929
8930 static void AmoebeAbleger(int ax, int ay)
8931 {
8932   int i;
8933   int element = Feld[ax][ay];
8934   int graphic = el2img(element);
8935   int newax = ax, neway = ay;
8936   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8937   static int xy[4][2] =
8938   {
8939     { 0, -1 },
8940     { -1, 0 },
8941     { +1, 0 },
8942     { 0, +1 }
8943   };
8944
8945   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8946   {
8947     Feld[ax][ay] = EL_AMOEBA_DEAD;
8948     TEST_DrawLevelField(ax, ay);
8949     return;
8950   }
8951
8952   if (IS_ANIMATED(graphic))
8953     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8954
8955   if (!MovDelay[ax][ay])        // start making new amoeba field
8956     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8957
8958   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8959   {
8960     MovDelay[ax][ay]--;
8961     if (MovDelay[ax][ay])
8962       return;
8963   }
8964
8965   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8966   {
8967     int start = RND(4);
8968     int x = ax + xy[start][0];
8969     int y = ay + xy[start][1];
8970
8971     if (!IN_LEV_FIELD(x, y))
8972       return;
8973
8974     if (IS_FREE(x, y) ||
8975         CAN_GROW_INTO(Feld[x][y]) ||
8976         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8977         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8978     {
8979       newax = x;
8980       neway = y;
8981     }
8982
8983     if (newax == ax && neway == ay)
8984       return;
8985   }
8986   else                          // normal or "filled" (BD style) amoeba
8987   {
8988     int start = RND(4);
8989     boolean waiting_for_player = FALSE;
8990
8991     for (i = 0; i < NUM_DIRECTIONS; i++)
8992     {
8993       int j = (start + i) % 4;
8994       int x = ax + xy[j][0];
8995       int y = ay + xy[j][1];
8996
8997       if (!IN_LEV_FIELD(x, y))
8998         continue;
8999
9000       if (IS_FREE(x, y) ||
9001           CAN_GROW_INTO(Feld[x][y]) ||
9002           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9003           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9004       {
9005         newax = x;
9006         neway = y;
9007         break;
9008       }
9009       else if (IS_PLAYER(x, y))
9010         waiting_for_player = TRUE;
9011     }
9012
9013     if (newax == ax && neway == ay)             // amoeba cannot grow
9014     {
9015       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9016       {
9017         Feld[ax][ay] = EL_AMOEBA_DEAD;
9018         TEST_DrawLevelField(ax, ay);
9019         AmoebaCnt[AmoebaNr[ax][ay]]--;
9020
9021         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9022         {
9023           if (element == EL_AMOEBA_FULL)
9024             AmoebeUmwandeln(ax, ay);
9025           else if (element == EL_BD_AMOEBA)
9026             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9027         }
9028       }
9029       return;
9030     }
9031     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9032     {
9033       // amoeba gets larger by growing in some direction
9034
9035       int new_group_nr = AmoebaNr[ax][ay];
9036
9037 #ifdef DEBUG
9038   if (new_group_nr == 0)
9039   {
9040     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9041     printf("AmoebeAbleger(): This should never happen!\n");
9042     return;
9043   }
9044 #endif
9045
9046       AmoebaNr[newax][neway] = new_group_nr;
9047       AmoebaCnt[new_group_nr]++;
9048       AmoebaCnt2[new_group_nr]++;
9049
9050       // if amoeba touches other amoeba(s) after growing, unify them
9051       AmoebenVereinigen(newax, neway);
9052
9053       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9054       {
9055         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9056         return;
9057       }
9058     }
9059   }
9060
9061   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9062       (neway == lev_fieldy - 1 && newax != ax))
9063   {
9064     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9065     Store[newax][neway] = element;
9066   }
9067   else if (neway == ay || element == EL_EMC_DRIPPER)
9068   {
9069     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9070
9071     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9072   }
9073   else
9074   {
9075     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9076     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9077     Store[ax][ay] = EL_AMOEBA_DROP;
9078     ContinueMoving(ax, ay);
9079     return;
9080   }
9081
9082   TEST_DrawLevelField(newax, neway);
9083 }
9084
9085 static void Life(int ax, int ay)
9086 {
9087   int x1, y1, x2, y2;
9088   int life_time = 40;
9089   int element = Feld[ax][ay];
9090   int graphic = el2img(element);
9091   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9092                          level.biomaze);
9093   boolean changed = FALSE;
9094
9095   if (IS_ANIMATED(graphic))
9096     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9097
9098   if (Stop[ax][ay])
9099     return;
9100
9101   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9102     MovDelay[ax][ay] = life_time;
9103
9104   if (MovDelay[ax][ay])         // wait some time before next cycle
9105   {
9106     MovDelay[ax][ay]--;
9107     if (MovDelay[ax][ay])
9108       return;
9109   }
9110
9111   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9112   {
9113     int xx = ax+x1, yy = ay+y1;
9114     int old_element = Feld[xx][yy];
9115     int num_neighbours = 0;
9116
9117     if (!IN_LEV_FIELD(xx, yy))
9118       continue;
9119
9120     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9121     {
9122       int x = xx+x2, y = yy+y2;
9123
9124       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9125         continue;
9126
9127       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9128       boolean is_neighbour = FALSE;
9129
9130       if (level.use_life_bugs)
9131         is_neighbour =
9132           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9133            (IS_FREE(x, y)                             &&  Stop[x][y]));
9134       else
9135         is_neighbour =
9136           (Last[x][y] == element || is_player_cell);
9137
9138       if (is_neighbour)
9139         num_neighbours++;
9140     }
9141
9142     boolean is_free = FALSE;
9143
9144     if (level.use_life_bugs)
9145       is_free = (IS_FREE(xx, yy));
9146     else
9147       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9148
9149     if (xx == ax && yy == ay)           // field in the middle
9150     {
9151       if (num_neighbours < life_parameter[0] ||
9152           num_neighbours > life_parameter[1])
9153       {
9154         Feld[xx][yy] = EL_EMPTY;
9155         if (Feld[xx][yy] != old_element)
9156           TEST_DrawLevelField(xx, yy);
9157         Stop[xx][yy] = TRUE;
9158         changed = TRUE;
9159       }
9160     }
9161     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9162     {                                   // free border field
9163       if (num_neighbours >= life_parameter[2] &&
9164           num_neighbours <= life_parameter[3])
9165       {
9166         Feld[xx][yy] = element;
9167         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9168         if (Feld[xx][yy] != old_element)
9169           TEST_DrawLevelField(xx, yy);
9170         Stop[xx][yy] = TRUE;
9171         changed = TRUE;
9172       }
9173     }
9174   }
9175
9176   if (changed)
9177     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9178                    SND_GAME_OF_LIFE_GROWING);
9179 }
9180
9181 static void InitRobotWheel(int x, int y)
9182 {
9183   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9184 }
9185
9186 static void RunRobotWheel(int x, int y)
9187 {
9188   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9189 }
9190
9191 static void StopRobotWheel(int x, int y)
9192 {
9193   if (game.robot_wheel_x == x &&
9194       game.robot_wheel_y == y)
9195   {
9196     game.robot_wheel_x = -1;
9197     game.robot_wheel_y = -1;
9198     game.robot_wheel_active = FALSE;
9199   }
9200 }
9201
9202 static void InitTimegateWheel(int x, int y)
9203 {
9204   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9205 }
9206
9207 static void RunTimegateWheel(int x, int y)
9208 {
9209   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9210 }
9211
9212 static void InitMagicBallDelay(int x, int y)
9213 {
9214   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9215 }
9216
9217 static void ActivateMagicBall(int bx, int by)
9218 {
9219   int x, y;
9220
9221   if (level.ball_random)
9222   {
9223     int pos_border = RND(8);    // select one of the eight border elements
9224     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9225     int xx = pos_content % 3;
9226     int yy = pos_content / 3;
9227
9228     x = bx - 1 + xx;
9229     y = by - 1 + yy;
9230
9231     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9232       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9233   }
9234   else
9235   {
9236     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9237     {
9238       int xx = x - bx + 1;
9239       int yy = y - by + 1;
9240
9241       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9242         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9243     }
9244   }
9245
9246   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9247 }
9248
9249 static void CheckExit(int x, int y)
9250 {
9251   if (game.gems_still_needed > 0 ||
9252       game.sokoban_fields_still_needed > 0 ||
9253       game.sokoban_objects_still_needed > 0 ||
9254       game.lights_still_needed > 0)
9255   {
9256     int element = Feld[x][y];
9257     int graphic = el2img(element);
9258
9259     if (IS_ANIMATED(graphic))
9260       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9261
9262     return;
9263   }
9264
9265   // do not re-open exit door closed after last player
9266   if (game.all_players_gone)
9267     return;
9268
9269   Feld[x][y] = EL_EXIT_OPENING;
9270
9271   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9272 }
9273
9274 static void CheckExitEM(int x, int y)
9275 {
9276   if (game.gems_still_needed > 0 ||
9277       game.sokoban_fields_still_needed > 0 ||
9278       game.sokoban_objects_still_needed > 0 ||
9279       game.lights_still_needed > 0)
9280   {
9281     int element = Feld[x][y];
9282     int graphic = el2img(element);
9283
9284     if (IS_ANIMATED(graphic))
9285       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9286
9287     return;
9288   }
9289
9290   // do not re-open exit door closed after last player
9291   if (game.all_players_gone)
9292     return;
9293
9294   Feld[x][y] = EL_EM_EXIT_OPENING;
9295
9296   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9297 }
9298
9299 static void CheckExitSteel(int x, int y)
9300 {
9301   if (game.gems_still_needed > 0 ||
9302       game.sokoban_fields_still_needed > 0 ||
9303       game.sokoban_objects_still_needed > 0 ||
9304       game.lights_still_needed > 0)
9305   {
9306     int element = Feld[x][y];
9307     int graphic = el2img(element);
9308
9309     if (IS_ANIMATED(graphic))
9310       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9311
9312     return;
9313   }
9314
9315   // do not re-open exit door closed after last player
9316   if (game.all_players_gone)
9317     return;
9318
9319   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9320
9321   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9322 }
9323
9324 static void CheckExitSteelEM(int x, int y)
9325 {
9326   if (game.gems_still_needed > 0 ||
9327       game.sokoban_fields_still_needed > 0 ||
9328       game.sokoban_objects_still_needed > 0 ||
9329       game.lights_still_needed > 0)
9330   {
9331     int element = Feld[x][y];
9332     int graphic = el2img(element);
9333
9334     if (IS_ANIMATED(graphic))
9335       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9336
9337     return;
9338   }
9339
9340   // do not re-open exit door closed after last player
9341   if (game.all_players_gone)
9342     return;
9343
9344   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9345
9346   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9347 }
9348
9349 static void CheckExitSP(int x, int y)
9350 {
9351   if (game.gems_still_needed > 0)
9352   {
9353     int element = Feld[x][y];
9354     int graphic = el2img(element);
9355
9356     if (IS_ANIMATED(graphic))
9357       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9358
9359     return;
9360   }
9361
9362   // do not re-open exit door closed after last player
9363   if (game.all_players_gone)
9364     return;
9365
9366   Feld[x][y] = EL_SP_EXIT_OPENING;
9367
9368   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9369 }
9370
9371 static void CloseAllOpenTimegates(void)
9372 {
9373   int x, y;
9374
9375   SCAN_PLAYFIELD(x, y)
9376   {
9377     int element = Feld[x][y];
9378
9379     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9380     {
9381       Feld[x][y] = EL_TIMEGATE_CLOSING;
9382
9383       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9384     }
9385   }
9386 }
9387
9388 static void DrawTwinkleOnField(int x, int y)
9389 {
9390   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9391     return;
9392
9393   if (Feld[x][y] == EL_BD_DIAMOND)
9394     return;
9395
9396   if (MovDelay[x][y] == 0)      // next animation frame
9397     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9398
9399   if (MovDelay[x][y] != 0)      // wait some time before next frame
9400   {
9401     MovDelay[x][y]--;
9402
9403     DrawLevelElementAnimation(x, y, Feld[x][y]);
9404
9405     if (MovDelay[x][y] != 0)
9406     {
9407       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9408                                            10 - MovDelay[x][y]);
9409
9410       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9411     }
9412   }
9413 }
9414
9415 static void MauerWaechst(int x, int y)
9416 {
9417   int delay = 6;
9418
9419   if (!MovDelay[x][y])          // next animation frame
9420     MovDelay[x][y] = 3 * delay;
9421
9422   if (MovDelay[x][y])           // wait some time before next frame
9423   {
9424     MovDelay[x][y]--;
9425
9426     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9427     {
9428       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9429       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9430
9431       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9432     }
9433
9434     if (!MovDelay[x][y])
9435     {
9436       if (MovDir[x][y] == MV_LEFT)
9437       {
9438         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9439           TEST_DrawLevelField(x - 1, y);
9440       }
9441       else if (MovDir[x][y] == MV_RIGHT)
9442       {
9443         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9444           TEST_DrawLevelField(x + 1, y);
9445       }
9446       else if (MovDir[x][y] == MV_UP)
9447       {
9448         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9449           TEST_DrawLevelField(x, y - 1);
9450       }
9451       else
9452       {
9453         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9454           TEST_DrawLevelField(x, y + 1);
9455       }
9456
9457       Feld[x][y] = Store[x][y];
9458       Store[x][y] = 0;
9459       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9460       TEST_DrawLevelField(x, y);
9461     }
9462   }
9463 }
9464
9465 static void MauerAbleger(int ax, int ay)
9466 {
9467   int element = Feld[ax][ay];
9468   int graphic = el2img(element);
9469   boolean oben_frei = FALSE, unten_frei = FALSE;
9470   boolean links_frei = FALSE, rechts_frei = FALSE;
9471   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9472   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9473   boolean new_wall = FALSE;
9474
9475   if (IS_ANIMATED(graphic))
9476     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9477
9478   if (!MovDelay[ax][ay])        // start building new wall
9479     MovDelay[ax][ay] = 6;
9480
9481   if (MovDelay[ax][ay])         // wait some time before building new wall
9482   {
9483     MovDelay[ax][ay]--;
9484     if (MovDelay[ax][ay])
9485       return;
9486   }
9487
9488   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9489     oben_frei = TRUE;
9490   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9491     unten_frei = TRUE;
9492   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9493     links_frei = TRUE;
9494   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9495     rechts_frei = TRUE;
9496
9497   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9498       element == EL_EXPANDABLE_WALL_ANY)
9499   {
9500     if (oben_frei)
9501     {
9502       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9503       Store[ax][ay-1] = element;
9504       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9505       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9506         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9507                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9508       new_wall = TRUE;
9509     }
9510     if (unten_frei)
9511     {
9512       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9513       Store[ax][ay+1] = element;
9514       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9515       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9516         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9517                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9518       new_wall = TRUE;
9519     }
9520   }
9521
9522   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9523       element == EL_EXPANDABLE_WALL_ANY ||
9524       element == EL_EXPANDABLE_WALL ||
9525       element == EL_BD_EXPANDABLE_WALL)
9526   {
9527     if (links_frei)
9528     {
9529       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9530       Store[ax-1][ay] = element;
9531       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9532       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9533         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9534                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9535       new_wall = TRUE;
9536     }
9537
9538     if (rechts_frei)
9539     {
9540       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9541       Store[ax+1][ay] = element;
9542       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9543       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9544         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9545                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9546       new_wall = TRUE;
9547     }
9548   }
9549
9550   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9551     TEST_DrawLevelField(ax, ay);
9552
9553   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9554     oben_massiv = TRUE;
9555   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9556     unten_massiv = TRUE;
9557   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9558     links_massiv = TRUE;
9559   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9560     rechts_massiv = TRUE;
9561
9562   if (((oben_massiv && unten_massiv) ||
9563        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9564        element == EL_EXPANDABLE_WALL) &&
9565       ((links_massiv && rechts_massiv) ||
9566        element == EL_EXPANDABLE_WALL_VERTICAL))
9567     Feld[ax][ay] = EL_WALL;
9568
9569   if (new_wall)
9570     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9571 }
9572
9573 static void MauerAblegerStahl(int ax, int ay)
9574 {
9575   int element = Feld[ax][ay];
9576   int graphic = el2img(element);
9577   boolean oben_frei = FALSE, unten_frei = FALSE;
9578   boolean links_frei = FALSE, rechts_frei = FALSE;
9579   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9580   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9581   boolean new_wall = FALSE;
9582
9583   if (IS_ANIMATED(graphic))
9584     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9585
9586   if (!MovDelay[ax][ay])        // start building new wall
9587     MovDelay[ax][ay] = 6;
9588
9589   if (MovDelay[ax][ay])         // wait some time before building new wall
9590   {
9591     MovDelay[ax][ay]--;
9592     if (MovDelay[ax][ay])
9593       return;
9594   }
9595
9596   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9597     oben_frei = TRUE;
9598   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9599     unten_frei = TRUE;
9600   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9601     links_frei = TRUE;
9602   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9603     rechts_frei = TRUE;
9604
9605   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9606       element == EL_EXPANDABLE_STEELWALL_ANY)
9607   {
9608     if (oben_frei)
9609     {
9610       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9611       Store[ax][ay-1] = element;
9612       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9613       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9614         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9615                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9616       new_wall = TRUE;
9617     }
9618     if (unten_frei)
9619     {
9620       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9621       Store[ax][ay+1] = element;
9622       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9623       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9624         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9625                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9626       new_wall = TRUE;
9627     }
9628   }
9629
9630   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9631       element == EL_EXPANDABLE_STEELWALL_ANY)
9632   {
9633     if (links_frei)
9634     {
9635       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9636       Store[ax-1][ay] = element;
9637       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9638       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9639         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9640                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9641       new_wall = TRUE;
9642     }
9643
9644     if (rechts_frei)
9645     {
9646       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9647       Store[ax+1][ay] = element;
9648       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9649       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9650         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9651                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9652       new_wall = TRUE;
9653     }
9654   }
9655
9656   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9657     oben_massiv = TRUE;
9658   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9659     unten_massiv = TRUE;
9660   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9661     links_massiv = TRUE;
9662   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9663     rechts_massiv = TRUE;
9664
9665   if (((oben_massiv && unten_massiv) ||
9666        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9667       ((links_massiv && rechts_massiv) ||
9668        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9669     Feld[ax][ay] = EL_STEELWALL;
9670
9671   if (new_wall)
9672     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9673 }
9674
9675 static void CheckForDragon(int x, int y)
9676 {
9677   int i, j;
9678   boolean dragon_found = FALSE;
9679   static int xy[4][2] =
9680   {
9681     { 0, -1 },
9682     { -1, 0 },
9683     { +1, 0 },
9684     { 0, +1 }
9685   };
9686
9687   for (i = 0; i < NUM_DIRECTIONS; i++)
9688   {
9689     for (j = 0; j < 4; j++)
9690     {
9691       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9692
9693       if (IN_LEV_FIELD(xx, yy) &&
9694           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9695       {
9696         if (Feld[xx][yy] == EL_DRAGON)
9697           dragon_found = TRUE;
9698       }
9699       else
9700         break;
9701     }
9702   }
9703
9704   if (!dragon_found)
9705   {
9706     for (i = 0; i < NUM_DIRECTIONS; i++)
9707     {
9708       for (j = 0; j < 3; j++)
9709       {
9710         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9711   
9712         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9713         {
9714           Feld[xx][yy] = EL_EMPTY;
9715           TEST_DrawLevelField(xx, yy);
9716         }
9717         else
9718           break;
9719       }
9720     }
9721   }
9722 }
9723
9724 static void InitBuggyBase(int x, int y)
9725 {
9726   int element = Feld[x][y];
9727   int activating_delay = FRAMES_PER_SECOND / 4;
9728
9729   ChangeDelay[x][y] =
9730     (element == EL_SP_BUGGY_BASE ?
9731      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9732      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9733      activating_delay :
9734      element == EL_SP_BUGGY_BASE_ACTIVE ?
9735      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9736 }
9737
9738 static void WarnBuggyBase(int x, int y)
9739 {
9740   int i;
9741   static int xy[4][2] =
9742   {
9743     { 0, -1 },
9744     { -1, 0 },
9745     { +1, 0 },
9746     { 0, +1 }
9747   };
9748
9749   for (i = 0; i < NUM_DIRECTIONS; i++)
9750   {
9751     int xx = x + xy[i][0];
9752     int yy = y + xy[i][1];
9753
9754     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9755     {
9756       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9757
9758       break;
9759     }
9760   }
9761 }
9762
9763 static void InitTrap(int x, int y)
9764 {
9765   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9766 }
9767
9768 static void ActivateTrap(int x, int y)
9769 {
9770   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9771 }
9772
9773 static void ChangeActiveTrap(int x, int y)
9774 {
9775   int graphic = IMG_TRAP_ACTIVE;
9776
9777   // if new animation frame was drawn, correct crumbled sand border
9778   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9779     TEST_DrawLevelFieldCrumbled(x, y);
9780 }
9781
9782 static int getSpecialActionElement(int element, int number, int base_element)
9783 {
9784   return (element != EL_EMPTY ? element :
9785           number != -1 ? base_element + number - 1 :
9786           EL_EMPTY);
9787 }
9788
9789 static int getModifiedActionNumber(int value_old, int operator, int operand,
9790                                    int value_min, int value_max)
9791 {
9792   int value_new = (operator == CA_MODE_SET      ? operand :
9793                    operator == CA_MODE_ADD      ? value_old + operand :
9794                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9795                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9796                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9797                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9798                    value_old);
9799
9800   return (value_new < value_min ? value_min :
9801           value_new > value_max ? value_max :
9802           value_new);
9803 }
9804
9805 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9806 {
9807   struct ElementInfo *ei = &element_info[element];
9808   struct ElementChangeInfo *change = &ei->change_page[page];
9809   int target_element = change->target_element;
9810   int action_type = change->action_type;
9811   int action_mode = change->action_mode;
9812   int action_arg = change->action_arg;
9813   int action_element = change->action_element;
9814   int i;
9815
9816   if (!change->has_action)
9817     return;
9818
9819   // ---------- determine action paramater values -----------------------------
9820
9821   int level_time_value =
9822     (level.time > 0 ? TimeLeft :
9823      TimePlayed);
9824
9825   int action_arg_element_raw =
9826     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9827      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9828      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9829      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9830      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9831      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9832      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9833      EL_EMPTY);
9834   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9835
9836   int action_arg_direction =
9837     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9838      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9839      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9840      change->actual_trigger_side :
9841      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9842      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9843      MV_NONE);
9844
9845   int action_arg_number_min =
9846     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9847      CA_ARG_MIN);
9848
9849   int action_arg_number_max =
9850     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9851      action_type == CA_SET_LEVEL_GEMS ? 999 :
9852      action_type == CA_SET_LEVEL_TIME ? 9999 :
9853      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9854      action_type == CA_SET_CE_VALUE ? 9999 :
9855      action_type == CA_SET_CE_SCORE ? 9999 :
9856      CA_ARG_MAX);
9857
9858   int action_arg_number_reset =
9859     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9860      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9861      action_type == CA_SET_LEVEL_TIME ? level.time :
9862      action_type == CA_SET_LEVEL_SCORE ? 0 :
9863      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9864      action_type == CA_SET_CE_SCORE ? 0 :
9865      0);
9866
9867   int action_arg_number =
9868     (action_arg <= CA_ARG_MAX ? action_arg :
9869      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9870      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9871      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9872      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9873      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9874      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9875      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9876      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9877      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9878      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9879      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9880      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9881      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9882      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9883      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9884      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9885      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9886      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9887      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9888      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9889      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9890      -1);
9891
9892   int action_arg_number_old =
9893     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9894      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9895      action_type == CA_SET_LEVEL_SCORE ? game.score :
9896      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9897      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9898      0);
9899
9900   int action_arg_number_new =
9901     getModifiedActionNumber(action_arg_number_old,
9902                             action_mode, action_arg_number,
9903                             action_arg_number_min, action_arg_number_max);
9904
9905   int trigger_player_bits =
9906     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9907      change->actual_trigger_player_bits : change->trigger_player);
9908
9909   int action_arg_player_bits =
9910     (action_arg >= CA_ARG_PLAYER_1 &&
9911      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9912      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9913      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9914      PLAYER_BITS_ANY);
9915
9916   // ---------- execute action  -----------------------------------------------
9917
9918   switch (action_type)
9919   {
9920     case CA_NO_ACTION:
9921     {
9922       return;
9923     }
9924
9925     // ---------- level actions  ----------------------------------------------
9926
9927     case CA_RESTART_LEVEL:
9928     {
9929       game.restart_level = TRUE;
9930
9931       break;
9932     }
9933
9934     case CA_SHOW_ENVELOPE:
9935     {
9936       int element = getSpecialActionElement(action_arg_element,
9937                                             action_arg_number, EL_ENVELOPE_1);
9938
9939       if (IS_ENVELOPE(element))
9940         local_player->show_envelope = element;
9941
9942       break;
9943     }
9944
9945     case CA_SET_LEVEL_TIME:
9946     {
9947       if (level.time > 0)       // only modify limited time value
9948       {
9949         TimeLeft = action_arg_number_new;
9950
9951         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9952
9953         DisplayGameControlValues();
9954
9955         if (!TimeLeft && setup.time_limit)
9956           for (i = 0; i < MAX_PLAYERS; i++)
9957             KillPlayer(&stored_player[i]);
9958       }
9959
9960       break;
9961     }
9962
9963     case CA_SET_LEVEL_SCORE:
9964     {
9965       game.score = action_arg_number_new;
9966
9967       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9968
9969       DisplayGameControlValues();
9970
9971       break;
9972     }
9973
9974     case CA_SET_LEVEL_GEMS:
9975     {
9976       game.gems_still_needed = action_arg_number_new;
9977
9978       game.snapshot.collected_item = TRUE;
9979
9980       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9981
9982       DisplayGameControlValues();
9983
9984       break;
9985     }
9986
9987     case CA_SET_LEVEL_WIND:
9988     {
9989       game.wind_direction = action_arg_direction;
9990
9991       break;
9992     }
9993
9994     case CA_SET_LEVEL_RANDOM_SEED:
9995     {
9996       // ensure that setting a new random seed while playing is predictable
9997       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9998
9999       break;
10000     }
10001
10002     // ---------- player actions  ---------------------------------------------
10003
10004     case CA_MOVE_PLAYER:
10005     case CA_MOVE_PLAYER_NEW:
10006     {
10007       // automatically move to the next field in specified direction
10008       for (i = 0; i < MAX_PLAYERS; i++)
10009         if (trigger_player_bits & (1 << i))
10010           if (action_type == CA_MOVE_PLAYER ||
10011               stored_player[i].MovPos == 0)
10012             stored_player[i].programmed_action = action_arg_direction;
10013
10014       break;
10015     }
10016
10017     case CA_EXIT_PLAYER:
10018     {
10019       for (i = 0; i < MAX_PLAYERS; i++)
10020         if (action_arg_player_bits & (1 << i))
10021           ExitPlayer(&stored_player[i]);
10022
10023       if (game.players_still_needed == 0)
10024         LevelSolved();
10025
10026       break;
10027     }
10028
10029     case CA_KILL_PLAYER:
10030     {
10031       for (i = 0; i < MAX_PLAYERS; i++)
10032         if (action_arg_player_bits & (1 << i))
10033           KillPlayer(&stored_player[i]);
10034
10035       break;
10036     }
10037
10038     case CA_SET_PLAYER_KEYS:
10039     {
10040       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10041       int element = getSpecialActionElement(action_arg_element,
10042                                             action_arg_number, EL_KEY_1);
10043
10044       if (IS_KEY(element))
10045       {
10046         for (i = 0; i < MAX_PLAYERS; i++)
10047         {
10048           if (trigger_player_bits & (1 << i))
10049           {
10050             stored_player[i].key[KEY_NR(element)] = key_state;
10051
10052             DrawGameDoorValues();
10053           }
10054         }
10055       }
10056
10057       break;
10058     }
10059
10060     case CA_SET_PLAYER_SPEED:
10061     {
10062       for (i = 0; i < MAX_PLAYERS; i++)
10063       {
10064         if (trigger_player_bits & (1 << i))
10065         {
10066           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10067
10068           if (action_arg == CA_ARG_SPEED_FASTER &&
10069               stored_player[i].cannot_move)
10070           {
10071             action_arg_number = STEPSIZE_VERY_SLOW;
10072           }
10073           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10074                    action_arg == CA_ARG_SPEED_FASTER)
10075           {
10076             action_arg_number = 2;
10077             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10078                            CA_MODE_MULTIPLY);
10079           }
10080           else if (action_arg == CA_ARG_NUMBER_RESET)
10081           {
10082             action_arg_number = level.initial_player_stepsize[i];
10083           }
10084
10085           move_stepsize =
10086             getModifiedActionNumber(move_stepsize,
10087                                     action_mode,
10088                                     action_arg_number,
10089                                     action_arg_number_min,
10090                                     action_arg_number_max);
10091
10092           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10093         }
10094       }
10095
10096       break;
10097     }
10098
10099     case CA_SET_PLAYER_SHIELD:
10100     {
10101       for (i = 0; i < MAX_PLAYERS; i++)
10102       {
10103         if (trigger_player_bits & (1 << i))
10104         {
10105           if (action_arg == CA_ARG_SHIELD_OFF)
10106           {
10107             stored_player[i].shield_normal_time_left = 0;
10108             stored_player[i].shield_deadly_time_left = 0;
10109           }
10110           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10111           {
10112             stored_player[i].shield_normal_time_left = 999999;
10113           }
10114           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10115           {
10116             stored_player[i].shield_normal_time_left = 999999;
10117             stored_player[i].shield_deadly_time_left = 999999;
10118           }
10119         }
10120       }
10121
10122       break;
10123     }
10124
10125     case CA_SET_PLAYER_GRAVITY:
10126     {
10127       for (i = 0; i < MAX_PLAYERS; i++)
10128       {
10129         if (trigger_player_bits & (1 << i))
10130         {
10131           stored_player[i].gravity =
10132             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10133              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10134              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10135              stored_player[i].gravity);
10136         }
10137       }
10138
10139       break;
10140     }
10141
10142     case CA_SET_PLAYER_ARTWORK:
10143     {
10144       for (i = 0; i < MAX_PLAYERS; i++)
10145       {
10146         if (trigger_player_bits & (1 << i))
10147         {
10148           int artwork_element = action_arg_element;
10149
10150           if (action_arg == CA_ARG_ELEMENT_RESET)
10151             artwork_element =
10152               (level.use_artwork_element[i] ? level.artwork_element[i] :
10153                stored_player[i].element_nr);
10154
10155           if (stored_player[i].artwork_element != artwork_element)
10156             stored_player[i].Frame = 0;
10157
10158           stored_player[i].artwork_element = artwork_element;
10159
10160           SetPlayerWaiting(&stored_player[i], FALSE);
10161
10162           // set number of special actions for bored and sleeping animation
10163           stored_player[i].num_special_action_bored =
10164             get_num_special_action(artwork_element,
10165                                    ACTION_BORING_1, ACTION_BORING_LAST);
10166           stored_player[i].num_special_action_sleeping =
10167             get_num_special_action(artwork_element,
10168                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10169         }
10170       }
10171
10172       break;
10173     }
10174
10175     case CA_SET_PLAYER_INVENTORY:
10176     {
10177       for (i = 0; i < MAX_PLAYERS; i++)
10178       {
10179         struct PlayerInfo *player = &stored_player[i];
10180         int j, k;
10181
10182         if (trigger_player_bits & (1 << i))
10183         {
10184           int inventory_element = action_arg_element;
10185
10186           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10187               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10188               action_arg == CA_ARG_ELEMENT_ACTION)
10189           {
10190             int element = inventory_element;
10191             int collect_count = element_info[element].collect_count_initial;
10192
10193             if (!IS_CUSTOM_ELEMENT(element))
10194               collect_count = 1;
10195
10196             if (collect_count == 0)
10197               player->inventory_infinite_element = element;
10198             else
10199               for (k = 0; k < collect_count; k++)
10200                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10201                   player->inventory_element[player->inventory_size++] =
10202                     element;
10203           }
10204           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10205                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10206                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10207           {
10208             if (player->inventory_infinite_element != EL_UNDEFINED &&
10209                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10210                                      action_arg_element_raw))
10211               player->inventory_infinite_element = EL_UNDEFINED;
10212
10213             for (k = 0, j = 0; j < player->inventory_size; j++)
10214             {
10215               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10216                                         action_arg_element_raw))
10217                 player->inventory_element[k++] = player->inventory_element[j];
10218             }
10219
10220             player->inventory_size = k;
10221           }
10222           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10223           {
10224             if (player->inventory_size > 0)
10225             {
10226               for (j = 0; j < player->inventory_size - 1; j++)
10227                 player->inventory_element[j] = player->inventory_element[j + 1];
10228
10229               player->inventory_size--;
10230             }
10231           }
10232           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10233           {
10234             if (player->inventory_size > 0)
10235               player->inventory_size--;
10236           }
10237           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10238           {
10239             player->inventory_infinite_element = EL_UNDEFINED;
10240             player->inventory_size = 0;
10241           }
10242           else if (action_arg == CA_ARG_INVENTORY_RESET)
10243           {
10244             player->inventory_infinite_element = EL_UNDEFINED;
10245             player->inventory_size = 0;
10246
10247             if (level.use_initial_inventory[i])
10248             {
10249               for (j = 0; j < level.initial_inventory_size[i]; j++)
10250               {
10251                 int element = level.initial_inventory_content[i][j];
10252                 int collect_count = element_info[element].collect_count_initial;
10253
10254                 if (!IS_CUSTOM_ELEMENT(element))
10255                   collect_count = 1;
10256
10257                 if (collect_count == 0)
10258                   player->inventory_infinite_element = element;
10259                 else
10260                   for (k = 0; k < collect_count; k++)
10261                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10262                       player->inventory_element[player->inventory_size++] =
10263                         element;
10264               }
10265             }
10266           }
10267         }
10268       }
10269
10270       break;
10271     }
10272
10273     // ---------- CE actions  -------------------------------------------------
10274
10275     case CA_SET_CE_VALUE:
10276     {
10277       int last_ce_value = CustomValue[x][y];
10278
10279       CustomValue[x][y] = action_arg_number_new;
10280
10281       if (CustomValue[x][y] != last_ce_value)
10282       {
10283         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10284         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10285
10286         if (CustomValue[x][y] == 0)
10287         {
10288           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10289           ChangeCount[x][y] = 0;        // allow at least one more change
10290
10291           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10292           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10293         }
10294       }
10295
10296       break;
10297     }
10298
10299     case CA_SET_CE_SCORE:
10300     {
10301       int last_ce_score = ei->collect_score;
10302
10303       ei->collect_score = action_arg_number_new;
10304
10305       if (ei->collect_score != last_ce_score)
10306       {
10307         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10308         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10309
10310         if (ei->collect_score == 0)
10311         {
10312           int xx, yy;
10313
10314           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10315           ChangeCount[x][y] = 0;        // allow at least one more change
10316
10317           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10318           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10319
10320           /*
10321             This is a very special case that seems to be a mixture between
10322             CheckElementChange() and CheckTriggeredElementChange(): while
10323             the first one only affects single elements that are triggered
10324             directly, the second one affects multiple elements in the playfield
10325             that are triggered indirectly by another element. This is a third
10326             case: Changing the CE score always affects multiple identical CEs,
10327             so every affected CE must be checked, not only the single CE for
10328             which the CE score was changed in the first place (as every instance
10329             of that CE shares the same CE score, and therefore also can change)!
10330           */
10331           SCAN_PLAYFIELD(xx, yy)
10332           {
10333             if (Feld[xx][yy] == element)
10334               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10335                                  CE_SCORE_GETS_ZERO);
10336           }
10337         }
10338       }
10339
10340       break;
10341     }
10342
10343     case CA_SET_CE_ARTWORK:
10344     {
10345       int artwork_element = action_arg_element;
10346       boolean reset_frame = FALSE;
10347       int xx, yy;
10348
10349       if (action_arg == CA_ARG_ELEMENT_RESET)
10350         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10351                            element);
10352
10353       if (ei->gfx_element != artwork_element)
10354         reset_frame = TRUE;
10355
10356       ei->gfx_element = artwork_element;
10357
10358       SCAN_PLAYFIELD(xx, yy)
10359       {
10360         if (Feld[xx][yy] == element)
10361         {
10362           if (reset_frame)
10363           {
10364             ResetGfxAnimation(xx, yy);
10365             ResetRandomAnimationValue(xx, yy);
10366           }
10367
10368           TEST_DrawLevelField(xx, yy);
10369         }
10370       }
10371
10372       break;
10373     }
10374
10375     // ---------- engine actions  ---------------------------------------------
10376
10377     case CA_SET_ENGINE_SCAN_MODE:
10378     {
10379       InitPlayfieldScanMode(action_arg);
10380
10381       break;
10382     }
10383
10384     default:
10385       break;
10386   }
10387 }
10388
10389 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10390 {
10391   int old_element = Feld[x][y];
10392   int new_element = GetElementFromGroupElement(element);
10393   int previous_move_direction = MovDir[x][y];
10394   int last_ce_value = CustomValue[x][y];
10395   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10396   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10397   boolean add_player_onto_element = (new_element_is_player &&
10398                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10399                                      IS_WALKABLE(old_element));
10400
10401   if (!add_player_onto_element)
10402   {
10403     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10404       RemoveMovingField(x, y);
10405     else
10406       RemoveField(x, y);
10407
10408     Feld[x][y] = new_element;
10409
10410     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10411       MovDir[x][y] = previous_move_direction;
10412
10413     if (element_info[new_element].use_last_ce_value)
10414       CustomValue[x][y] = last_ce_value;
10415
10416     InitField_WithBug1(x, y, FALSE);
10417
10418     new_element = Feld[x][y];   // element may have changed
10419
10420     ResetGfxAnimation(x, y);
10421     ResetRandomAnimationValue(x, y);
10422
10423     TEST_DrawLevelField(x, y);
10424
10425     if (GFX_CRUMBLED(new_element))
10426       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10427   }
10428
10429   // check if element under the player changes from accessible to unaccessible
10430   // (needed for special case of dropping element which then changes)
10431   // (must be checked after creating new element for walkable group elements)
10432   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10433       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10434   {
10435     Bang(x, y);
10436
10437     return;
10438   }
10439
10440   // "ChangeCount" not set yet to allow "entered by player" change one time
10441   if (new_element_is_player)
10442     RelocatePlayer(x, y, new_element);
10443
10444   if (is_change)
10445     ChangeCount[x][y]++;        // count number of changes in the same frame
10446
10447   TestIfBadThingTouchesPlayer(x, y);
10448   TestIfPlayerTouchesCustomElement(x, y);
10449   TestIfElementTouchesCustomElement(x, y);
10450 }
10451
10452 static void CreateField(int x, int y, int element)
10453 {
10454   CreateFieldExt(x, y, element, FALSE);
10455 }
10456
10457 static void CreateElementFromChange(int x, int y, int element)
10458 {
10459   element = GET_VALID_RUNTIME_ELEMENT(element);
10460
10461   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10462   {
10463     int old_element = Feld[x][y];
10464
10465     // prevent changed element from moving in same engine frame
10466     // unless both old and new element can either fall or move
10467     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10468         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10469       Stop[x][y] = TRUE;
10470   }
10471
10472   CreateFieldExt(x, y, element, TRUE);
10473 }
10474
10475 static boolean ChangeElement(int x, int y, int element, int page)
10476 {
10477   struct ElementInfo *ei = &element_info[element];
10478   struct ElementChangeInfo *change = &ei->change_page[page];
10479   int ce_value = CustomValue[x][y];
10480   int ce_score = ei->collect_score;
10481   int target_element;
10482   int old_element = Feld[x][y];
10483
10484   // always use default change event to prevent running into a loop
10485   if (ChangeEvent[x][y] == -1)
10486     ChangeEvent[x][y] = CE_DELAY;
10487
10488   if (ChangeEvent[x][y] == CE_DELAY)
10489   {
10490     // reset actual trigger element, trigger player and action element
10491     change->actual_trigger_element = EL_EMPTY;
10492     change->actual_trigger_player = EL_EMPTY;
10493     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10494     change->actual_trigger_side = CH_SIDE_NONE;
10495     change->actual_trigger_ce_value = 0;
10496     change->actual_trigger_ce_score = 0;
10497   }
10498
10499   // do not change elements more than a specified maximum number of changes
10500   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10501     return FALSE;
10502
10503   ChangeCount[x][y]++;          // count number of changes in the same frame
10504
10505   if (change->explode)
10506   {
10507     Bang(x, y);
10508
10509     return TRUE;
10510   }
10511
10512   if (change->use_target_content)
10513   {
10514     boolean complete_replace = TRUE;
10515     boolean can_replace[3][3];
10516     int xx, yy;
10517
10518     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10519     {
10520       boolean is_empty;
10521       boolean is_walkable;
10522       boolean is_diggable;
10523       boolean is_collectible;
10524       boolean is_removable;
10525       boolean is_destructible;
10526       int ex = x + xx - 1;
10527       int ey = y + yy - 1;
10528       int content_element = change->target_content.e[xx][yy];
10529       int e;
10530
10531       can_replace[xx][yy] = TRUE;
10532
10533       if (ex == x && ey == y)   // do not check changing element itself
10534         continue;
10535
10536       if (content_element == EL_EMPTY_SPACE)
10537       {
10538         can_replace[xx][yy] = FALSE;    // do not replace border with space
10539
10540         continue;
10541       }
10542
10543       if (!IN_LEV_FIELD(ex, ey))
10544       {
10545         can_replace[xx][yy] = FALSE;
10546         complete_replace = FALSE;
10547
10548         continue;
10549       }
10550
10551       e = Feld[ex][ey];
10552
10553       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10554         e = MovingOrBlocked2Element(ex, ey);
10555
10556       is_empty = (IS_FREE(ex, ey) ||
10557                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10558
10559       is_walkable     = (is_empty || IS_WALKABLE(e));
10560       is_diggable     = (is_empty || IS_DIGGABLE(e));
10561       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10562       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10563       is_removable    = (is_diggable || is_collectible);
10564
10565       can_replace[xx][yy] =
10566         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10567           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10568           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10569           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10570           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10571           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10572          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10573
10574       if (!can_replace[xx][yy])
10575         complete_replace = FALSE;
10576     }
10577
10578     if (!change->only_if_complete || complete_replace)
10579     {
10580       boolean something_has_changed = FALSE;
10581
10582       if (change->only_if_complete && change->use_random_replace &&
10583           RND(100) < change->random_percentage)
10584         return FALSE;
10585
10586       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10587       {
10588         int ex = x + xx - 1;
10589         int ey = y + yy - 1;
10590         int content_element;
10591
10592         if (can_replace[xx][yy] && (!change->use_random_replace ||
10593                                     RND(100) < change->random_percentage))
10594         {
10595           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10596             RemoveMovingField(ex, ey);
10597
10598           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10599
10600           content_element = change->target_content.e[xx][yy];
10601           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10602                                               ce_value, ce_score);
10603
10604           CreateElementFromChange(ex, ey, target_element);
10605
10606           something_has_changed = TRUE;
10607
10608           // for symmetry reasons, freeze newly created border elements
10609           if (ex != x || ey != y)
10610             Stop[ex][ey] = TRUE;        // no more moving in this frame
10611         }
10612       }
10613
10614       if (something_has_changed)
10615       {
10616         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10617         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10618       }
10619     }
10620   }
10621   else
10622   {
10623     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10624                                         ce_value, ce_score);
10625
10626     if (element == EL_DIAGONAL_GROWING ||
10627         element == EL_DIAGONAL_SHRINKING)
10628     {
10629       target_element = Store[x][y];
10630
10631       Store[x][y] = EL_EMPTY;
10632     }
10633
10634     CreateElementFromChange(x, y, target_element);
10635
10636     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10637     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10638   }
10639
10640   // this uses direct change before indirect change
10641   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10642
10643   return TRUE;
10644 }
10645
10646 static void HandleElementChange(int x, int y, int page)
10647 {
10648   int element = MovingOrBlocked2Element(x, y);
10649   struct ElementInfo *ei = &element_info[element];
10650   struct ElementChangeInfo *change = &ei->change_page[page];
10651   boolean handle_action_before_change = FALSE;
10652
10653 #ifdef DEBUG
10654   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10655       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10656   {
10657     printf("\n\n");
10658     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10659            x, y, element, element_info[element].token_name);
10660     printf("HandleElementChange(): This should never happen!\n");
10661     printf("\n\n");
10662   }
10663 #endif
10664
10665   // this can happen with classic bombs on walkable, changing elements
10666   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10667   {
10668     return;
10669   }
10670
10671   if (ChangeDelay[x][y] == 0)           // initialize element change
10672   {
10673     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10674
10675     if (change->can_change)
10676     {
10677       // !!! not clear why graphic animation should be reset at all here !!!
10678       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10679       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10680
10681       /*
10682         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10683
10684         When using an animation frame delay of 1 (this only happens with
10685         "sp_zonk.moving.left/right" in the classic graphics), the default
10686         (non-moving) animation shows wrong animation frames (while the
10687         moving animation, like "sp_zonk.moving.left/right", is correct,
10688         so this graphical bug never shows up with the classic graphics).
10689         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10690         be drawn instead of the correct frames 0,1,2,3. This is caused by
10691         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10692         an element change: First when the change delay ("ChangeDelay[][]")
10693         counter has reached zero after decrementing, then a second time in
10694         the next frame (after "GfxFrame[][]" was already incremented) when
10695         "ChangeDelay[][]" is reset to the initial delay value again.
10696
10697         This causes frame 0 to be drawn twice, while the last frame won't
10698         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10699
10700         As some animations may already be cleverly designed around this bug
10701         (at least the "Snake Bite" snake tail animation does this), it cannot
10702         simply be fixed here without breaking such existing animations.
10703         Unfortunately, it cannot easily be detected if a graphics set was
10704         designed "before" or "after" the bug was fixed. As a workaround,
10705         a new graphics set option "game.graphics_engine_version" was added
10706         to be able to specify the game's major release version for which the
10707         graphics set was designed, which can then be used to decide if the
10708         bugfix should be used (version 4 and above) or not (version 3 or
10709         below, or if no version was specified at all, as with old sets).
10710
10711         (The wrong/fixed animation frames can be tested with the test level set
10712         "test_gfxframe" and level "000", which contains a specially prepared
10713         custom element at level position (x/y) == (11/9) which uses the zonk
10714         animation mentioned above. Using "game.graphics_engine_version: 4"
10715         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10716         This can also be seen from the debug output for this test element.)
10717       */
10718
10719       // when a custom element is about to change (for example by change delay),
10720       // do not reset graphic animation when the custom element is moving
10721       if (game.graphics_engine_version < 4 &&
10722           !IS_MOVING(x, y))
10723       {
10724         ResetGfxAnimation(x, y);
10725         ResetRandomAnimationValue(x, y);
10726       }
10727
10728       if (change->pre_change_function)
10729         change->pre_change_function(x, y);
10730     }
10731   }
10732
10733   ChangeDelay[x][y]--;
10734
10735   if (ChangeDelay[x][y] != 0)           // continue element change
10736   {
10737     if (change->can_change)
10738     {
10739       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10740
10741       if (IS_ANIMATED(graphic))
10742         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10743
10744       if (change->change_function)
10745         change->change_function(x, y);
10746     }
10747   }
10748   else                                  // finish element change
10749   {
10750     if (ChangePage[x][y] != -1)         // remember page from delayed change
10751     {
10752       page = ChangePage[x][y];
10753       ChangePage[x][y] = -1;
10754
10755       change = &ei->change_page[page];
10756     }
10757
10758     if (IS_MOVING(x, y))                // never change a running system ;-)
10759     {
10760       ChangeDelay[x][y] = 1;            // try change after next move step
10761       ChangePage[x][y] = page;          // remember page to use for change
10762
10763       return;
10764     }
10765
10766     // special case: set new level random seed before changing element
10767     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10768       handle_action_before_change = TRUE;
10769
10770     if (change->has_action && handle_action_before_change)
10771       ExecuteCustomElementAction(x, y, element, page);
10772
10773     if (change->can_change)
10774     {
10775       if (ChangeElement(x, y, element, page))
10776       {
10777         if (change->post_change_function)
10778           change->post_change_function(x, y);
10779       }
10780     }
10781
10782     if (change->has_action && !handle_action_before_change)
10783       ExecuteCustomElementAction(x, y, element, page);
10784   }
10785 }
10786
10787 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10788                                               int trigger_element,
10789                                               int trigger_event,
10790                                               int trigger_player,
10791                                               int trigger_side,
10792                                               int trigger_page)
10793 {
10794   boolean change_done_any = FALSE;
10795   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10796   int i;
10797
10798   if (!(trigger_events[trigger_element][trigger_event]))
10799     return FALSE;
10800
10801   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10802
10803   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10804   {
10805     int element = EL_CUSTOM_START + i;
10806     boolean change_done = FALSE;
10807     int p;
10808
10809     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10810         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10811       continue;
10812
10813     for (p = 0; p < element_info[element].num_change_pages; p++)
10814     {
10815       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10816
10817       if (change->can_change_or_has_action &&
10818           change->has_event[trigger_event] &&
10819           change->trigger_side & trigger_side &&
10820           change->trigger_player & trigger_player &&
10821           change->trigger_page & trigger_page_bits &&
10822           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10823       {
10824         change->actual_trigger_element = trigger_element;
10825         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10826         change->actual_trigger_player_bits = trigger_player;
10827         change->actual_trigger_side = trigger_side;
10828         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10829         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10830
10831         if ((change->can_change && !change_done) || change->has_action)
10832         {
10833           int x, y;
10834
10835           SCAN_PLAYFIELD(x, y)
10836           {
10837             if (Feld[x][y] == element)
10838             {
10839               if (change->can_change && !change_done)
10840               {
10841                 // if element already changed in this frame, not only prevent
10842                 // another element change (checked in ChangeElement()), but
10843                 // also prevent additional element actions for this element
10844
10845                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10846                     !level.use_action_after_change_bug)
10847                   continue;
10848
10849                 ChangeDelay[x][y] = 1;
10850                 ChangeEvent[x][y] = trigger_event;
10851
10852                 HandleElementChange(x, y, p);
10853               }
10854               else if (change->has_action)
10855               {
10856                 // if element already changed in this frame, not only prevent
10857                 // another element change (checked in ChangeElement()), but
10858                 // also prevent additional element actions for this element
10859
10860                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10861                     !level.use_action_after_change_bug)
10862                   continue;
10863
10864                 ExecuteCustomElementAction(x, y, element, p);
10865                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10866               }
10867             }
10868           }
10869
10870           if (change->can_change)
10871           {
10872             change_done = TRUE;
10873             change_done_any = TRUE;
10874           }
10875         }
10876       }
10877     }
10878   }
10879
10880   RECURSION_LOOP_DETECTION_END();
10881
10882   return change_done_any;
10883 }
10884
10885 static boolean CheckElementChangeExt(int x, int y,
10886                                      int element,
10887                                      int trigger_element,
10888                                      int trigger_event,
10889                                      int trigger_player,
10890                                      int trigger_side)
10891 {
10892   boolean change_done = FALSE;
10893   int p;
10894
10895   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10896       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10897     return FALSE;
10898
10899   if (Feld[x][y] == EL_BLOCKED)
10900   {
10901     Blocked2Moving(x, y, &x, &y);
10902     element = Feld[x][y];
10903   }
10904
10905   // check if element has already changed or is about to change after moving
10906   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10907        Feld[x][y] != element) ||
10908
10909       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10910        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10911         ChangePage[x][y] != -1)))
10912     return FALSE;
10913
10914   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10915
10916   for (p = 0; p < element_info[element].num_change_pages; p++)
10917   {
10918     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10919
10920     /* check trigger element for all events where the element that is checked
10921        for changing interacts with a directly adjacent element -- this is
10922        different to element changes that affect other elements to change on the
10923        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10924     boolean check_trigger_element =
10925       (trigger_event == CE_TOUCHING_X ||
10926        trigger_event == CE_HITTING_X ||
10927        trigger_event == CE_HIT_BY_X ||
10928        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10929
10930     if (change->can_change_or_has_action &&
10931         change->has_event[trigger_event] &&
10932         change->trigger_side & trigger_side &&
10933         change->trigger_player & trigger_player &&
10934         (!check_trigger_element ||
10935          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10936     {
10937       change->actual_trigger_element = trigger_element;
10938       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10939       change->actual_trigger_player_bits = trigger_player;
10940       change->actual_trigger_side = trigger_side;
10941       change->actual_trigger_ce_value = CustomValue[x][y];
10942       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10943
10944       // special case: trigger element not at (x,y) position for some events
10945       if (check_trigger_element)
10946       {
10947         static struct
10948         {
10949           int dx, dy;
10950         } move_xy[] =
10951           {
10952             {  0,  0 },
10953             { -1,  0 },
10954             { +1,  0 },
10955             {  0,  0 },
10956             {  0, -1 },
10957             {  0,  0 }, { 0, 0 }, { 0, 0 },
10958             {  0, +1 }
10959           };
10960
10961         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10962         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10963
10964         change->actual_trigger_ce_value = CustomValue[xx][yy];
10965         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10966       }
10967
10968       if (change->can_change && !change_done)
10969       {
10970         ChangeDelay[x][y] = 1;
10971         ChangeEvent[x][y] = trigger_event;
10972
10973         HandleElementChange(x, y, p);
10974
10975         change_done = TRUE;
10976       }
10977       else if (change->has_action)
10978       {
10979         ExecuteCustomElementAction(x, y, element, p);
10980         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10981       }
10982     }
10983   }
10984
10985   RECURSION_LOOP_DETECTION_END();
10986
10987   return change_done;
10988 }
10989
10990 static void PlayPlayerSound(struct PlayerInfo *player)
10991 {
10992   int jx = player->jx, jy = player->jy;
10993   int sound_element = player->artwork_element;
10994   int last_action = player->last_action_waiting;
10995   int action = player->action_waiting;
10996
10997   if (player->is_waiting)
10998   {
10999     if (action != last_action)
11000       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11001     else
11002       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11003   }
11004   else
11005   {
11006     if (action != last_action)
11007       StopSound(element_info[sound_element].sound[last_action]);
11008
11009     if (last_action == ACTION_SLEEPING)
11010       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11011   }
11012 }
11013
11014 static void PlayAllPlayersSound(void)
11015 {
11016   int i;
11017
11018   for (i = 0; i < MAX_PLAYERS; i++)
11019     if (stored_player[i].active)
11020       PlayPlayerSound(&stored_player[i]);
11021 }
11022
11023 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11024 {
11025   boolean last_waiting = player->is_waiting;
11026   int move_dir = player->MovDir;
11027
11028   player->dir_waiting = move_dir;
11029   player->last_action_waiting = player->action_waiting;
11030
11031   if (is_waiting)
11032   {
11033     if (!last_waiting)          // not waiting -> waiting
11034     {
11035       player->is_waiting = TRUE;
11036
11037       player->frame_counter_bored =
11038         FrameCounter +
11039         game.player_boring_delay_fixed +
11040         GetSimpleRandom(game.player_boring_delay_random);
11041       player->frame_counter_sleeping =
11042         FrameCounter +
11043         game.player_sleeping_delay_fixed +
11044         GetSimpleRandom(game.player_sleeping_delay_random);
11045
11046       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11047     }
11048
11049     if (game.player_sleeping_delay_fixed +
11050         game.player_sleeping_delay_random > 0 &&
11051         player->anim_delay_counter == 0 &&
11052         player->post_delay_counter == 0 &&
11053         FrameCounter >= player->frame_counter_sleeping)
11054       player->is_sleeping = TRUE;
11055     else if (game.player_boring_delay_fixed +
11056              game.player_boring_delay_random > 0 &&
11057              FrameCounter >= player->frame_counter_bored)
11058       player->is_bored = TRUE;
11059
11060     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11061                               player->is_bored ? ACTION_BORING :
11062                               ACTION_WAITING);
11063
11064     if (player->is_sleeping && player->use_murphy)
11065     {
11066       // special case for sleeping Murphy when leaning against non-free tile
11067
11068       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11069           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11070            !IS_MOVING(player->jx - 1, player->jy)))
11071         move_dir = MV_LEFT;
11072       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11073                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11074                 !IS_MOVING(player->jx + 1, player->jy)))
11075         move_dir = MV_RIGHT;
11076       else
11077         player->is_sleeping = FALSE;
11078
11079       player->dir_waiting = move_dir;
11080     }
11081
11082     if (player->is_sleeping)
11083     {
11084       if (player->num_special_action_sleeping > 0)
11085       {
11086         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11087         {
11088           int last_special_action = player->special_action_sleeping;
11089           int num_special_action = player->num_special_action_sleeping;
11090           int special_action =
11091             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11092              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11093              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11094              last_special_action + 1 : ACTION_SLEEPING);
11095           int special_graphic =
11096             el_act_dir2img(player->artwork_element, special_action, move_dir);
11097
11098           player->anim_delay_counter =
11099             graphic_info[special_graphic].anim_delay_fixed +
11100             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11101           player->post_delay_counter =
11102             graphic_info[special_graphic].post_delay_fixed +
11103             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11104
11105           player->special_action_sleeping = special_action;
11106         }
11107
11108         if (player->anim_delay_counter > 0)
11109         {
11110           player->action_waiting = player->special_action_sleeping;
11111           player->anim_delay_counter--;
11112         }
11113         else if (player->post_delay_counter > 0)
11114         {
11115           player->post_delay_counter--;
11116         }
11117       }
11118     }
11119     else if (player->is_bored)
11120     {
11121       if (player->num_special_action_bored > 0)
11122       {
11123         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11124         {
11125           int special_action =
11126             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11127           int special_graphic =
11128             el_act_dir2img(player->artwork_element, special_action, move_dir);
11129
11130           player->anim_delay_counter =
11131             graphic_info[special_graphic].anim_delay_fixed +
11132             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11133           player->post_delay_counter =
11134             graphic_info[special_graphic].post_delay_fixed +
11135             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11136
11137           player->special_action_bored = special_action;
11138         }
11139
11140         if (player->anim_delay_counter > 0)
11141         {
11142           player->action_waiting = player->special_action_bored;
11143           player->anim_delay_counter--;
11144         }
11145         else if (player->post_delay_counter > 0)
11146         {
11147           player->post_delay_counter--;
11148         }
11149       }
11150     }
11151   }
11152   else if (last_waiting)        // waiting -> not waiting
11153   {
11154     player->is_waiting = FALSE;
11155     player->is_bored = FALSE;
11156     player->is_sleeping = FALSE;
11157
11158     player->frame_counter_bored = -1;
11159     player->frame_counter_sleeping = -1;
11160
11161     player->anim_delay_counter = 0;
11162     player->post_delay_counter = 0;
11163
11164     player->dir_waiting = player->MovDir;
11165     player->action_waiting = ACTION_DEFAULT;
11166
11167     player->special_action_bored = ACTION_DEFAULT;
11168     player->special_action_sleeping = ACTION_DEFAULT;
11169   }
11170 }
11171
11172 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11173 {
11174   if ((!player->is_moving  && player->was_moving) ||
11175       (player->MovPos == 0 && player->was_moving) ||
11176       (player->is_snapping && !player->was_snapping) ||
11177       (player->is_dropping && !player->was_dropping))
11178   {
11179     if (!CheckSaveEngineSnapshotToList())
11180       return;
11181
11182     player->was_moving = FALSE;
11183     player->was_snapping = TRUE;
11184     player->was_dropping = TRUE;
11185   }
11186   else
11187   {
11188     if (player->is_moving)
11189       player->was_moving = TRUE;
11190
11191     if (!player->is_snapping)
11192       player->was_snapping = FALSE;
11193
11194     if (!player->is_dropping)
11195       player->was_dropping = FALSE;
11196   }
11197 }
11198
11199 static void CheckSingleStepMode(struct PlayerInfo *player)
11200 {
11201   if (tape.single_step && tape.recording && !tape.pausing)
11202   {
11203     /* as it is called "single step mode", just return to pause mode when the
11204        player stopped moving after one tile (or never starts moving at all) */
11205     if (!player->is_moving &&
11206         !player->is_pushing &&
11207         !player->is_dropping_pressed)
11208       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11209   }
11210
11211   CheckSaveEngineSnapshot(player);
11212 }
11213
11214 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11215 {
11216   int left      = player_action & JOY_LEFT;
11217   int right     = player_action & JOY_RIGHT;
11218   int up        = player_action & JOY_UP;
11219   int down      = player_action & JOY_DOWN;
11220   int button1   = player_action & JOY_BUTTON_1;
11221   int button2   = player_action & JOY_BUTTON_2;
11222   int dx        = (left ? -1 : right ? 1 : 0);
11223   int dy        = (up   ? -1 : down  ? 1 : 0);
11224
11225   if (!player->active || tape.pausing)
11226     return 0;
11227
11228   if (player_action)
11229   {
11230     if (button1)
11231       SnapField(player, dx, dy);
11232     else
11233     {
11234       if (button2)
11235         DropElement(player);
11236
11237       MovePlayer(player, dx, dy);
11238     }
11239
11240     CheckSingleStepMode(player);
11241
11242     SetPlayerWaiting(player, FALSE);
11243
11244     return player_action;
11245   }
11246   else
11247   {
11248     // no actions for this player (no input at player's configured device)
11249
11250     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11251     SnapField(player, 0, 0);
11252     CheckGravityMovementWhenNotMoving(player);
11253
11254     if (player->MovPos == 0)
11255       SetPlayerWaiting(player, TRUE);
11256
11257     if (player->MovPos == 0)    // needed for tape.playing
11258       player->is_moving = FALSE;
11259
11260     player->is_dropping = FALSE;
11261     player->is_dropping_pressed = FALSE;
11262     player->drop_pressed_delay = 0;
11263
11264     CheckSingleStepMode(player);
11265
11266     return 0;
11267   }
11268 }
11269
11270 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11271                                          byte *tape_action)
11272 {
11273   if (!tape.use_mouse_actions)
11274     return;
11275
11276   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11277   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11278   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11279 }
11280
11281 static void SetTapeActionFromMouseAction(byte *tape_action,
11282                                          struct MouseActionInfo *mouse_action)
11283 {
11284   if (!tape.use_mouse_actions)
11285     return;
11286
11287   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11288   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11289   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11290 }
11291
11292 static void CheckLevelSolved(void)
11293 {
11294   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11295   {
11296     if (game_em.level_solved &&
11297         !game_em.game_over)                             // game won
11298     {
11299       LevelSolved();
11300
11301       game_em.game_over = TRUE;
11302
11303       game.all_players_gone = TRUE;
11304     }
11305
11306     if (game_em.game_over)                              // game lost
11307       game.all_players_gone = TRUE;
11308   }
11309   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11310   {
11311     if (game_sp.level_solved &&
11312         !game_sp.game_over)                             // game won
11313     {
11314       LevelSolved();
11315
11316       game_sp.game_over = TRUE;
11317
11318       game.all_players_gone = TRUE;
11319     }
11320
11321     if (game_sp.game_over)                              // game lost
11322       game.all_players_gone = TRUE;
11323   }
11324   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11325   {
11326     if (game_mm.level_solved &&
11327         !game_mm.game_over)                             // game won
11328     {
11329       LevelSolved();
11330
11331       game_mm.game_over = TRUE;
11332
11333       game.all_players_gone = TRUE;
11334     }
11335
11336     if (game_mm.game_over)                              // game lost
11337       game.all_players_gone = TRUE;
11338   }
11339 }
11340
11341 static void CheckLevelTime(void)
11342 {
11343   int i;
11344
11345   if (TimeFrames >= FRAMES_PER_SECOND)
11346   {
11347     TimeFrames = 0;
11348     TapeTime++;
11349
11350     for (i = 0; i < MAX_PLAYERS; i++)
11351     {
11352       struct PlayerInfo *player = &stored_player[i];
11353
11354       if (SHIELD_ON(player))
11355       {
11356         player->shield_normal_time_left--;
11357
11358         if (player->shield_deadly_time_left > 0)
11359           player->shield_deadly_time_left--;
11360       }
11361     }
11362
11363     if (!game.LevelSolved && !level.use_step_counter)
11364     {
11365       TimePlayed++;
11366
11367       if (TimeLeft > 0)
11368       {
11369         TimeLeft--;
11370
11371         if (TimeLeft <= 10 && setup.time_limit)
11372           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11373
11374         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11375            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11376
11377         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11378
11379         if (!TimeLeft && setup.time_limit)
11380         {
11381           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11382             game_em.lev->killed_out_of_time = TRUE;
11383           else
11384             for (i = 0; i < MAX_PLAYERS; i++)
11385               KillPlayer(&stored_player[i]);
11386         }
11387       }
11388       else if (game.no_time_limit && !game.all_players_gone)
11389       {
11390         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11391       }
11392
11393       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11394     }
11395
11396     if (tape.recording || tape.playing)
11397       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11398   }
11399
11400   if (tape.recording || tape.playing)
11401     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11402
11403   UpdateAndDisplayGameControlValues();
11404 }
11405
11406 void AdvanceFrameAndPlayerCounters(int player_nr)
11407 {
11408   int i;
11409
11410   // advance frame counters (global frame counter and time frame counter)
11411   FrameCounter++;
11412   TimeFrames++;
11413
11414   // advance player counters (counters for move delay, move animation etc.)
11415   for (i = 0; i < MAX_PLAYERS; i++)
11416   {
11417     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11418     int move_delay_value = stored_player[i].move_delay_value;
11419     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11420
11421     if (!advance_player_counters)       // not all players may be affected
11422       continue;
11423
11424     if (move_frames == 0)       // less than one move per game frame
11425     {
11426       int stepsize = TILEX / move_delay_value;
11427       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11428       int count = (stored_player[i].is_moving ?
11429                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11430
11431       if (count % delay == 0)
11432         move_frames = 1;
11433     }
11434
11435     stored_player[i].Frame += move_frames;
11436
11437     if (stored_player[i].MovPos != 0)
11438       stored_player[i].StepFrame += move_frames;
11439
11440     if (stored_player[i].move_delay > 0)
11441       stored_player[i].move_delay--;
11442
11443     // due to bugs in previous versions, counter must count up, not down
11444     if (stored_player[i].push_delay != -1)
11445       stored_player[i].push_delay++;
11446
11447     if (stored_player[i].drop_delay > 0)
11448       stored_player[i].drop_delay--;
11449
11450     if (stored_player[i].is_dropping_pressed)
11451       stored_player[i].drop_pressed_delay++;
11452   }
11453 }
11454
11455 void StartGameActions(boolean init_network_game, boolean record_tape,
11456                       int random_seed)
11457 {
11458   unsigned int new_random_seed = InitRND(random_seed);
11459
11460   if (record_tape)
11461     TapeStartRecording(new_random_seed);
11462
11463   if (init_network_game)
11464   {
11465     SendToServer_LevelFile();
11466     SendToServer_StartPlaying();
11467
11468     return;
11469   }
11470
11471   InitGame();
11472 }
11473
11474 static void GameActionsExt(void)
11475 {
11476 #if 0
11477   static unsigned int game_frame_delay = 0;
11478 #endif
11479   unsigned int game_frame_delay_value;
11480   byte *recorded_player_action;
11481   byte summarized_player_action = 0;
11482   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11483   int i;
11484
11485   // detect endless loops, caused by custom element programming
11486   if (recursion_loop_detected && recursion_loop_depth == 0)
11487   {
11488     char *message = getStringCat3("Internal Error! Element ",
11489                                   EL_NAME(recursion_loop_element),
11490                                   " caused endless loop! Quit the game?");
11491
11492     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11493           EL_NAME(recursion_loop_element));
11494
11495     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11496
11497     recursion_loop_detected = FALSE;    // if game should be continued
11498
11499     free(message);
11500
11501     return;
11502   }
11503
11504   if (game.restart_level)
11505     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11506
11507   CheckLevelSolved();
11508
11509   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11510     GameWon();
11511
11512   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11513     TapeStop();
11514
11515   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11516     return;
11517
11518   game_frame_delay_value =
11519     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11520
11521   if (tape.playing && tape.warp_forward && !tape.pausing)
11522     game_frame_delay_value = 0;
11523
11524   SetVideoFrameDelay(game_frame_delay_value);
11525
11526   // (de)activate virtual buttons depending on current game status
11527   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11528   {
11529     if (game.all_players_gone)  // if no players there to be controlled anymore
11530       SetOverlayActive(FALSE);
11531     else if (!tape.playing)     // if game continues after tape stopped playing
11532       SetOverlayActive(TRUE);
11533   }
11534
11535 #if 0
11536 #if 0
11537   // ---------- main game synchronization point ----------
11538
11539   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11540
11541   printf("::: skip == %d\n", skip);
11542
11543 #else
11544   // ---------- main game synchronization point ----------
11545
11546   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11547 #endif
11548 #endif
11549
11550   if (network_playing && !network_player_action_received)
11551   {
11552     // try to get network player actions in time
11553
11554     // last chance to get network player actions without main loop delay
11555     HandleNetworking();
11556
11557     // game was quit by network peer
11558     if (game_status != GAME_MODE_PLAYING)
11559       return;
11560
11561     // check if network player actions still missing and game still running
11562     if (!network_player_action_received && !checkGameEnded())
11563       return;           // failed to get network player actions in time
11564
11565     // do not yet reset "network_player_action_received" (for tape.pausing)
11566   }
11567
11568   if (tape.pausing)
11569     return;
11570
11571   // at this point we know that we really continue executing the game
11572
11573   network_player_action_received = FALSE;
11574
11575   // when playing tape, read previously recorded player input from tape data
11576   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11577
11578   local_player->effective_mouse_action = local_player->mouse_action;
11579
11580   if (recorded_player_action != NULL)
11581     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11582                                  recorded_player_action);
11583
11584   // TapePlayAction() may return NULL when toggling to "pause before death"
11585   if (tape.pausing)
11586     return;
11587
11588   if (tape.set_centered_player)
11589   {
11590     game.centered_player_nr_next = tape.centered_player_nr_next;
11591     game.set_centered_player = TRUE;
11592   }
11593
11594   for (i = 0; i < MAX_PLAYERS; i++)
11595   {
11596     summarized_player_action |= stored_player[i].action;
11597
11598     if (!network_playing && (game.team_mode || tape.playing))
11599       stored_player[i].effective_action = stored_player[i].action;
11600   }
11601
11602   if (network_playing && !checkGameEnded())
11603     SendToServer_MovePlayer(summarized_player_action);
11604
11605   // summarize all actions at local players mapped input device position
11606   // (this allows using different input devices in single player mode)
11607   if (!network.enabled && !game.team_mode)
11608     stored_player[map_player_action[local_player->index_nr]].effective_action =
11609       summarized_player_action;
11610
11611   // summarize all actions at centered player in local team mode
11612   if (tape.recording &&
11613       setup.team_mode && !network.enabled &&
11614       setup.input_on_focus &&
11615       game.centered_player_nr != -1)
11616   {
11617     for (i = 0; i < MAX_PLAYERS; i++)
11618       stored_player[map_player_action[i]].effective_action =
11619         (i == game.centered_player_nr ? summarized_player_action : 0);
11620   }
11621
11622   if (recorded_player_action != NULL)
11623     for (i = 0; i < MAX_PLAYERS; i++)
11624       stored_player[i].effective_action = recorded_player_action[i];
11625
11626   for (i = 0; i < MAX_PLAYERS; i++)
11627   {
11628     tape_action[i] = stored_player[i].effective_action;
11629
11630     /* (this may happen in the RND game engine if a player was not present on
11631        the playfield on level start, but appeared later from a custom element */
11632     if (setup.team_mode &&
11633         tape.recording &&
11634         tape_action[i] &&
11635         !tape.player_participates[i])
11636       tape.player_participates[i] = TRUE;
11637   }
11638
11639   SetTapeActionFromMouseAction(tape_action,
11640                                &local_player->effective_mouse_action);
11641
11642   // only record actions from input devices, but not programmed actions
11643   if (tape.recording)
11644     TapeRecordAction(tape_action);
11645
11646   // remember if game was played (especially after tape stopped playing)
11647   if (!tape.playing && summarized_player_action)
11648     game.GamePlayed = TRUE;
11649
11650 #if USE_NEW_PLAYER_ASSIGNMENTS
11651   // !!! also map player actions in single player mode !!!
11652   // if (game.team_mode)
11653   if (1)
11654   {
11655     byte mapped_action[MAX_PLAYERS];
11656
11657 #if DEBUG_PLAYER_ACTIONS
11658     printf(":::");
11659     for (i = 0; i < MAX_PLAYERS; i++)
11660       printf(" %d, ", stored_player[i].effective_action);
11661 #endif
11662
11663     for (i = 0; i < MAX_PLAYERS; i++)
11664       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11665
11666     for (i = 0; i < MAX_PLAYERS; i++)
11667       stored_player[i].effective_action = mapped_action[i];
11668
11669 #if DEBUG_PLAYER_ACTIONS
11670     printf(" =>");
11671     for (i = 0; i < MAX_PLAYERS; i++)
11672       printf(" %d, ", stored_player[i].effective_action);
11673     printf("\n");
11674 #endif
11675   }
11676 #if DEBUG_PLAYER_ACTIONS
11677   else
11678   {
11679     printf(":::");
11680     for (i = 0; i < MAX_PLAYERS; i++)
11681       printf(" %d, ", stored_player[i].effective_action);
11682     printf("\n");
11683   }
11684 #endif
11685 #endif
11686
11687   for (i = 0; i < MAX_PLAYERS; i++)
11688   {
11689     // allow engine snapshot in case of changed movement attempt
11690     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11691         (stored_player[i].effective_action & KEY_MOTION))
11692       game.snapshot.changed_action = TRUE;
11693
11694     // allow engine snapshot in case of snapping/dropping attempt
11695     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11696         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11697       game.snapshot.changed_action = TRUE;
11698
11699     game.snapshot.last_action[i] = stored_player[i].effective_action;
11700   }
11701
11702   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11703   {
11704     GameActions_EM_Main();
11705   }
11706   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11707   {
11708     GameActions_SP_Main();
11709   }
11710   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11711   {
11712     GameActions_MM_Main();
11713   }
11714   else
11715   {
11716     GameActions_RND_Main();
11717   }
11718
11719   BlitScreenToBitmap(backbuffer);
11720
11721   CheckLevelSolved();
11722   CheckLevelTime();
11723
11724   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11725
11726   if (global.show_frames_per_second)
11727   {
11728     static unsigned int fps_counter = 0;
11729     static int fps_frames = 0;
11730     unsigned int fps_delay_ms = Counter() - fps_counter;
11731
11732     fps_frames++;
11733
11734     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11735     {
11736       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11737
11738       fps_frames = 0;
11739       fps_counter = Counter();
11740
11741       // always draw FPS to screen after FPS value was updated
11742       redraw_mask |= REDRAW_FPS;
11743     }
11744
11745     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11746     if (GetDrawDeactivationMask() == REDRAW_NONE)
11747       redraw_mask |= REDRAW_FPS;
11748   }
11749 }
11750
11751 static void GameActions_CheckSaveEngineSnapshot(void)
11752 {
11753   if (!game.snapshot.save_snapshot)
11754     return;
11755
11756   // clear flag for saving snapshot _before_ saving snapshot
11757   game.snapshot.save_snapshot = FALSE;
11758
11759   SaveEngineSnapshotToList();
11760 }
11761
11762 void GameActions(void)
11763 {
11764   GameActionsExt();
11765
11766   GameActions_CheckSaveEngineSnapshot();
11767 }
11768
11769 void GameActions_EM_Main(void)
11770 {
11771   byte effective_action[MAX_PLAYERS];
11772   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11773   int i;
11774
11775   for (i = 0; i < MAX_PLAYERS; i++)
11776     effective_action[i] = stored_player[i].effective_action;
11777
11778   GameActions_EM(effective_action, warp_mode);
11779 }
11780
11781 void GameActions_SP_Main(void)
11782 {
11783   byte effective_action[MAX_PLAYERS];
11784   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11785   int i;
11786
11787   for (i = 0; i < MAX_PLAYERS; i++)
11788     effective_action[i] = stored_player[i].effective_action;
11789
11790   GameActions_SP(effective_action, warp_mode);
11791
11792   for (i = 0; i < MAX_PLAYERS; i++)
11793   {
11794     if (stored_player[i].force_dropping)
11795       stored_player[i].action |= KEY_BUTTON_DROP;
11796
11797     stored_player[i].force_dropping = FALSE;
11798   }
11799 }
11800
11801 void GameActions_MM_Main(void)
11802 {
11803   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11804
11805   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11806 }
11807
11808 void GameActions_RND_Main(void)
11809 {
11810   GameActions_RND();
11811 }
11812
11813 void GameActions_RND(void)
11814 {
11815   static struct MouseActionInfo mouse_action_last = { 0 };
11816   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11817   int magic_wall_x = 0, magic_wall_y = 0;
11818   int i, x, y, element, graphic, last_gfx_frame;
11819
11820   InitPlayfieldScanModeVars();
11821
11822   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11823   {
11824     SCAN_PLAYFIELD(x, y)
11825     {
11826       ChangeCount[x][y] = 0;
11827       ChangeEvent[x][y] = -1;
11828     }
11829   }
11830
11831   if (game.set_centered_player)
11832   {
11833     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11834
11835     // switching to "all players" only possible if all players fit to screen
11836     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11837     {
11838       game.centered_player_nr_next = game.centered_player_nr;
11839       game.set_centered_player = FALSE;
11840     }
11841
11842     // do not switch focus to non-existing (or non-active) player
11843     if (game.centered_player_nr_next >= 0 &&
11844         !stored_player[game.centered_player_nr_next].active)
11845     {
11846       game.centered_player_nr_next = game.centered_player_nr;
11847       game.set_centered_player = FALSE;
11848     }
11849   }
11850
11851   if (game.set_centered_player &&
11852       ScreenMovPos == 0)        // screen currently aligned at tile position
11853   {
11854     int sx, sy;
11855
11856     if (game.centered_player_nr_next == -1)
11857     {
11858       setScreenCenteredToAllPlayers(&sx, &sy);
11859     }
11860     else
11861     {
11862       sx = stored_player[game.centered_player_nr_next].jx;
11863       sy = stored_player[game.centered_player_nr_next].jy;
11864     }
11865
11866     game.centered_player_nr = game.centered_player_nr_next;
11867     game.set_centered_player = FALSE;
11868
11869     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11870     DrawGameDoorValues();
11871   }
11872
11873   for (i = 0; i < MAX_PLAYERS; i++)
11874   {
11875     int actual_player_action = stored_player[i].effective_action;
11876
11877 #if 1
11878     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11879        - rnd_equinox_tetrachloride 048
11880        - rnd_equinox_tetrachloride_ii 096
11881        - rnd_emanuel_schmieg 002
11882        - doctor_sloan_ww 001, 020
11883     */
11884     if (stored_player[i].MovPos == 0)
11885       CheckGravityMovement(&stored_player[i]);
11886 #endif
11887
11888     // overwrite programmed action with tape action
11889     if (stored_player[i].programmed_action)
11890       actual_player_action = stored_player[i].programmed_action;
11891
11892     PlayerActions(&stored_player[i], actual_player_action);
11893
11894     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11895   }
11896
11897   ScrollScreen(NULL, SCROLL_GO_ON);
11898
11899   /* for backwards compatibility, the following code emulates a fixed bug that
11900      occured when pushing elements (causing elements that just made their last
11901      pushing step to already (if possible) make their first falling step in the
11902      same game frame, which is bad); this code is also needed to use the famous
11903      "spring push bug" which is used in older levels and might be wanted to be
11904      used also in newer levels, but in this case the buggy pushing code is only
11905      affecting the "spring" element and no other elements */
11906
11907   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11908   {
11909     for (i = 0; i < MAX_PLAYERS; i++)
11910     {
11911       struct PlayerInfo *player = &stored_player[i];
11912       int x = player->jx;
11913       int y = player->jy;
11914
11915       if (player->active && player->is_pushing && player->is_moving &&
11916           IS_MOVING(x, y) &&
11917           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11918            Feld[x][y] == EL_SPRING))
11919       {
11920         ContinueMoving(x, y);
11921
11922         // continue moving after pushing (this is actually a bug)
11923         if (!IS_MOVING(x, y))
11924           Stop[x][y] = FALSE;
11925       }
11926     }
11927   }
11928
11929   SCAN_PLAYFIELD(x, y)
11930   {
11931     Last[x][y] = Feld[x][y];
11932
11933     ChangeCount[x][y] = 0;
11934     ChangeEvent[x][y] = -1;
11935
11936     // this must be handled before main playfield loop
11937     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11938     {
11939       MovDelay[x][y]--;
11940       if (MovDelay[x][y] <= 0)
11941         RemoveField(x, y);
11942     }
11943
11944     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11945     {
11946       MovDelay[x][y]--;
11947       if (MovDelay[x][y] <= 0)
11948       {
11949         RemoveField(x, y);
11950         TEST_DrawLevelField(x, y);
11951
11952         TestIfElementTouchesCustomElement(x, y);        // for empty space
11953       }
11954     }
11955
11956 #if DEBUG
11957     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11958     {
11959       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11960       printf("GameActions(): This should never happen!\n");
11961
11962       ChangePage[x][y] = -1;
11963     }
11964 #endif
11965
11966     Stop[x][y] = FALSE;
11967     if (WasJustMoving[x][y] > 0)
11968       WasJustMoving[x][y]--;
11969     if (WasJustFalling[x][y] > 0)
11970       WasJustFalling[x][y]--;
11971     if (CheckCollision[x][y] > 0)
11972       CheckCollision[x][y]--;
11973     if (CheckImpact[x][y] > 0)
11974       CheckImpact[x][y]--;
11975
11976     GfxFrame[x][y]++;
11977
11978     /* reset finished pushing action (not done in ContinueMoving() to allow
11979        continuous pushing animation for elements with zero push delay) */
11980     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11981     {
11982       ResetGfxAnimation(x, y);
11983       TEST_DrawLevelField(x, y);
11984     }
11985
11986 #if DEBUG
11987     if (IS_BLOCKED(x, y))
11988     {
11989       int oldx, oldy;
11990
11991       Blocked2Moving(x, y, &oldx, &oldy);
11992       if (!IS_MOVING(oldx, oldy))
11993       {
11994         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11995         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11996         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11997         printf("GameActions(): This should never happen!\n");
11998       }
11999     }
12000 #endif
12001   }
12002
12003   if (mouse_action.button)
12004   {
12005     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12006
12007     x = mouse_action.lx;
12008     y = mouse_action.ly;
12009     element = Feld[x][y];
12010
12011     if (new_button)
12012     {
12013       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12014       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12015     }
12016
12017     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12018     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12019   }
12020
12021   SCAN_PLAYFIELD(x, y)
12022   {
12023     element = Feld[x][y];
12024     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12025     last_gfx_frame = GfxFrame[x][y];
12026
12027     ResetGfxFrame(x, y);
12028
12029     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12030       DrawLevelGraphicAnimation(x, y, graphic);
12031
12032     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12033         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12034       ResetRandomAnimationValue(x, y);
12035
12036     SetRandomAnimationValue(x, y);
12037
12038     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12039
12040     if (IS_INACTIVE(element))
12041     {
12042       if (IS_ANIMATED(graphic))
12043         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12044
12045       continue;
12046     }
12047
12048     // this may take place after moving, so 'element' may have changed
12049     if (IS_CHANGING(x, y) &&
12050         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12051     {
12052       int page = element_info[element].event_page_nr[CE_DELAY];
12053
12054       HandleElementChange(x, y, page);
12055
12056       element = Feld[x][y];
12057       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12058     }
12059
12060     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12061     {
12062       StartMoving(x, y);
12063
12064       element = Feld[x][y];
12065       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12066
12067       if (IS_ANIMATED(graphic) &&
12068           !IS_MOVING(x, y) &&
12069           !Stop[x][y])
12070         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12071
12072       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12073         TEST_DrawTwinkleOnField(x, y);
12074     }
12075     else if (element == EL_ACID)
12076     {
12077       if (!Stop[x][y])
12078         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12079     }
12080     else if ((element == EL_EXIT_OPEN ||
12081               element == EL_EM_EXIT_OPEN ||
12082               element == EL_SP_EXIT_OPEN ||
12083               element == EL_STEEL_EXIT_OPEN ||
12084               element == EL_EM_STEEL_EXIT_OPEN ||
12085               element == EL_SP_TERMINAL ||
12086               element == EL_SP_TERMINAL_ACTIVE ||
12087               element == EL_EXTRA_TIME ||
12088               element == EL_SHIELD_NORMAL ||
12089               element == EL_SHIELD_DEADLY) &&
12090              IS_ANIMATED(graphic))
12091       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12092     else if (IS_MOVING(x, y))
12093       ContinueMoving(x, y);
12094     else if (IS_ACTIVE_BOMB(element))
12095       CheckDynamite(x, y);
12096     else if (element == EL_AMOEBA_GROWING)
12097       AmoebeWaechst(x, y);
12098     else if (element == EL_AMOEBA_SHRINKING)
12099       AmoebaDisappearing(x, y);
12100
12101 #if !USE_NEW_AMOEBA_CODE
12102     else if (IS_AMOEBALIVE(element))
12103       AmoebeAbleger(x, y);
12104 #endif
12105
12106     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12107       Life(x, y);
12108     else if (element == EL_EXIT_CLOSED)
12109       CheckExit(x, y);
12110     else if (element == EL_EM_EXIT_CLOSED)
12111       CheckExitEM(x, y);
12112     else if (element == EL_STEEL_EXIT_CLOSED)
12113       CheckExitSteel(x, y);
12114     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12115       CheckExitSteelEM(x, y);
12116     else if (element == EL_SP_EXIT_CLOSED)
12117       CheckExitSP(x, y);
12118     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12119              element == EL_EXPANDABLE_STEELWALL_GROWING)
12120       MauerWaechst(x, y);
12121     else if (element == EL_EXPANDABLE_WALL ||
12122              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12123              element == EL_EXPANDABLE_WALL_VERTICAL ||
12124              element == EL_EXPANDABLE_WALL_ANY ||
12125              element == EL_BD_EXPANDABLE_WALL)
12126       MauerAbleger(x, y);
12127     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12128              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12129              element == EL_EXPANDABLE_STEELWALL_ANY)
12130       MauerAblegerStahl(x, y);
12131     else if (element == EL_FLAMES)
12132       CheckForDragon(x, y);
12133     else if (element == EL_EXPLOSION)
12134       ; // drawing of correct explosion animation is handled separately
12135     else if (element == EL_ELEMENT_SNAPPING ||
12136              element == EL_DIAGONAL_SHRINKING ||
12137              element == EL_DIAGONAL_GROWING)
12138     {
12139       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12140
12141       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12142     }
12143     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12144       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12145
12146     if (IS_BELT_ACTIVE(element))
12147       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12148
12149     if (game.magic_wall_active)
12150     {
12151       int jx = local_player->jx, jy = local_player->jy;
12152
12153       // play the element sound at the position nearest to the player
12154       if ((element == EL_MAGIC_WALL_FULL ||
12155            element == EL_MAGIC_WALL_ACTIVE ||
12156            element == EL_MAGIC_WALL_EMPTYING ||
12157            element == EL_BD_MAGIC_WALL_FULL ||
12158            element == EL_BD_MAGIC_WALL_ACTIVE ||
12159            element == EL_BD_MAGIC_WALL_EMPTYING ||
12160            element == EL_DC_MAGIC_WALL_FULL ||
12161            element == EL_DC_MAGIC_WALL_ACTIVE ||
12162            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12163           ABS(x - jx) + ABS(y - jy) <
12164           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12165       {
12166         magic_wall_x = x;
12167         magic_wall_y = y;
12168       }
12169     }
12170   }
12171
12172 #if USE_NEW_AMOEBA_CODE
12173   // new experimental amoeba growth stuff
12174   if (!(FrameCounter % 8))
12175   {
12176     static unsigned int random = 1684108901;
12177
12178     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12179     {
12180       x = RND(lev_fieldx);
12181       y = RND(lev_fieldy);
12182       element = Feld[x][y];
12183
12184       if (!IS_PLAYER(x,y) &&
12185           (element == EL_EMPTY ||
12186            CAN_GROW_INTO(element) ||
12187            element == EL_QUICKSAND_EMPTY ||
12188            element == EL_QUICKSAND_FAST_EMPTY ||
12189            element == EL_ACID_SPLASH_LEFT ||
12190            element == EL_ACID_SPLASH_RIGHT))
12191       {
12192         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12193             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12194             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12195             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12196           Feld[x][y] = EL_AMOEBA_DROP;
12197       }
12198
12199       random = random * 129 + 1;
12200     }
12201   }
12202 #endif
12203
12204   game.explosions_delayed = FALSE;
12205
12206   SCAN_PLAYFIELD(x, y)
12207   {
12208     element = Feld[x][y];
12209
12210     if (ExplodeField[x][y])
12211       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12212     else if (element == EL_EXPLOSION)
12213       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12214
12215     ExplodeField[x][y] = EX_TYPE_NONE;
12216   }
12217
12218   game.explosions_delayed = TRUE;
12219
12220   if (game.magic_wall_active)
12221   {
12222     if (!(game.magic_wall_time_left % 4))
12223     {
12224       int element = Feld[magic_wall_x][magic_wall_y];
12225
12226       if (element == EL_BD_MAGIC_WALL_FULL ||
12227           element == EL_BD_MAGIC_WALL_ACTIVE ||
12228           element == EL_BD_MAGIC_WALL_EMPTYING)
12229         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12230       else if (element == EL_DC_MAGIC_WALL_FULL ||
12231                element == EL_DC_MAGIC_WALL_ACTIVE ||
12232                element == EL_DC_MAGIC_WALL_EMPTYING)
12233         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12234       else
12235         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12236     }
12237
12238     if (game.magic_wall_time_left > 0)
12239     {
12240       game.magic_wall_time_left--;
12241
12242       if (!game.magic_wall_time_left)
12243       {
12244         SCAN_PLAYFIELD(x, y)
12245         {
12246           element = Feld[x][y];
12247
12248           if (element == EL_MAGIC_WALL_ACTIVE ||
12249               element == EL_MAGIC_WALL_FULL)
12250           {
12251             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12252             TEST_DrawLevelField(x, y);
12253           }
12254           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12255                    element == EL_BD_MAGIC_WALL_FULL)
12256           {
12257             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12258             TEST_DrawLevelField(x, y);
12259           }
12260           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12261                    element == EL_DC_MAGIC_WALL_FULL)
12262           {
12263             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12264             TEST_DrawLevelField(x, y);
12265           }
12266         }
12267
12268         game.magic_wall_active = FALSE;
12269       }
12270     }
12271   }
12272
12273   if (game.light_time_left > 0)
12274   {
12275     game.light_time_left--;
12276
12277     if (game.light_time_left == 0)
12278       RedrawAllLightSwitchesAndInvisibleElements();
12279   }
12280
12281   if (game.timegate_time_left > 0)
12282   {
12283     game.timegate_time_left--;
12284
12285     if (game.timegate_time_left == 0)
12286       CloseAllOpenTimegates();
12287   }
12288
12289   if (game.lenses_time_left > 0)
12290   {
12291     game.lenses_time_left--;
12292
12293     if (game.lenses_time_left == 0)
12294       RedrawAllInvisibleElementsForLenses();
12295   }
12296
12297   if (game.magnify_time_left > 0)
12298   {
12299     game.magnify_time_left--;
12300
12301     if (game.magnify_time_left == 0)
12302       RedrawAllInvisibleElementsForMagnifier();
12303   }
12304
12305   for (i = 0; i < MAX_PLAYERS; i++)
12306   {
12307     struct PlayerInfo *player = &stored_player[i];
12308
12309     if (SHIELD_ON(player))
12310     {
12311       if (player->shield_deadly_time_left)
12312         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12313       else if (player->shield_normal_time_left)
12314         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12315     }
12316   }
12317
12318 #if USE_DELAYED_GFX_REDRAW
12319   SCAN_PLAYFIELD(x, y)
12320   {
12321     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12322     {
12323       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12324          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12325
12326       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12327         DrawLevelField(x, y);
12328
12329       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12330         DrawLevelFieldCrumbled(x, y);
12331
12332       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12333         DrawLevelFieldCrumbledNeighbours(x, y);
12334
12335       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12336         DrawTwinkleOnField(x, y);
12337     }
12338
12339     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12340   }
12341 #endif
12342
12343   DrawAllPlayers();
12344   PlayAllPlayersSound();
12345
12346   for (i = 0; i < MAX_PLAYERS; i++)
12347   {
12348     struct PlayerInfo *player = &stored_player[i];
12349
12350     if (player->show_envelope != 0 && (!player->active ||
12351                                        player->MovPos == 0))
12352     {
12353       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12354
12355       player->show_envelope = 0;
12356     }
12357   }
12358
12359   // use random number generator in every frame to make it less predictable
12360   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12361     RND(1);
12362
12363   mouse_action_last = mouse_action;
12364 }
12365
12366 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12367 {
12368   int min_x = x, min_y = y, max_x = x, max_y = y;
12369   int i;
12370
12371   for (i = 0; i < MAX_PLAYERS; i++)
12372   {
12373     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12374
12375     if (!stored_player[i].active || &stored_player[i] == player)
12376       continue;
12377
12378     min_x = MIN(min_x, jx);
12379     min_y = MIN(min_y, jy);
12380     max_x = MAX(max_x, jx);
12381     max_y = MAX(max_y, jy);
12382   }
12383
12384   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12385 }
12386
12387 static boolean AllPlayersInVisibleScreen(void)
12388 {
12389   int i;
12390
12391   for (i = 0; i < MAX_PLAYERS; i++)
12392   {
12393     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12394
12395     if (!stored_player[i].active)
12396       continue;
12397
12398     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12399       return FALSE;
12400   }
12401
12402   return TRUE;
12403 }
12404
12405 void ScrollLevel(int dx, int dy)
12406 {
12407   int scroll_offset = 2 * TILEX_VAR;
12408   int x, y;
12409
12410   BlitBitmap(drawto_field, drawto_field,
12411              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12412              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12413              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12414              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12415              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12416              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12417
12418   if (dx != 0)
12419   {
12420     x = (dx == 1 ? BX1 : BX2);
12421     for (y = BY1; y <= BY2; y++)
12422       DrawScreenField(x, y);
12423   }
12424
12425   if (dy != 0)
12426   {
12427     y = (dy == 1 ? BY1 : BY2);
12428     for (x = BX1; x <= BX2; x++)
12429       DrawScreenField(x, y);
12430   }
12431
12432   redraw_mask |= REDRAW_FIELD;
12433 }
12434
12435 static boolean canFallDown(struct PlayerInfo *player)
12436 {
12437   int jx = player->jx, jy = player->jy;
12438
12439   return (IN_LEV_FIELD(jx, jy + 1) &&
12440           (IS_FREE(jx, jy + 1) ||
12441            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12442           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12443           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12444 }
12445
12446 static boolean canPassField(int x, int y, int move_dir)
12447 {
12448   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12449   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12450   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12451   int nextx = x + dx;
12452   int nexty = y + dy;
12453   int element = Feld[x][y];
12454
12455   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12456           !CAN_MOVE(element) &&
12457           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12458           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12459           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12460 }
12461
12462 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12463 {
12464   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12465   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12466   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12467   int newx = x + dx;
12468   int newy = y + dy;
12469
12470   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12471           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12472           (IS_DIGGABLE(Feld[newx][newy]) ||
12473            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12474            canPassField(newx, newy, move_dir)));
12475 }
12476
12477 static void CheckGravityMovement(struct PlayerInfo *player)
12478 {
12479   if (player->gravity && !player->programmed_action)
12480   {
12481     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12482     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12483     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12484     int jx = player->jx, jy = player->jy;
12485     boolean player_is_moving_to_valid_field =
12486       (!player_is_snapping &&
12487        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12488         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12489     boolean player_can_fall_down = canFallDown(player);
12490
12491     if (player_can_fall_down &&
12492         !player_is_moving_to_valid_field)
12493       player->programmed_action = MV_DOWN;
12494   }
12495 }
12496
12497 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12498 {
12499   return CheckGravityMovement(player);
12500
12501   if (player->gravity && !player->programmed_action)
12502   {
12503     int jx = player->jx, jy = player->jy;
12504     boolean field_under_player_is_free =
12505       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12506     boolean player_is_standing_on_valid_field =
12507       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12508        (IS_WALKABLE(Feld[jx][jy]) &&
12509         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12510
12511     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12512       player->programmed_action = MV_DOWN;
12513   }
12514 }
12515
12516 /*
12517   MovePlayerOneStep()
12518   -----------------------------------------------------------------------------
12519   dx, dy:               direction (non-diagonal) to try to move the player to
12520   real_dx, real_dy:     direction as read from input device (can be diagonal)
12521 */
12522
12523 boolean MovePlayerOneStep(struct PlayerInfo *player,
12524                           int dx, int dy, int real_dx, int real_dy)
12525 {
12526   int jx = player->jx, jy = player->jy;
12527   int new_jx = jx + dx, new_jy = jy + dy;
12528   int can_move;
12529   boolean player_can_move = !player->cannot_move;
12530
12531   if (!player->active || (!dx && !dy))
12532     return MP_NO_ACTION;
12533
12534   player->MovDir = (dx < 0 ? MV_LEFT :
12535                     dx > 0 ? MV_RIGHT :
12536                     dy < 0 ? MV_UP :
12537                     dy > 0 ? MV_DOWN :  MV_NONE);
12538
12539   if (!IN_LEV_FIELD(new_jx, new_jy))
12540     return MP_NO_ACTION;
12541
12542   if (!player_can_move)
12543   {
12544     if (player->MovPos == 0)
12545     {
12546       player->is_moving = FALSE;
12547       player->is_digging = FALSE;
12548       player->is_collecting = FALSE;
12549       player->is_snapping = FALSE;
12550       player->is_pushing = FALSE;
12551     }
12552   }
12553
12554   if (!network.enabled && game.centered_player_nr == -1 &&
12555       !AllPlayersInSight(player, new_jx, new_jy))
12556     return MP_NO_ACTION;
12557
12558   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12559   if (can_move != MP_MOVING)
12560     return can_move;
12561
12562   // check if DigField() has caused relocation of the player
12563   if (player->jx != jx || player->jy != jy)
12564     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12565
12566   StorePlayer[jx][jy] = 0;
12567   player->last_jx = jx;
12568   player->last_jy = jy;
12569   player->jx = new_jx;
12570   player->jy = new_jy;
12571   StorePlayer[new_jx][new_jy] = player->element_nr;
12572
12573   if (player->move_delay_value_next != -1)
12574   {
12575     player->move_delay_value = player->move_delay_value_next;
12576     player->move_delay_value_next = -1;
12577   }
12578
12579   player->MovPos =
12580     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12581
12582   player->step_counter++;
12583
12584   PlayerVisit[jx][jy] = FrameCounter;
12585
12586   player->is_moving = TRUE;
12587
12588 #if 1
12589   // should better be called in MovePlayer(), but this breaks some tapes
12590   ScrollPlayer(player, SCROLL_INIT);
12591 #endif
12592
12593   return MP_MOVING;
12594 }
12595
12596 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12597 {
12598   int jx = player->jx, jy = player->jy;
12599   int old_jx = jx, old_jy = jy;
12600   int moved = MP_NO_ACTION;
12601
12602   if (!player->active)
12603     return FALSE;
12604
12605   if (!dx && !dy)
12606   {
12607     if (player->MovPos == 0)
12608     {
12609       player->is_moving = FALSE;
12610       player->is_digging = FALSE;
12611       player->is_collecting = FALSE;
12612       player->is_snapping = FALSE;
12613       player->is_pushing = FALSE;
12614     }
12615
12616     return FALSE;
12617   }
12618
12619   if (player->move_delay > 0)
12620     return FALSE;
12621
12622   player->move_delay = -1;              // set to "uninitialized" value
12623
12624   // store if player is automatically moved to next field
12625   player->is_auto_moving = (player->programmed_action != MV_NONE);
12626
12627   // remove the last programmed player action
12628   player->programmed_action = 0;
12629
12630   if (player->MovPos)
12631   {
12632     // should only happen if pre-1.2 tape recordings are played
12633     // this is only for backward compatibility
12634
12635     int original_move_delay_value = player->move_delay_value;
12636
12637 #if DEBUG
12638     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12639            tape.counter);
12640 #endif
12641
12642     // scroll remaining steps with finest movement resolution
12643     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12644
12645     while (player->MovPos)
12646     {
12647       ScrollPlayer(player, SCROLL_GO_ON);
12648       ScrollScreen(NULL, SCROLL_GO_ON);
12649
12650       AdvanceFrameAndPlayerCounters(player->index_nr);
12651
12652       DrawAllPlayers();
12653       BackToFront_WithFrameDelay(0);
12654     }
12655
12656     player->move_delay_value = original_move_delay_value;
12657   }
12658
12659   player->is_active = FALSE;
12660
12661   if (player->last_move_dir & MV_HORIZONTAL)
12662   {
12663     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12664       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12665   }
12666   else
12667   {
12668     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12669       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12670   }
12671
12672   if (!moved && !player->is_active)
12673   {
12674     player->is_moving = FALSE;
12675     player->is_digging = FALSE;
12676     player->is_collecting = FALSE;
12677     player->is_snapping = FALSE;
12678     player->is_pushing = FALSE;
12679   }
12680
12681   jx = player->jx;
12682   jy = player->jy;
12683
12684   if (moved & MP_MOVING && !ScreenMovPos &&
12685       (player->index_nr == game.centered_player_nr ||
12686        game.centered_player_nr == -1))
12687   {
12688     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12689
12690     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12691     {
12692       // actual player has left the screen -- scroll in that direction
12693       if (jx != old_jx)         // player has moved horizontally
12694         scroll_x += (jx - old_jx);
12695       else                      // player has moved vertically
12696         scroll_y += (jy - old_jy);
12697     }
12698     else
12699     {
12700       int offset_raw = game.scroll_delay_value;
12701
12702       if (jx != old_jx)         // player has moved horizontally
12703       {
12704         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12705         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12706         int new_scroll_x = jx - MIDPOSX + offset_x;
12707
12708         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12709             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12710           scroll_x = new_scroll_x;
12711
12712         // don't scroll over playfield boundaries
12713         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12714
12715         // don't scroll more than one field at a time
12716         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12717
12718         // don't scroll against the player's moving direction
12719         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12720             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12721           scroll_x = old_scroll_x;
12722       }
12723       else                      // player has moved vertically
12724       {
12725         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12726         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12727         int new_scroll_y = jy - MIDPOSY + offset_y;
12728
12729         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12730             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12731           scroll_y = new_scroll_y;
12732
12733         // don't scroll over playfield boundaries
12734         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12735
12736         // don't scroll more than one field at a time
12737         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12738
12739         // don't scroll against the player's moving direction
12740         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12741             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12742           scroll_y = old_scroll_y;
12743       }
12744     }
12745
12746     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12747     {
12748       if (!network.enabled && game.centered_player_nr == -1 &&
12749           !AllPlayersInVisibleScreen())
12750       {
12751         scroll_x = old_scroll_x;
12752         scroll_y = old_scroll_y;
12753       }
12754       else
12755       {
12756         ScrollScreen(player, SCROLL_INIT);
12757         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12758       }
12759     }
12760   }
12761
12762   player->StepFrame = 0;
12763
12764   if (moved & MP_MOVING)
12765   {
12766     if (old_jx != jx && old_jy == jy)
12767       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12768     else if (old_jx == jx && old_jy != jy)
12769       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12770
12771     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12772
12773     player->last_move_dir = player->MovDir;
12774     player->is_moving = TRUE;
12775     player->is_snapping = FALSE;
12776     player->is_switching = FALSE;
12777     player->is_dropping = FALSE;
12778     player->is_dropping_pressed = FALSE;
12779     player->drop_pressed_delay = 0;
12780
12781 #if 0
12782     // should better be called here than above, but this breaks some tapes
12783     ScrollPlayer(player, SCROLL_INIT);
12784 #endif
12785   }
12786   else
12787   {
12788     CheckGravityMovementWhenNotMoving(player);
12789
12790     player->is_moving = FALSE;
12791
12792     /* at this point, the player is allowed to move, but cannot move right now
12793        (e.g. because of something blocking the way) -- ensure that the player
12794        is also allowed to move in the next frame (in old versions before 3.1.1,
12795        the player was forced to wait again for eight frames before next try) */
12796
12797     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12798       player->move_delay = 0;   // allow direct movement in the next frame
12799   }
12800
12801   if (player->move_delay == -1)         // not yet initialized by DigField()
12802     player->move_delay = player->move_delay_value;
12803
12804   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12805   {
12806     TestIfPlayerTouchesBadThing(jx, jy);
12807     TestIfPlayerTouchesCustomElement(jx, jy);
12808   }
12809
12810   if (!player->active)
12811     RemovePlayer(player);
12812
12813   return moved;
12814 }
12815
12816 void ScrollPlayer(struct PlayerInfo *player, int mode)
12817 {
12818   int jx = player->jx, jy = player->jy;
12819   int last_jx = player->last_jx, last_jy = player->last_jy;
12820   int move_stepsize = TILEX / player->move_delay_value;
12821
12822   if (!player->active)
12823     return;
12824
12825   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12826     return;
12827
12828   if (mode == SCROLL_INIT)
12829   {
12830     player->actual_frame_counter = FrameCounter;
12831     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12832
12833     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12834         Feld[last_jx][last_jy] == EL_EMPTY)
12835     {
12836       int last_field_block_delay = 0;   // start with no blocking at all
12837       int block_delay_adjustment = player->block_delay_adjustment;
12838
12839       // if player blocks last field, add delay for exactly one move
12840       if (player->block_last_field)
12841       {
12842         last_field_block_delay += player->move_delay_value;
12843
12844         // when blocking enabled, prevent moving up despite gravity
12845         if (player->gravity && player->MovDir == MV_UP)
12846           block_delay_adjustment = -1;
12847       }
12848
12849       // add block delay adjustment (also possible when not blocking)
12850       last_field_block_delay += block_delay_adjustment;
12851
12852       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12853       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12854     }
12855
12856     if (player->MovPos != 0)    // player has not yet reached destination
12857       return;
12858   }
12859   else if (!FrameReached(&player->actual_frame_counter, 1))
12860     return;
12861
12862   if (player->MovPos != 0)
12863   {
12864     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12865     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12866
12867     // before DrawPlayer() to draw correct player graphic for this case
12868     if (player->MovPos == 0)
12869       CheckGravityMovement(player);
12870   }
12871
12872   if (player->MovPos == 0)      // player reached destination field
12873   {
12874     if (player->move_delay_reset_counter > 0)
12875     {
12876       player->move_delay_reset_counter--;
12877
12878       if (player->move_delay_reset_counter == 0)
12879       {
12880         // continue with normal speed after quickly moving through gate
12881         HALVE_PLAYER_SPEED(player);
12882
12883         // be able to make the next move without delay
12884         player->move_delay = 0;
12885       }
12886     }
12887
12888     player->last_jx = jx;
12889     player->last_jy = jy;
12890
12891     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12892         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12893         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12894         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12895         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12896         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12897         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12898         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12899     {
12900       ExitPlayer(player);
12901
12902       if (game.players_still_needed == 0 &&
12903           (game.friends_still_needed == 0 ||
12904            IS_SP_ELEMENT(Feld[jx][jy])))
12905         LevelSolved();
12906     }
12907
12908     // this breaks one level: "machine", level 000
12909     {
12910       int move_direction = player->MovDir;
12911       int enter_side = MV_DIR_OPPOSITE(move_direction);
12912       int leave_side = move_direction;
12913       int old_jx = last_jx;
12914       int old_jy = last_jy;
12915       int old_element = Feld[old_jx][old_jy];
12916       int new_element = Feld[jx][jy];
12917
12918       if (IS_CUSTOM_ELEMENT(old_element))
12919         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12920                                    CE_LEFT_BY_PLAYER,
12921                                    player->index_bit, leave_side);
12922
12923       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12924                                           CE_PLAYER_LEAVES_X,
12925                                           player->index_bit, leave_side);
12926
12927       if (IS_CUSTOM_ELEMENT(new_element))
12928         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12929                                    player->index_bit, enter_side);
12930
12931       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12932                                           CE_PLAYER_ENTERS_X,
12933                                           player->index_bit, enter_side);
12934
12935       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12936                                         CE_MOVE_OF_X, move_direction);
12937     }
12938
12939     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12940     {
12941       TestIfPlayerTouchesBadThing(jx, jy);
12942       TestIfPlayerTouchesCustomElement(jx, jy);
12943
12944       /* needed because pushed element has not yet reached its destination,
12945          so it would trigger a change event at its previous field location */
12946       if (!player->is_pushing)
12947         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12948
12949       if (!player->active)
12950         RemovePlayer(player);
12951     }
12952
12953     if (!game.LevelSolved && level.use_step_counter)
12954     {
12955       int i;
12956
12957       TimePlayed++;
12958
12959       if (TimeLeft > 0)
12960       {
12961         TimeLeft--;
12962
12963         if (TimeLeft <= 10 && setup.time_limit)
12964           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12965
12966         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12967
12968         DisplayGameControlValues();
12969
12970         if (!TimeLeft && setup.time_limit)
12971           for (i = 0; i < MAX_PLAYERS; i++)
12972             KillPlayer(&stored_player[i]);
12973       }
12974       else if (game.no_time_limit && !game.all_players_gone)
12975       {
12976         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12977
12978         DisplayGameControlValues();
12979       }
12980     }
12981
12982     if (tape.single_step && tape.recording && !tape.pausing &&
12983         !player->programmed_action)
12984       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12985
12986     if (!player->programmed_action)
12987       CheckSaveEngineSnapshot(player);
12988   }
12989 }
12990
12991 void ScrollScreen(struct PlayerInfo *player, int mode)
12992 {
12993   static unsigned int screen_frame_counter = 0;
12994
12995   if (mode == SCROLL_INIT)
12996   {
12997     // set scrolling step size according to actual player's moving speed
12998     ScrollStepSize = TILEX / player->move_delay_value;
12999
13000     screen_frame_counter = FrameCounter;
13001     ScreenMovDir = player->MovDir;
13002     ScreenMovPos = player->MovPos;
13003     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13004     return;
13005   }
13006   else if (!FrameReached(&screen_frame_counter, 1))
13007     return;
13008
13009   if (ScreenMovPos)
13010   {
13011     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13012     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13013     redraw_mask |= REDRAW_FIELD;
13014   }
13015   else
13016     ScreenMovDir = MV_NONE;
13017 }
13018
13019 void TestIfPlayerTouchesCustomElement(int x, int y)
13020 {
13021   static int xy[4][2] =
13022   {
13023     { 0, -1 },
13024     { -1, 0 },
13025     { +1, 0 },
13026     { 0, +1 }
13027   };
13028   static int trigger_sides[4][2] =
13029   {
13030     // center side       border side
13031     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13032     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13033     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13034     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13035   };
13036   static int touch_dir[4] =
13037   {
13038     MV_LEFT | MV_RIGHT,
13039     MV_UP   | MV_DOWN,
13040     MV_UP   | MV_DOWN,
13041     MV_LEFT | MV_RIGHT
13042   };
13043   int center_element = Feld[x][y];      // should always be non-moving!
13044   int i;
13045
13046   for (i = 0; i < NUM_DIRECTIONS; i++)
13047   {
13048     int xx = x + xy[i][0];
13049     int yy = y + xy[i][1];
13050     int center_side = trigger_sides[i][0];
13051     int border_side = trigger_sides[i][1];
13052     int border_element;
13053
13054     if (!IN_LEV_FIELD(xx, yy))
13055       continue;
13056
13057     if (IS_PLAYER(x, y))                // player found at center element
13058     {
13059       struct PlayerInfo *player = PLAYERINFO(x, y);
13060
13061       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13062         border_element = Feld[xx][yy];          // may be moving!
13063       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13064         border_element = Feld[xx][yy];
13065       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13066         border_element = MovingOrBlocked2Element(xx, yy);
13067       else
13068         continue;               // center and border element do not touch
13069
13070       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13071                                  player->index_bit, border_side);
13072       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13073                                           CE_PLAYER_TOUCHES_X,
13074                                           player->index_bit, border_side);
13075
13076       {
13077         /* use player element that is initially defined in the level playfield,
13078            not the player element that corresponds to the runtime player number
13079            (example: a level that contains EL_PLAYER_3 as the only player would
13080            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13081         int player_element = PLAYERINFO(x, y)->initial_element;
13082
13083         CheckElementChangeBySide(xx, yy, border_element, player_element,
13084                                  CE_TOUCHING_X, border_side);
13085       }
13086     }
13087     else if (IS_PLAYER(xx, yy))         // player found at border element
13088     {
13089       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13090
13091       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13092       {
13093         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13094           continue;             // center and border element do not touch
13095       }
13096
13097       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13098                                  player->index_bit, center_side);
13099       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13100                                           CE_PLAYER_TOUCHES_X,
13101                                           player->index_bit, center_side);
13102
13103       {
13104         /* use player element that is initially defined in the level playfield,
13105            not the player element that corresponds to the runtime player number
13106            (example: a level that contains EL_PLAYER_3 as the only player would
13107            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13108         int player_element = PLAYERINFO(xx, yy)->initial_element;
13109
13110         CheckElementChangeBySide(x, y, center_element, player_element,
13111                                  CE_TOUCHING_X, center_side);
13112       }
13113
13114       break;
13115     }
13116   }
13117 }
13118
13119 void TestIfElementTouchesCustomElement(int x, int y)
13120 {
13121   static int xy[4][2] =
13122   {
13123     { 0, -1 },
13124     { -1, 0 },
13125     { +1, 0 },
13126     { 0, +1 }
13127   };
13128   static int trigger_sides[4][2] =
13129   {
13130     // center side      border side
13131     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13132     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13133     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13134     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13135   };
13136   static int touch_dir[4] =
13137   {
13138     MV_LEFT | MV_RIGHT,
13139     MV_UP   | MV_DOWN,
13140     MV_UP   | MV_DOWN,
13141     MV_LEFT | MV_RIGHT
13142   };
13143   boolean change_center_element = FALSE;
13144   int center_element = Feld[x][y];      // should always be non-moving!
13145   int border_element_old[NUM_DIRECTIONS];
13146   int i;
13147
13148   for (i = 0; i < NUM_DIRECTIONS; i++)
13149   {
13150     int xx = x + xy[i][0];
13151     int yy = y + xy[i][1];
13152     int border_element;
13153
13154     border_element_old[i] = -1;
13155
13156     if (!IN_LEV_FIELD(xx, yy))
13157       continue;
13158
13159     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13160       border_element = Feld[xx][yy];    // may be moving!
13161     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13162       border_element = Feld[xx][yy];
13163     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13164       border_element = MovingOrBlocked2Element(xx, yy);
13165     else
13166       continue;                 // center and border element do not touch
13167
13168     border_element_old[i] = border_element;
13169   }
13170
13171   for (i = 0; i < NUM_DIRECTIONS; i++)
13172   {
13173     int xx = x + xy[i][0];
13174     int yy = y + xy[i][1];
13175     int center_side = trigger_sides[i][0];
13176     int border_element = border_element_old[i];
13177
13178     if (border_element == -1)
13179       continue;
13180
13181     // check for change of border element
13182     CheckElementChangeBySide(xx, yy, border_element, center_element,
13183                              CE_TOUCHING_X, center_side);
13184
13185     // (center element cannot be player, so we dont have to check this here)
13186   }
13187
13188   for (i = 0; i < NUM_DIRECTIONS; i++)
13189   {
13190     int xx = x + xy[i][0];
13191     int yy = y + xy[i][1];
13192     int border_side = trigger_sides[i][1];
13193     int border_element = border_element_old[i];
13194
13195     if (border_element == -1)
13196       continue;
13197
13198     // check for change of center element (but change it only once)
13199     if (!change_center_element)
13200       change_center_element =
13201         CheckElementChangeBySide(x, y, center_element, border_element,
13202                                  CE_TOUCHING_X, border_side);
13203
13204     if (IS_PLAYER(xx, yy))
13205     {
13206       /* use player element that is initially defined in the level playfield,
13207          not the player element that corresponds to the runtime player number
13208          (example: a level that contains EL_PLAYER_3 as the only player would
13209          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13210       int player_element = PLAYERINFO(xx, yy)->initial_element;
13211
13212       CheckElementChangeBySide(x, y, center_element, player_element,
13213                                CE_TOUCHING_X, border_side);
13214     }
13215   }
13216 }
13217
13218 void TestIfElementHitsCustomElement(int x, int y, int direction)
13219 {
13220   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13221   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13222   int hitx = x + dx, hity = y + dy;
13223   int hitting_element = Feld[x][y];
13224   int touched_element;
13225
13226   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13227     return;
13228
13229   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13230                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13231
13232   if (IN_LEV_FIELD(hitx, hity))
13233   {
13234     int opposite_direction = MV_DIR_OPPOSITE(direction);
13235     int hitting_side = direction;
13236     int touched_side = opposite_direction;
13237     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13238                           MovDir[hitx][hity] != direction ||
13239                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13240
13241     object_hit = TRUE;
13242
13243     if (object_hit)
13244     {
13245       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13246                                CE_HITTING_X, touched_side);
13247
13248       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13249                                CE_HIT_BY_X, hitting_side);
13250
13251       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13252                                CE_HIT_BY_SOMETHING, opposite_direction);
13253
13254       if (IS_PLAYER(hitx, hity))
13255       {
13256         /* use player element that is initially defined in the level playfield,
13257            not the player element that corresponds to the runtime player number
13258            (example: a level that contains EL_PLAYER_3 as the only player would
13259            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13260         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13261
13262         CheckElementChangeBySide(x, y, hitting_element, player_element,
13263                                  CE_HITTING_X, touched_side);
13264       }
13265     }
13266   }
13267
13268   // "hitting something" is also true when hitting the playfield border
13269   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13270                            CE_HITTING_SOMETHING, direction);
13271 }
13272
13273 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13274 {
13275   int i, kill_x = -1, kill_y = -1;
13276
13277   int bad_element = -1;
13278   static int test_xy[4][2] =
13279   {
13280     { 0, -1 },
13281     { -1, 0 },
13282     { +1, 0 },
13283     { 0, +1 }
13284   };
13285   static int test_dir[4] =
13286   {
13287     MV_UP,
13288     MV_LEFT,
13289     MV_RIGHT,
13290     MV_DOWN
13291   };
13292
13293   for (i = 0; i < NUM_DIRECTIONS; i++)
13294   {
13295     int test_x, test_y, test_move_dir, test_element;
13296
13297     test_x = good_x + test_xy[i][0];
13298     test_y = good_y + test_xy[i][1];
13299
13300     if (!IN_LEV_FIELD(test_x, test_y))
13301       continue;
13302
13303     test_move_dir =
13304       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13305
13306     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13307
13308     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13309        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13310     */
13311     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13312         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13313     {
13314       kill_x = test_x;
13315       kill_y = test_y;
13316       bad_element = test_element;
13317
13318       break;
13319     }
13320   }
13321
13322   if (kill_x != -1 || kill_y != -1)
13323   {
13324     if (IS_PLAYER(good_x, good_y))
13325     {
13326       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13327
13328       if (player->shield_deadly_time_left > 0 &&
13329           !IS_INDESTRUCTIBLE(bad_element))
13330         Bang(kill_x, kill_y);
13331       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13332         KillPlayer(player);
13333     }
13334     else
13335       Bang(good_x, good_y);
13336   }
13337 }
13338
13339 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13340 {
13341   int i, kill_x = -1, kill_y = -1;
13342   int bad_element = Feld[bad_x][bad_y];
13343   static int test_xy[4][2] =
13344   {
13345     { 0, -1 },
13346     { -1, 0 },
13347     { +1, 0 },
13348     { 0, +1 }
13349   };
13350   static int touch_dir[4] =
13351   {
13352     MV_LEFT | MV_RIGHT,
13353     MV_UP   | MV_DOWN,
13354     MV_UP   | MV_DOWN,
13355     MV_LEFT | MV_RIGHT
13356   };
13357   static int test_dir[4] =
13358   {
13359     MV_UP,
13360     MV_LEFT,
13361     MV_RIGHT,
13362     MV_DOWN
13363   };
13364
13365   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13366     return;
13367
13368   for (i = 0; i < NUM_DIRECTIONS; i++)
13369   {
13370     int test_x, test_y, test_move_dir, test_element;
13371
13372     test_x = bad_x + test_xy[i][0];
13373     test_y = bad_y + test_xy[i][1];
13374
13375     if (!IN_LEV_FIELD(test_x, test_y))
13376       continue;
13377
13378     test_move_dir =
13379       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13380
13381     test_element = Feld[test_x][test_y];
13382
13383     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13384        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13385     */
13386     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13387         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13388     {
13389       // good thing is player or penguin that does not move away
13390       if (IS_PLAYER(test_x, test_y))
13391       {
13392         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13393
13394         if (bad_element == EL_ROBOT && player->is_moving)
13395           continue;     // robot does not kill player if he is moving
13396
13397         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13398         {
13399           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13400             continue;           // center and border element do not touch
13401         }
13402
13403         kill_x = test_x;
13404         kill_y = test_y;
13405
13406         break;
13407       }
13408       else if (test_element == EL_PENGUIN)
13409       {
13410         kill_x = test_x;
13411         kill_y = test_y;
13412
13413         break;
13414       }
13415     }
13416   }
13417
13418   if (kill_x != -1 || kill_y != -1)
13419   {
13420     if (IS_PLAYER(kill_x, kill_y))
13421     {
13422       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13423
13424       if (player->shield_deadly_time_left > 0 &&
13425           !IS_INDESTRUCTIBLE(bad_element))
13426         Bang(bad_x, bad_y);
13427       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13428         KillPlayer(player);
13429     }
13430     else
13431       Bang(kill_x, kill_y);
13432   }
13433 }
13434
13435 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13436 {
13437   int bad_element = Feld[bad_x][bad_y];
13438   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13439   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13440   int test_x = bad_x + dx, test_y = bad_y + dy;
13441   int test_move_dir, test_element;
13442   int kill_x = -1, kill_y = -1;
13443
13444   if (!IN_LEV_FIELD(test_x, test_y))
13445     return;
13446
13447   test_move_dir =
13448     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13449
13450   test_element = Feld[test_x][test_y];
13451
13452   if (test_move_dir != bad_move_dir)
13453   {
13454     // good thing can be player or penguin that does not move away
13455     if (IS_PLAYER(test_x, test_y))
13456     {
13457       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13458
13459       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13460          player as being hit when he is moving towards the bad thing, because
13461          the "get hit by" condition would be lost after the player stops) */
13462       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13463         return;         // player moves away from bad thing
13464
13465       kill_x = test_x;
13466       kill_y = test_y;
13467     }
13468     else if (test_element == EL_PENGUIN)
13469     {
13470       kill_x = test_x;
13471       kill_y = test_y;
13472     }
13473   }
13474
13475   if (kill_x != -1 || kill_y != -1)
13476   {
13477     if (IS_PLAYER(kill_x, kill_y))
13478     {
13479       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13480
13481       if (player->shield_deadly_time_left > 0 &&
13482           !IS_INDESTRUCTIBLE(bad_element))
13483         Bang(bad_x, bad_y);
13484       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13485         KillPlayer(player);
13486     }
13487     else
13488       Bang(kill_x, kill_y);
13489   }
13490 }
13491
13492 void TestIfPlayerTouchesBadThing(int x, int y)
13493 {
13494   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13495 }
13496
13497 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13498 {
13499   TestIfGoodThingHitsBadThing(x, y, move_dir);
13500 }
13501
13502 void TestIfBadThingTouchesPlayer(int x, int y)
13503 {
13504   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13505 }
13506
13507 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13508 {
13509   TestIfBadThingHitsGoodThing(x, y, move_dir);
13510 }
13511
13512 void TestIfFriendTouchesBadThing(int x, int y)
13513 {
13514   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13515 }
13516
13517 void TestIfBadThingTouchesFriend(int x, int y)
13518 {
13519   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13520 }
13521
13522 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13523 {
13524   int i, kill_x = bad_x, kill_y = bad_y;
13525   static int xy[4][2] =
13526   {
13527     { 0, -1 },
13528     { -1, 0 },
13529     { +1, 0 },
13530     { 0, +1 }
13531   };
13532
13533   for (i = 0; i < NUM_DIRECTIONS; i++)
13534   {
13535     int x, y, element;
13536
13537     x = bad_x + xy[i][0];
13538     y = bad_y + xy[i][1];
13539     if (!IN_LEV_FIELD(x, y))
13540       continue;
13541
13542     element = Feld[x][y];
13543     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13544         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13545     {
13546       kill_x = x;
13547       kill_y = y;
13548       break;
13549     }
13550   }
13551
13552   if (kill_x != bad_x || kill_y != bad_y)
13553     Bang(bad_x, bad_y);
13554 }
13555
13556 void KillPlayer(struct PlayerInfo *player)
13557 {
13558   int jx = player->jx, jy = player->jy;
13559
13560   if (!player->active)
13561     return;
13562
13563 #if 0
13564   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13565          player->killed, player->active, player->reanimated);
13566 #endif
13567
13568   /* the following code was introduced to prevent an infinite loop when calling
13569      -> Bang()
13570      -> CheckTriggeredElementChangeExt()
13571      -> ExecuteCustomElementAction()
13572      -> KillPlayer()
13573      -> (infinitely repeating the above sequence of function calls)
13574      which occurs when killing the player while having a CE with the setting
13575      "kill player X when explosion of <player X>"; the solution using a new
13576      field "player->killed" was chosen for backwards compatibility, although
13577      clever use of the fields "player->active" etc. would probably also work */
13578 #if 1
13579   if (player->killed)
13580     return;
13581 #endif
13582
13583   player->killed = TRUE;
13584
13585   // remove accessible field at the player's position
13586   Feld[jx][jy] = EL_EMPTY;
13587
13588   // deactivate shield (else Bang()/Explode() would not work right)
13589   player->shield_normal_time_left = 0;
13590   player->shield_deadly_time_left = 0;
13591
13592 #if 0
13593   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13594          player->killed, player->active, player->reanimated);
13595 #endif
13596
13597   Bang(jx, jy);
13598
13599 #if 0
13600   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13601          player->killed, player->active, player->reanimated);
13602 #endif
13603
13604   if (player->reanimated)       // killed player may have been reanimated
13605     player->killed = player->reanimated = FALSE;
13606   else
13607     BuryPlayer(player);
13608 }
13609
13610 static void KillPlayerUnlessEnemyProtected(int x, int y)
13611 {
13612   if (!PLAYER_ENEMY_PROTECTED(x, y))
13613     KillPlayer(PLAYERINFO(x, y));
13614 }
13615
13616 static void KillPlayerUnlessExplosionProtected(int x, int y)
13617 {
13618   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13619     KillPlayer(PLAYERINFO(x, y));
13620 }
13621
13622 void BuryPlayer(struct PlayerInfo *player)
13623 {
13624   int jx = player->jx, jy = player->jy;
13625
13626   if (!player->active)
13627     return;
13628
13629   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13630   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13631
13632   RemovePlayer(player);
13633
13634   player->buried = TRUE;
13635
13636   if (game.all_players_gone)
13637     game.GameOver = TRUE;
13638 }
13639
13640 void RemovePlayer(struct PlayerInfo *player)
13641 {
13642   int jx = player->jx, jy = player->jy;
13643   int i, found = FALSE;
13644
13645   player->present = FALSE;
13646   player->active = FALSE;
13647
13648   // required for some CE actions (even if the player is not active anymore)
13649   player->MovPos = 0;
13650
13651   if (!ExplodeField[jx][jy])
13652     StorePlayer[jx][jy] = 0;
13653
13654   if (player->is_moving)
13655     TEST_DrawLevelField(player->last_jx, player->last_jy);
13656
13657   for (i = 0; i < MAX_PLAYERS; i++)
13658     if (stored_player[i].active)
13659       found = TRUE;
13660
13661   if (!found)
13662   {
13663     game.all_players_gone = TRUE;
13664     game.GameOver = TRUE;
13665   }
13666
13667   game.exit_x = game.robot_wheel_x = jx;
13668   game.exit_y = game.robot_wheel_y = jy;
13669 }
13670
13671 void ExitPlayer(struct PlayerInfo *player)
13672 {
13673   DrawPlayer(player);   // needed here only to cleanup last field
13674   RemovePlayer(player);
13675
13676   if (game.players_still_needed > 0)
13677     game.players_still_needed--;
13678 }
13679
13680 static void setFieldForSnapping(int x, int y, int element, int direction)
13681 {
13682   struct ElementInfo *ei = &element_info[element];
13683   int direction_bit = MV_DIR_TO_BIT(direction);
13684   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13685   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13686                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13687
13688   Feld[x][y] = EL_ELEMENT_SNAPPING;
13689   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13690
13691   ResetGfxAnimation(x, y);
13692
13693   GfxElement[x][y] = element;
13694   GfxAction[x][y] = action;
13695   GfxDir[x][y] = direction;
13696   GfxFrame[x][y] = -1;
13697 }
13698
13699 /*
13700   =============================================================================
13701   checkDiagonalPushing()
13702   -----------------------------------------------------------------------------
13703   check if diagonal input device direction results in pushing of object
13704   (by checking if the alternative direction is walkable, diggable, ...)
13705   =============================================================================
13706 */
13707
13708 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13709                                     int x, int y, int real_dx, int real_dy)
13710 {
13711   int jx, jy, dx, dy, xx, yy;
13712
13713   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13714     return TRUE;
13715
13716   // diagonal direction: check alternative direction
13717   jx = player->jx;
13718   jy = player->jy;
13719   dx = x - jx;
13720   dy = y - jy;
13721   xx = jx + (dx == 0 ? real_dx : 0);
13722   yy = jy + (dy == 0 ? real_dy : 0);
13723
13724   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13725 }
13726
13727 /*
13728   =============================================================================
13729   DigField()
13730   -----------------------------------------------------------------------------
13731   x, y:                 field next to player (non-diagonal) to try to dig to
13732   real_dx, real_dy:     direction as read from input device (can be diagonal)
13733   =============================================================================
13734 */
13735
13736 static int DigField(struct PlayerInfo *player,
13737                     int oldx, int oldy, int x, int y,
13738                     int real_dx, int real_dy, int mode)
13739 {
13740   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13741   boolean player_was_pushing = player->is_pushing;
13742   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13743   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13744   int jx = oldx, jy = oldy;
13745   int dx = x - jx, dy = y - jy;
13746   int nextx = x + dx, nexty = y + dy;
13747   int move_direction = (dx == -1 ? MV_LEFT  :
13748                         dx == +1 ? MV_RIGHT :
13749                         dy == -1 ? MV_UP    :
13750                         dy == +1 ? MV_DOWN  : MV_NONE);
13751   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13752   int dig_side = MV_DIR_OPPOSITE(move_direction);
13753   int old_element = Feld[jx][jy];
13754   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13755   int collect_count;
13756
13757   if (is_player)                // function can also be called by EL_PENGUIN
13758   {
13759     if (player->MovPos == 0)
13760     {
13761       player->is_digging = FALSE;
13762       player->is_collecting = FALSE;
13763     }
13764
13765     if (player->MovPos == 0)    // last pushing move finished
13766       player->is_pushing = FALSE;
13767
13768     if (mode == DF_NO_PUSH)     // player just stopped pushing
13769     {
13770       player->is_switching = FALSE;
13771       player->push_delay = -1;
13772
13773       return MP_NO_ACTION;
13774     }
13775   }
13776
13777   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13778     old_element = Back[jx][jy];
13779
13780   // in case of element dropped at player position, check background
13781   else if (Back[jx][jy] != EL_EMPTY &&
13782            game.engine_version >= VERSION_IDENT(2,2,0,0))
13783     old_element = Back[jx][jy];
13784
13785   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13786     return MP_NO_ACTION;        // field has no opening in this direction
13787
13788   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13789     return MP_NO_ACTION;        // field has no opening in this direction
13790
13791   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13792   {
13793     SplashAcid(x, y);
13794
13795     Feld[jx][jy] = player->artwork_element;
13796     InitMovingField(jx, jy, MV_DOWN);
13797     Store[jx][jy] = EL_ACID;
13798     ContinueMoving(jx, jy);
13799     BuryPlayer(player);
13800
13801     return MP_DONT_RUN_INTO;
13802   }
13803
13804   if (player_can_move && DONT_RUN_INTO(element))
13805   {
13806     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13807
13808     return MP_DONT_RUN_INTO;
13809   }
13810
13811   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13812     return MP_NO_ACTION;
13813
13814   collect_count = element_info[element].collect_count_initial;
13815
13816   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13817     return MP_NO_ACTION;
13818
13819   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13820     player_can_move = player_can_move_or_snap;
13821
13822   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13823       game.engine_version >= VERSION_IDENT(2,2,0,0))
13824   {
13825     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13826                                player->index_bit, dig_side);
13827     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13828                                         player->index_bit, dig_side);
13829
13830     if (element == EL_DC_LANDMINE)
13831       Bang(x, y);
13832
13833     if (Feld[x][y] != element)          // field changed by snapping
13834       return MP_ACTION;
13835
13836     return MP_NO_ACTION;
13837   }
13838
13839   if (player->gravity && is_player && !player->is_auto_moving &&
13840       canFallDown(player) && move_direction != MV_DOWN &&
13841       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13842     return MP_NO_ACTION;        // player cannot walk here due to gravity
13843
13844   if (player_can_move &&
13845       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13846   {
13847     int sound_element = SND_ELEMENT(element);
13848     int sound_action = ACTION_WALKING;
13849
13850     if (IS_RND_GATE(element))
13851     {
13852       if (!player->key[RND_GATE_NR(element)])
13853         return MP_NO_ACTION;
13854     }
13855     else if (IS_RND_GATE_GRAY(element))
13856     {
13857       if (!player->key[RND_GATE_GRAY_NR(element)])
13858         return MP_NO_ACTION;
13859     }
13860     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13861     {
13862       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13863         return MP_NO_ACTION;
13864     }
13865     else if (element == EL_EXIT_OPEN ||
13866              element == EL_EM_EXIT_OPEN ||
13867              element == EL_EM_EXIT_OPENING ||
13868              element == EL_STEEL_EXIT_OPEN ||
13869              element == EL_EM_STEEL_EXIT_OPEN ||
13870              element == EL_EM_STEEL_EXIT_OPENING ||
13871              element == EL_SP_EXIT_OPEN ||
13872              element == EL_SP_EXIT_OPENING)
13873     {
13874       sound_action = ACTION_PASSING;    // player is passing exit
13875     }
13876     else if (element == EL_EMPTY)
13877     {
13878       sound_action = ACTION_MOVING;             // nothing to walk on
13879     }
13880
13881     // play sound from background or player, whatever is available
13882     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13883       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13884     else
13885       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13886   }
13887   else if (player_can_move &&
13888            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13889   {
13890     if (!ACCESS_FROM(element, opposite_direction))
13891       return MP_NO_ACTION;      // field not accessible from this direction
13892
13893     if (CAN_MOVE(element))      // only fixed elements can be passed!
13894       return MP_NO_ACTION;
13895
13896     if (IS_EM_GATE(element))
13897     {
13898       if (!player->key[EM_GATE_NR(element)])
13899         return MP_NO_ACTION;
13900     }
13901     else if (IS_EM_GATE_GRAY(element))
13902     {
13903       if (!player->key[EM_GATE_GRAY_NR(element)])
13904         return MP_NO_ACTION;
13905     }
13906     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13907     {
13908       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13909         return MP_NO_ACTION;
13910     }
13911     else if (IS_EMC_GATE(element))
13912     {
13913       if (!player->key[EMC_GATE_NR(element)])
13914         return MP_NO_ACTION;
13915     }
13916     else if (IS_EMC_GATE_GRAY(element))
13917     {
13918       if (!player->key[EMC_GATE_GRAY_NR(element)])
13919         return MP_NO_ACTION;
13920     }
13921     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13922     {
13923       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13924         return MP_NO_ACTION;
13925     }
13926     else if (element == EL_DC_GATE_WHITE ||
13927              element == EL_DC_GATE_WHITE_GRAY ||
13928              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13929     {
13930       if (player->num_white_keys == 0)
13931         return MP_NO_ACTION;
13932
13933       player->num_white_keys--;
13934     }
13935     else if (IS_SP_PORT(element))
13936     {
13937       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13938           element == EL_SP_GRAVITY_PORT_RIGHT ||
13939           element == EL_SP_GRAVITY_PORT_UP ||
13940           element == EL_SP_GRAVITY_PORT_DOWN)
13941         player->gravity = !player->gravity;
13942       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13943                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13944                element == EL_SP_GRAVITY_ON_PORT_UP ||
13945                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13946         player->gravity = TRUE;
13947       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13948                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13949                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13950                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13951         player->gravity = FALSE;
13952     }
13953
13954     // automatically move to the next field with double speed
13955     player->programmed_action = move_direction;
13956
13957     if (player->move_delay_reset_counter == 0)
13958     {
13959       player->move_delay_reset_counter = 2;     // two double speed steps
13960
13961       DOUBLE_PLAYER_SPEED(player);
13962     }
13963
13964     PlayLevelSoundAction(x, y, ACTION_PASSING);
13965   }
13966   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13967   {
13968     RemoveField(x, y);
13969
13970     if (mode != DF_SNAP)
13971     {
13972       GfxElement[x][y] = GFX_ELEMENT(element);
13973       player->is_digging = TRUE;
13974     }
13975
13976     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13977
13978     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13979                                         player->index_bit, dig_side);
13980
13981     if (mode == DF_SNAP)
13982     {
13983       if (level.block_snap_field)
13984         setFieldForSnapping(x, y, element, move_direction);
13985       else
13986         TestIfElementTouchesCustomElement(x, y);        // for empty space
13987
13988       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13989                                           player->index_bit, dig_side);
13990     }
13991   }
13992   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13993   {
13994     RemoveField(x, y);
13995
13996     if (is_player && mode != DF_SNAP)
13997     {
13998       GfxElement[x][y] = element;
13999       player->is_collecting = TRUE;
14000     }
14001
14002     if (element == EL_SPEED_PILL)
14003     {
14004       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14005     }
14006     else if (element == EL_EXTRA_TIME && level.time > 0)
14007     {
14008       TimeLeft += level.extra_time;
14009
14010       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14011
14012       DisplayGameControlValues();
14013     }
14014     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14015     {
14016       player->shield_normal_time_left += level.shield_normal_time;
14017       if (element == EL_SHIELD_DEADLY)
14018         player->shield_deadly_time_left += level.shield_deadly_time;
14019     }
14020     else if (element == EL_DYNAMITE ||
14021              element == EL_EM_DYNAMITE ||
14022              element == EL_SP_DISK_RED)
14023     {
14024       if (player->inventory_size < MAX_INVENTORY_SIZE)
14025         player->inventory_element[player->inventory_size++] = element;
14026
14027       DrawGameDoorValues();
14028     }
14029     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14030     {
14031       player->dynabomb_count++;
14032       player->dynabombs_left++;
14033     }
14034     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14035     {
14036       player->dynabomb_size++;
14037     }
14038     else if (element == EL_DYNABOMB_INCREASE_POWER)
14039     {
14040       player->dynabomb_xl = TRUE;
14041     }
14042     else if (IS_KEY(element))
14043     {
14044       player->key[KEY_NR(element)] = TRUE;
14045
14046       DrawGameDoorValues();
14047     }
14048     else if (element == EL_DC_KEY_WHITE)
14049     {
14050       player->num_white_keys++;
14051
14052       // display white keys?
14053       // DrawGameDoorValues();
14054     }
14055     else if (IS_ENVELOPE(element))
14056     {
14057       player->show_envelope = element;
14058     }
14059     else if (element == EL_EMC_LENSES)
14060     {
14061       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14062
14063       RedrawAllInvisibleElementsForLenses();
14064     }
14065     else if (element == EL_EMC_MAGNIFIER)
14066     {
14067       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14068
14069       RedrawAllInvisibleElementsForMagnifier();
14070     }
14071     else if (IS_DROPPABLE(element) ||
14072              IS_THROWABLE(element))     // can be collected and dropped
14073     {
14074       int i;
14075
14076       if (collect_count == 0)
14077         player->inventory_infinite_element = element;
14078       else
14079         for (i = 0; i < collect_count; i++)
14080           if (player->inventory_size < MAX_INVENTORY_SIZE)
14081             player->inventory_element[player->inventory_size++] = element;
14082
14083       DrawGameDoorValues();
14084     }
14085     else if (collect_count > 0)
14086     {
14087       game.gems_still_needed -= collect_count;
14088       if (game.gems_still_needed < 0)
14089         game.gems_still_needed = 0;
14090
14091       game.snapshot.collected_item = TRUE;
14092
14093       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14094
14095       DisplayGameControlValues();
14096     }
14097
14098     RaiseScoreElement(element);
14099     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14100
14101     if (is_player)
14102       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14103                                           player->index_bit, dig_side);
14104
14105     if (mode == DF_SNAP)
14106     {
14107       if (level.block_snap_field)
14108         setFieldForSnapping(x, y, element, move_direction);
14109       else
14110         TestIfElementTouchesCustomElement(x, y);        // for empty space
14111
14112       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14113                                           player->index_bit, dig_side);
14114     }
14115   }
14116   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14117   {
14118     if (mode == DF_SNAP && element != EL_BD_ROCK)
14119       return MP_NO_ACTION;
14120
14121     if (CAN_FALL(element) && dy)
14122       return MP_NO_ACTION;
14123
14124     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14125         !(element == EL_SPRING && level.use_spring_bug))
14126       return MP_NO_ACTION;
14127
14128     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14129         ((move_direction & MV_VERTICAL &&
14130           ((element_info[element].move_pattern & MV_LEFT &&
14131             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14132            (element_info[element].move_pattern & MV_RIGHT &&
14133             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14134          (move_direction & MV_HORIZONTAL &&
14135           ((element_info[element].move_pattern & MV_UP &&
14136             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14137            (element_info[element].move_pattern & MV_DOWN &&
14138             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14139       return MP_NO_ACTION;
14140
14141     // do not push elements already moving away faster than player
14142     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14143         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14144       return MP_NO_ACTION;
14145
14146     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14147     {
14148       if (player->push_delay_value == -1 || !player_was_pushing)
14149         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14150     }
14151     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14152     {
14153       if (player->push_delay_value == -1)
14154         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14155     }
14156     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14157     {
14158       if (!player->is_pushing)
14159         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14160     }
14161
14162     player->is_pushing = TRUE;
14163     player->is_active = TRUE;
14164
14165     if (!(IN_LEV_FIELD(nextx, nexty) &&
14166           (IS_FREE(nextx, nexty) ||
14167            (IS_SB_ELEMENT(element) &&
14168             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14169            (IS_CUSTOM_ELEMENT(element) &&
14170             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14171       return MP_NO_ACTION;
14172
14173     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14174       return MP_NO_ACTION;
14175
14176     if (player->push_delay == -1)       // new pushing; restart delay
14177       player->push_delay = 0;
14178
14179     if (player->push_delay < player->push_delay_value &&
14180         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14181         element != EL_SPRING && element != EL_BALLOON)
14182     {
14183       // make sure that there is no move delay before next try to push
14184       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14185         player->move_delay = 0;
14186
14187       return MP_NO_ACTION;
14188     }
14189
14190     if (IS_CUSTOM_ELEMENT(element) &&
14191         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14192     {
14193       if (!DigFieldByCE(nextx, nexty, element))
14194         return MP_NO_ACTION;
14195     }
14196
14197     if (IS_SB_ELEMENT(element))
14198     {
14199       boolean sokoban_task_solved = FALSE;
14200
14201       if (element == EL_SOKOBAN_FIELD_FULL)
14202       {
14203         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14204
14205         IncrementSokobanFieldsNeeded();
14206         IncrementSokobanObjectsNeeded();
14207       }
14208
14209       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14210       {
14211         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14212
14213         DecrementSokobanFieldsNeeded();
14214         DecrementSokobanObjectsNeeded();
14215
14216         // sokoban object was pushed from empty field to sokoban field
14217         if (Back[x][y] == EL_EMPTY)
14218           sokoban_task_solved = TRUE;
14219       }
14220
14221       Feld[x][y] = EL_SOKOBAN_OBJECT;
14222
14223       if (Back[x][y] == Back[nextx][nexty])
14224         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14225       else if (Back[x][y] != 0)
14226         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14227                                     ACTION_EMPTYING);
14228       else
14229         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14230                                     ACTION_FILLING);
14231
14232       if (sokoban_task_solved &&
14233           game.sokoban_fields_still_needed == 0 &&
14234           game.sokoban_objects_still_needed == 0 &&
14235           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14236       {
14237         game.players_still_needed = 0;
14238
14239         LevelSolved();
14240
14241         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14242       }
14243     }
14244     else
14245       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14246
14247     InitMovingField(x, y, move_direction);
14248     GfxAction[x][y] = ACTION_PUSHING;
14249
14250     if (mode == DF_SNAP)
14251       ContinueMoving(x, y);
14252     else
14253       MovPos[x][y] = (dx != 0 ? dx : dy);
14254
14255     Pushed[x][y] = TRUE;
14256     Pushed[nextx][nexty] = TRUE;
14257
14258     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14259       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14260     else
14261       player->push_delay_value = -1;    // get new value later
14262
14263     // check for element change _after_ element has been pushed
14264     if (game.use_change_when_pushing_bug)
14265     {
14266       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14267                                  player->index_bit, dig_side);
14268       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14269                                           player->index_bit, dig_side);
14270     }
14271   }
14272   else if (IS_SWITCHABLE(element))
14273   {
14274     if (PLAYER_SWITCHING(player, x, y))
14275     {
14276       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14277                                           player->index_bit, dig_side);
14278
14279       return MP_ACTION;
14280     }
14281
14282     player->is_switching = TRUE;
14283     player->switch_x = x;
14284     player->switch_y = y;
14285
14286     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14287
14288     if (element == EL_ROBOT_WHEEL)
14289     {
14290       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14291
14292       game.robot_wheel_x = x;
14293       game.robot_wheel_y = y;
14294       game.robot_wheel_active = TRUE;
14295
14296       TEST_DrawLevelField(x, y);
14297     }
14298     else if (element == EL_SP_TERMINAL)
14299     {
14300       int xx, yy;
14301
14302       SCAN_PLAYFIELD(xx, yy)
14303       {
14304         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14305         {
14306           Bang(xx, yy);
14307         }
14308         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14309         {
14310           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14311
14312           ResetGfxAnimation(xx, yy);
14313           TEST_DrawLevelField(xx, yy);
14314         }
14315       }
14316     }
14317     else if (IS_BELT_SWITCH(element))
14318     {
14319       ToggleBeltSwitch(x, y);
14320     }
14321     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14322              element == EL_SWITCHGATE_SWITCH_DOWN ||
14323              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14324              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14325     {
14326       ToggleSwitchgateSwitch(x, y);
14327     }
14328     else if (element == EL_LIGHT_SWITCH ||
14329              element == EL_LIGHT_SWITCH_ACTIVE)
14330     {
14331       ToggleLightSwitch(x, y);
14332     }
14333     else if (element == EL_TIMEGATE_SWITCH ||
14334              element == EL_DC_TIMEGATE_SWITCH)
14335     {
14336       ActivateTimegateSwitch(x, y);
14337     }
14338     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14339              element == EL_BALLOON_SWITCH_RIGHT ||
14340              element == EL_BALLOON_SWITCH_UP    ||
14341              element == EL_BALLOON_SWITCH_DOWN  ||
14342              element == EL_BALLOON_SWITCH_NONE  ||
14343              element == EL_BALLOON_SWITCH_ANY)
14344     {
14345       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14346                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14347                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14348                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14349                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14350                              move_direction);
14351     }
14352     else if (element == EL_LAMP)
14353     {
14354       Feld[x][y] = EL_LAMP_ACTIVE;
14355       game.lights_still_needed--;
14356
14357       ResetGfxAnimation(x, y);
14358       TEST_DrawLevelField(x, y);
14359     }
14360     else if (element == EL_TIME_ORB_FULL)
14361     {
14362       Feld[x][y] = EL_TIME_ORB_EMPTY;
14363
14364       if (level.time > 0 || level.use_time_orb_bug)
14365       {
14366         TimeLeft += level.time_orb_time;
14367         game.no_time_limit = FALSE;
14368
14369         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14370
14371         DisplayGameControlValues();
14372       }
14373
14374       ResetGfxAnimation(x, y);
14375       TEST_DrawLevelField(x, y);
14376     }
14377     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14378              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14379     {
14380       int xx, yy;
14381
14382       game.ball_active = !game.ball_active;
14383
14384       SCAN_PLAYFIELD(xx, yy)
14385       {
14386         int e = Feld[xx][yy];
14387
14388         if (game.ball_active)
14389         {
14390           if (e == EL_EMC_MAGIC_BALL)
14391             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14392           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14393             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14394         }
14395         else
14396         {
14397           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14398             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14399           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14400             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14401         }
14402       }
14403     }
14404
14405     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14406                                         player->index_bit, dig_side);
14407
14408     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14409                                         player->index_bit, dig_side);
14410
14411     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14412                                         player->index_bit, dig_side);
14413
14414     return MP_ACTION;
14415   }
14416   else
14417   {
14418     if (!PLAYER_SWITCHING(player, x, y))
14419     {
14420       player->is_switching = TRUE;
14421       player->switch_x = x;
14422       player->switch_y = y;
14423
14424       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14425                                  player->index_bit, dig_side);
14426       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14427                                           player->index_bit, dig_side);
14428
14429       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14430                                  player->index_bit, dig_side);
14431       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14432                                           player->index_bit, dig_side);
14433     }
14434
14435     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14436                                player->index_bit, dig_side);
14437     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14438                                         player->index_bit, dig_side);
14439
14440     return MP_NO_ACTION;
14441   }
14442
14443   player->push_delay = -1;
14444
14445   if (is_player)                // function can also be called by EL_PENGUIN
14446   {
14447     if (Feld[x][y] != element)          // really digged/collected something
14448     {
14449       player->is_collecting = !player->is_digging;
14450       player->is_active = TRUE;
14451     }
14452   }
14453
14454   return MP_MOVING;
14455 }
14456
14457 static boolean DigFieldByCE(int x, int y, int digging_element)
14458 {
14459   int element = Feld[x][y];
14460
14461   if (!IS_FREE(x, y))
14462   {
14463     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14464                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14465                   ACTION_BREAKING);
14466
14467     // no element can dig solid indestructible elements
14468     if (IS_INDESTRUCTIBLE(element) &&
14469         !IS_DIGGABLE(element) &&
14470         !IS_COLLECTIBLE(element))
14471       return FALSE;
14472
14473     if (AmoebaNr[x][y] &&
14474         (element == EL_AMOEBA_FULL ||
14475          element == EL_BD_AMOEBA ||
14476          element == EL_AMOEBA_GROWING))
14477     {
14478       AmoebaCnt[AmoebaNr[x][y]]--;
14479       AmoebaCnt2[AmoebaNr[x][y]]--;
14480     }
14481
14482     if (IS_MOVING(x, y))
14483       RemoveMovingField(x, y);
14484     else
14485     {
14486       RemoveField(x, y);
14487       TEST_DrawLevelField(x, y);
14488     }
14489
14490     // if digged element was about to explode, prevent the explosion
14491     ExplodeField[x][y] = EX_TYPE_NONE;
14492
14493     PlayLevelSoundAction(x, y, action);
14494   }
14495
14496   Store[x][y] = EL_EMPTY;
14497
14498   // this makes it possible to leave the removed element again
14499   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14500     Store[x][y] = element;
14501
14502   return TRUE;
14503 }
14504
14505 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14506 {
14507   int jx = player->jx, jy = player->jy;
14508   int x = jx + dx, y = jy + dy;
14509   int snap_direction = (dx == -1 ? MV_LEFT  :
14510                         dx == +1 ? MV_RIGHT :
14511                         dy == -1 ? MV_UP    :
14512                         dy == +1 ? MV_DOWN  : MV_NONE);
14513   boolean can_continue_snapping = (level.continuous_snapping &&
14514                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14515
14516   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14517     return FALSE;
14518
14519   if (!player->active || !IN_LEV_FIELD(x, y))
14520     return FALSE;
14521
14522   if (dx && dy)
14523     return FALSE;
14524
14525   if (!dx && !dy)
14526   {
14527     if (player->MovPos == 0)
14528       player->is_pushing = FALSE;
14529
14530     player->is_snapping = FALSE;
14531
14532     if (player->MovPos == 0)
14533     {
14534       player->is_moving = FALSE;
14535       player->is_digging = FALSE;
14536       player->is_collecting = FALSE;
14537     }
14538
14539     return FALSE;
14540   }
14541
14542   // prevent snapping with already pressed snap key when not allowed
14543   if (player->is_snapping && !can_continue_snapping)
14544     return FALSE;
14545
14546   player->MovDir = snap_direction;
14547
14548   if (player->MovPos == 0)
14549   {
14550     player->is_moving = FALSE;
14551     player->is_digging = FALSE;
14552     player->is_collecting = FALSE;
14553   }
14554
14555   player->is_dropping = FALSE;
14556   player->is_dropping_pressed = FALSE;
14557   player->drop_pressed_delay = 0;
14558
14559   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14560     return FALSE;
14561
14562   player->is_snapping = TRUE;
14563   player->is_active = TRUE;
14564
14565   if (player->MovPos == 0)
14566   {
14567     player->is_moving = FALSE;
14568     player->is_digging = FALSE;
14569     player->is_collecting = FALSE;
14570   }
14571
14572   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14573     TEST_DrawLevelField(player->last_jx, player->last_jy);
14574
14575   TEST_DrawLevelField(x, y);
14576
14577   return TRUE;
14578 }
14579
14580 static boolean DropElement(struct PlayerInfo *player)
14581 {
14582   int old_element, new_element;
14583   int dropx = player->jx, dropy = player->jy;
14584   int drop_direction = player->MovDir;
14585   int drop_side = drop_direction;
14586   int drop_element = get_next_dropped_element(player);
14587
14588   /* do not drop an element on top of another element; when holding drop key
14589      pressed without moving, dropped element must move away before the next
14590      element can be dropped (this is especially important if the next element
14591      is dynamite, which can be placed on background for historical reasons) */
14592   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14593     return MP_ACTION;
14594
14595   if (IS_THROWABLE(drop_element))
14596   {
14597     dropx += GET_DX_FROM_DIR(drop_direction);
14598     dropy += GET_DY_FROM_DIR(drop_direction);
14599
14600     if (!IN_LEV_FIELD(dropx, dropy))
14601       return FALSE;
14602   }
14603
14604   old_element = Feld[dropx][dropy];     // old element at dropping position
14605   new_element = drop_element;           // default: no change when dropping
14606
14607   // check if player is active, not moving and ready to drop
14608   if (!player->active || player->MovPos || player->drop_delay > 0)
14609     return FALSE;
14610
14611   // check if player has anything that can be dropped
14612   if (new_element == EL_UNDEFINED)
14613     return FALSE;
14614
14615   // only set if player has anything that can be dropped
14616   player->is_dropping_pressed = TRUE;
14617
14618   // check if drop key was pressed long enough for EM style dynamite
14619   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14620     return FALSE;
14621
14622   // check if anything can be dropped at the current position
14623   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14624     return FALSE;
14625
14626   // collected custom elements can only be dropped on empty fields
14627   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14628     return FALSE;
14629
14630   if (old_element != EL_EMPTY)
14631     Back[dropx][dropy] = old_element;   // store old element on this field
14632
14633   ResetGfxAnimation(dropx, dropy);
14634   ResetRandomAnimationValue(dropx, dropy);
14635
14636   if (player->inventory_size > 0 ||
14637       player->inventory_infinite_element != EL_UNDEFINED)
14638   {
14639     if (player->inventory_size > 0)
14640     {
14641       player->inventory_size--;
14642
14643       DrawGameDoorValues();
14644
14645       if (new_element == EL_DYNAMITE)
14646         new_element = EL_DYNAMITE_ACTIVE;
14647       else if (new_element == EL_EM_DYNAMITE)
14648         new_element = EL_EM_DYNAMITE_ACTIVE;
14649       else if (new_element == EL_SP_DISK_RED)
14650         new_element = EL_SP_DISK_RED_ACTIVE;
14651     }
14652
14653     Feld[dropx][dropy] = new_element;
14654
14655     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14656       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14657                           el2img(Feld[dropx][dropy]), 0);
14658
14659     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14660
14661     // needed if previous element just changed to "empty" in the last frame
14662     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14663
14664     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14665                                player->index_bit, drop_side);
14666     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14667                                         CE_PLAYER_DROPS_X,
14668                                         player->index_bit, drop_side);
14669
14670     TestIfElementTouchesCustomElement(dropx, dropy);
14671   }
14672   else          // player is dropping a dyna bomb
14673   {
14674     player->dynabombs_left--;
14675
14676     Feld[dropx][dropy] = new_element;
14677
14678     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14679       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14680                           el2img(Feld[dropx][dropy]), 0);
14681
14682     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14683   }
14684
14685   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14686     InitField_WithBug1(dropx, dropy, FALSE);
14687
14688   new_element = Feld[dropx][dropy];     // element might have changed
14689
14690   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14691       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14692   {
14693     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14694       MovDir[dropx][dropy] = drop_direction;
14695
14696     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14697
14698     // do not cause impact style collision by dropping elements that can fall
14699     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14700   }
14701
14702   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14703   player->is_dropping = TRUE;
14704
14705   player->drop_pressed_delay = 0;
14706   player->is_dropping_pressed = FALSE;
14707
14708   player->drop_x = dropx;
14709   player->drop_y = dropy;
14710
14711   return TRUE;
14712 }
14713
14714 // ----------------------------------------------------------------------------
14715 // game sound playing functions
14716 // ----------------------------------------------------------------------------
14717
14718 static int *loop_sound_frame = NULL;
14719 static int *loop_sound_volume = NULL;
14720
14721 void InitPlayLevelSound(void)
14722 {
14723   int num_sounds = getSoundListSize();
14724
14725   checked_free(loop_sound_frame);
14726   checked_free(loop_sound_volume);
14727
14728   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14729   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14730 }
14731
14732 static void PlayLevelSound(int x, int y, int nr)
14733 {
14734   int sx = SCREENX(x), sy = SCREENY(y);
14735   int volume, stereo_position;
14736   int max_distance = 8;
14737   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14738
14739   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14740       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14741     return;
14742
14743   if (!IN_LEV_FIELD(x, y) ||
14744       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14745       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14746     return;
14747
14748   volume = SOUND_MAX_VOLUME;
14749
14750   if (!IN_SCR_FIELD(sx, sy))
14751   {
14752     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14753     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14754
14755     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14756   }
14757
14758   stereo_position = (SOUND_MAX_LEFT +
14759                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14760                      (SCR_FIELDX + 2 * max_distance));
14761
14762   if (IS_LOOP_SOUND(nr))
14763   {
14764     /* This assures that quieter loop sounds do not overwrite louder ones,
14765        while restarting sound volume comparison with each new game frame. */
14766
14767     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14768       return;
14769
14770     loop_sound_volume[nr] = volume;
14771     loop_sound_frame[nr] = FrameCounter;
14772   }
14773
14774   PlaySoundExt(nr, volume, stereo_position, type);
14775 }
14776
14777 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14778 {
14779   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14780                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14781                  y < LEVELY(BY1) ? LEVELY(BY1) :
14782                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14783                  sound_action);
14784 }
14785
14786 static void PlayLevelSoundAction(int x, int y, int action)
14787 {
14788   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14789 }
14790
14791 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14792 {
14793   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14794
14795   if (sound_effect != SND_UNDEFINED)
14796     PlayLevelSound(x, y, sound_effect);
14797 }
14798
14799 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14800                                               int action)
14801 {
14802   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14803
14804   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14805     PlayLevelSound(x, y, sound_effect);
14806 }
14807
14808 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14809 {
14810   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14811
14812   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14813     PlayLevelSound(x, y, sound_effect);
14814 }
14815
14816 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14817 {
14818   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14819
14820   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14821     StopSound(sound_effect);
14822 }
14823
14824 static int getLevelMusicNr(void)
14825 {
14826   if (levelset.music[level_nr] != MUS_UNDEFINED)
14827     return levelset.music[level_nr];            // from config file
14828   else
14829     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14830 }
14831
14832 static void FadeLevelSounds(void)
14833 {
14834   FadeSounds();
14835 }
14836
14837 static void FadeLevelMusic(void)
14838 {
14839   int music_nr = getLevelMusicNr();
14840   char *curr_music = getCurrentlyPlayingMusicFilename();
14841   char *next_music = getMusicInfoEntryFilename(music_nr);
14842
14843   if (!strEqual(curr_music, next_music))
14844     FadeMusic();
14845 }
14846
14847 void FadeLevelSoundsAndMusic(void)
14848 {
14849   FadeLevelSounds();
14850   FadeLevelMusic();
14851 }
14852
14853 static void PlayLevelMusic(void)
14854 {
14855   int music_nr = getLevelMusicNr();
14856   char *curr_music = getCurrentlyPlayingMusicFilename();
14857   char *next_music = getMusicInfoEntryFilename(music_nr);
14858
14859   if (!strEqual(curr_music, next_music))
14860     PlayMusicLoop(music_nr);
14861 }
14862
14863 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14864 {
14865   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14866   int offset = 0;
14867   int x = xx - offset;
14868   int y = yy - offset;
14869
14870   switch (sample)
14871   {
14872     case SOUND_blank:
14873       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14874       break;
14875
14876     case SOUND_roll:
14877       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14878       break;
14879
14880     case SOUND_stone:
14881       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14882       break;
14883
14884     case SOUND_nut:
14885       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14886       break;
14887
14888     case SOUND_crack:
14889       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14890       break;
14891
14892     case SOUND_bug:
14893       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14894       break;
14895
14896     case SOUND_tank:
14897       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14898       break;
14899
14900     case SOUND_android_clone:
14901       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14902       break;
14903
14904     case SOUND_android_move:
14905       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14906       break;
14907
14908     case SOUND_spring:
14909       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14910       break;
14911
14912     case SOUND_slurp:
14913       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14914       break;
14915
14916     case SOUND_eater:
14917       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14918       break;
14919
14920     case SOUND_eater_eat:
14921       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14922       break;
14923
14924     case SOUND_alien:
14925       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14926       break;
14927
14928     case SOUND_collect:
14929       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14930       break;
14931
14932     case SOUND_diamond:
14933       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14934       break;
14935
14936     case SOUND_squash:
14937       // !!! CHECK THIS !!!
14938 #if 1
14939       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14940 #else
14941       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14942 #endif
14943       break;
14944
14945     case SOUND_wonderfall:
14946       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14947       break;
14948
14949     case SOUND_drip:
14950       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14951       break;
14952
14953     case SOUND_push:
14954       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14955       break;
14956
14957     case SOUND_dirt:
14958       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14959       break;
14960
14961     case SOUND_acid:
14962       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14963       break;
14964
14965     case SOUND_ball:
14966       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14967       break;
14968
14969     case SOUND_slide:
14970       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14971       break;
14972
14973     case SOUND_wonder:
14974       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14975       break;
14976
14977     case SOUND_door:
14978       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14979       break;
14980
14981     case SOUND_exit_open:
14982       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14983       break;
14984
14985     case SOUND_exit_leave:
14986       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14987       break;
14988
14989     case SOUND_dynamite:
14990       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14991       break;
14992
14993     case SOUND_tick:
14994       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14995       break;
14996
14997     case SOUND_press:
14998       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14999       break;
15000
15001     case SOUND_wheel:
15002       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15003       break;
15004
15005     case SOUND_boom:
15006       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15007       break;
15008
15009     case SOUND_die:
15010       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15011       break;
15012
15013     case SOUND_time:
15014       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15015       break;
15016
15017     default:
15018       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15019       break;
15020   }
15021 }
15022
15023 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15024 {
15025   int element = map_element_SP_to_RND(element_sp);
15026   int action = map_action_SP_to_RND(action_sp);
15027   int offset = (setup.sp_show_border_elements ? 0 : 1);
15028   int x = xx - offset;
15029   int y = yy - offset;
15030
15031   PlayLevelSoundElementAction(x, y, element, action);
15032 }
15033
15034 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15035 {
15036   int element = map_element_MM_to_RND(element_mm);
15037   int action = map_action_MM_to_RND(action_mm);
15038   int offset = 0;
15039   int x = xx - offset;
15040   int y = yy - offset;
15041
15042   if (!IS_MM_ELEMENT(element))
15043     element = EL_MM_DEFAULT;
15044
15045   PlayLevelSoundElementAction(x, y, element, action);
15046 }
15047
15048 void PlaySound_MM(int sound_mm)
15049 {
15050   int sound = map_sound_MM_to_RND(sound_mm);
15051
15052   if (sound == SND_UNDEFINED)
15053     return;
15054
15055   PlaySound(sound);
15056 }
15057
15058 void PlaySoundLoop_MM(int sound_mm)
15059 {
15060   int sound = map_sound_MM_to_RND(sound_mm);
15061
15062   if (sound == SND_UNDEFINED)
15063     return;
15064
15065   PlaySoundLoop(sound);
15066 }
15067
15068 void StopSound_MM(int sound_mm)
15069 {
15070   int sound = map_sound_MM_to_RND(sound_mm);
15071
15072   if (sound == SND_UNDEFINED)
15073     return;
15074
15075   StopSound(sound);
15076 }
15077
15078 void RaiseScore(int value)
15079 {
15080   game.score += value;
15081
15082   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15083
15084   DisplayGameControlValues();
15085 }
15086
15087 void RaiseScoreElement(int element)
15088 {
15089   switch (element)
15090   {
15091     case EL_EMERALD:
15092     case EL_BD_DIAMOND:
15093     case EL_EMERALD_YELLOW:
15094     case EL_EMERALD_RED:
15095     case EL_EMERALD_PURPLE:
15096     case EL_SP_INFOTRON:
15097       RaiseScore(level.score[SC_EMERALD]);
15098       break;
15099     case EL_DIAMOND:
15100       RaiseScore(level.score[SC_DIAMOND]);
15101       break;
15102     case EL_CRYSTAL:
15103       RaiseScore(level.score[SC_CRYSTAL]);
15104       break;
15105     case EL_PEARL:
15106       RaiseScore(level.score[SC_PEARL]);
15107       break;
15108     case EL_BUG:
15109     case EL_BD_BUTTERFLY:
15110     case EL_SP_ELECTRON:
15111       RaiseScore(level.score[SC_BUG]);
15112       break;
15113     case EL_SPACESHIP:
15114     case EL_BD_FIREFLY:
15115     case EL_SP_SNIKSNAK:
15116       RaiseScore(level.score[SC_SPACESHIP]);
15117       break;
15118     case EL_YAMYAM:
15119     case EL_DARK_YAMYAM:
15120       RaiseScore(level.score[SC_YAMYAM]);
15121       break;
15122     case EL_ROBOT:
15123       RaiseScore(level.score[SC_ROBOT]);
15124       break;
15125     case EL_PACMAN:
15126       RaiseScore(level.score[SC_PACMAN]);
15127       break;
15128     case EL_NUT:
15129       RaiseScore(level.score[SC_NUT]);
15130       break;
15131     case EL_DYNAMITE:
15132     case EL_EM_DYNAMITE:
15133     case EL_SP_DISK_RED:
15134     case EL_DYNABOMB_INCREASE_NUMBER:
15135     case EL_DYNABOMB_INCREASE_SIZE:
15136     case EL_DYNABOMB_INCREASE_POWER:
15137       RaiseScore(level.score[SC_DYNAMITE]);
15138       break;
15139     case EL_SHIELD_NORMAL:
15140     case EL_SHIELD_DEADLY:
15141       RaiseScore(level.score[SC_SHIELD]);
15142       break;
15143     case EL_EXTRA_TIME:
15144       RaiseScore(level.extra_time_score);
15145       break;
15146     case EL_KEY_1:
15147     case EL_KEY_2:
15148     case EL_KEY_3:
15149     case EL_KEY_4:
15150     case EL_EM_KEY_1:
15151     case EL_EM_KEY_2:
15152     case EL_EM_KEY_3:
15153     case EL_EM_KEY_4:
15154     case EL_EMC_KEY_5:
15155     case EL_EMC_KEY_6:
15156     case EL_EMC_KEY_7:
15157     case EL_EMC_KEY_8:
15158     case EL_DC_KEY_WHITE:
15159       RaiseScore(level.score[SC_KEY]);
15160       break;
15161     default:
15162       RaiseScore(element_info[element].collect_score);
15163       break;
15164   }
15165 }
15166
15167 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15168 {
15169   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15170   {
15171     // closing door required in case of envelope style request dialogs
15172     if (!skip_request)
15173     {
15174       // prevent short reactivation of overlay buttons while closing door
15175       SetOverlayActive(FALSE);
15176
15177       CloseDoor(DOOR_CLOSE_1);
15178     }
15179
15180     if (network.enabled)
15181       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15182     else
15183     {
15184       if (quick_quit)
15185         FadeSkipNextFadeIn();
15186
15187       SetGameStatus(GAME_MODE_MAIN);
15188
15189       DrawMainMenu();
15190     }
15191   }
15192   else          // continue playing the game
15193   {
15194     if (tape.playing && tape.deactivate_display)
15195       TapeDeactivateDisplayOff(TRUE);
15196
15197     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15198
15199     if (tape.playing && tape.deactivate_display)
15200       TapeDeactivateDisplayOn();
15201   }
15202 }
15203
15204 void RequestQuitGame(boolean ask_if_really_quit)
15205 {
15206   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15207   boolean skip_request = game.all_players_gone || quick_quit;
15208
15209   RequestQuitGameExt(skip_request, quick_quit,
15210                      "Do you really want to quit the game?");
15211 }
15212
15213 void RequestRestartGame(char *message)
15214 {
15215   game.restart_game_message = NULL;
15216
15217   boolean has_started_game = hasStartedNetworkGame();
15218   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15219
15220   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15221   {
15222     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15223   }
15224   else
15225   {
15226     SetGameStatus(GAME_MODE_MAIN);
15227
15228     DrawMainMenu();
15229   }
15230 }
15231
15232 void CheckGameOver(void)
15233 {
15234   static boolean last_game_over = FALSE;
15235   static int game_over_delay = 0;
15236   int game_over_delay_value = 50;
15237   boolean game_over = checkGameFailed();
15238
15239   // do not handle game over if request dialog is already active
15240   if (game.request_active)
15241     return;
15242
15243   // do not ask to play again if game was never actually played
15244   if (!game.GamePlayed)
15245     return;
15246
15247   if (!game_over)
15248   {
15249     last_game_over = FALSE;
15250     game_over_delay = game_over_delay_value;
15251
15252     return;
15253   }
15254
15255   if (game_over_delay > 0)
15256   {
15257     game_over_delay--;
15258
15259     return;
15260   }
15261
15262   if (last_game_over != game_over)
15263     game.restart_game_message = (hasStartedNetworkGame() ?
15264                                  "Game over! Play it again?" :
15265                                  "Game over!");
15266
15267   last_game_over = game_over;
15268 }
15269
15270 boolean checkGameSolved(void)
15271 {
15272   // set for all game engines if level was solved
15273   return game.LevelSolved_GameEnd;
15274 }
15275
15276 boolean checkGameFailed(void)
15277 {
15278   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15279     return (game_em.game_over && !game_em.level_solved);
15280   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15281     return (game_sp.game_over && !game_sp.level_solved);
15282   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15283     return (game_mm.game_over && !game_mm.level_solved);
15284   else                          // GAME_ENGINE_TYPE_RND
15285     return (game.GameOver && !game.LevelSolved);
15286 }
15287
15288 boolean checkGameEnded(void)
15289 {
15290   return (checkGameSolved() || checkGameFailed());
15291 }
15292
15293
15294 // ----------------------------------------------------------------------------
15295 // random generator functions
15296 // ----------------------------------------------------------------------------
15297
15298 unsigned int InitEngineRandom_RND(int seed)
15299 {
15300   game.num_random_calls = 0;
15301
15302   return InitEngineRandom(seed);
15303 }
15304
15305 unsigned int RND(int max)
15306 {
15307   if (max > 0)
15308   {
15309     game.num_random_calls++;
15310
15311     return GetEngineRandom(max);
15312   }
15313
15314   return 0;
15315 }
15316
15317
15318 // ----------------------------------------------------------------------------
15319 // game engine snapshot handling functions
15320 // ----------------------------------------------------------------------------
15321
15322 struct EngineSnapshotInfo
15323 {
15324   // runtime values for custom element collect score
15325   int collect_score[NUM_CUSTOM_ELEMENTS];
15326
15327   // runtime values for group element choice position
15328   int choice_pos[NUM_GROUP_ELEMENTS];
15329
15330   // runtime values for belt position animations
15331   int belt_graphic[4][NUM_BELT_PARTS];
15332   int belt_anim_mode[4][NUM_BELT_PARTS];
15333 };
15334
15335 static struct EngineSnapshotInfo engine_snapshot_rnd;
15336 static char *snapshot_level_identifier = NULL;
15337 static int snapshot_level_nr = -1;
15338
15339 static void SaveEngineSnapshotValues_RND(void)
15340 {
15341   static int belt_base_active_element[4] =
15342   {
15343     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15344     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15345     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15346     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15347   };
15348   int i, j;
15349
15350   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15351   {
15352     int element = EL_CUSTOM_START + i;
15353
15354     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15355   }
15356
15357   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15358   {
15359     int element = EL_GROUP_START + i;
15360
15361     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15362   }
15363
15364   for (i = 0; i < 4; i++)
15365   {
15366     for (j = 0; j < NUM_BELT_PARTS; j++)
15367     {
15368       int element = belt_base_active_element[i] + j;
15369       int graphic = el2img(element);
15370       int anim_mode = graphic_info[graphic].anim_mode;
15371
15372       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15373       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15374     }
15375   }
15376 }
15377
15378 static void LoadEngineSnapshotValues_RND(void)
15379 {
15380   unsigned int num_random_calls = game.num_random_calls;
15381   int i, j;
15382
15383   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15384   {
15385     int element = EL_CUSTOM_START + i;
15386
15387     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15388   }
15389
15390   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15391   {
15392     int element = EL_GROUP_START + i;
15393
15394     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15395   }
15396
15397   for (i = 0; i < 4; i++)
15398   {
15399     for (j = 0; j < NUM_BELT_PARTS; j++)
15400     {
15401       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15402       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15403
15404       graphic_info[graphic].anim_mode = anim_mode;
15405     }
15406   }
15407
15408   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15409   {
15410     InitRND(tape.random_seed);
15411     for (i = 0; i < num_random_calls; i++)
15412       RND(1);
15413   }
15414
15415   if (game.num_random_calls != num_random_calls)
15416   {
15417     Error(ERR_INFO, "number of random calls out of sync");
15418     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15419     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15420     Error(ERR_EXIT, "this should not happen -- please debug");
15421   }
15422 }
15423
15424 void FreeEngineSnapshotSingle(void)
15425 {
15426   FreeSnapshotSingle();
15427
15428   setString(&snapshot_level_identifier, NULL);
15429   snapshot_level_nr = -1;
15430 }
15431
15432 void FreeEngineSnapshotList(void)
15433 {
15434   FreeSnapshotList();
15435 }
15436
15437 static ListNode *SaveEngineSnapshotBuffers(void)
15438 {
15439   ListNode *buffers = NULL;
15440
15441   // copy some special values to a structure better suited for the snapshot
15442
15443   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15444     SaveEngineSnapshotValues_RND();
15445   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15446     SaveEngineSnapshotValues_EM();
15447   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15448     SaveEngineSnapshotValues_SP(&buffers);
15449   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15450     SaveEngineSnapshotValues_MM(&buffers);
15451
15452   // save values stored in special snapshot structure
15453
15454   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15455     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15456   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15457     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15458   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15459     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15460   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15461     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15462
15463   // save further RND engine values
15464
15465   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15466   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15467   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15468
15469   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15470   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15471   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15472   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15473   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15474
15475   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15476   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15477   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15478
15479   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15480
15481   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15482   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15483
15484   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15485   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15486   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15487   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15488   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15489   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15490   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15491   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15492   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15493   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15494   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15495   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15496   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15497   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15498   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15499   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15500   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15501   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15502
15503   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15504   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15505
15506   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15507   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15508   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15509
15510   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15511   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15512
15513   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15514   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15515   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15516   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15517   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15518
15519   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15520   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15521
15522 #if 0
15523   ListNode *node = engine_snapshot_list_rnd;
15524   int num_bytes = 0;
15525
15526   while (node != NULL)
15527   {
15528     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15529
15530     node = node->next;
15531   }
15532
15533   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15534 #endif
15535
15536   return buffers;
15537 }
15538
15539 void SaveEngineSnapshotSingle(void)
15540 {
15541   ListNode *buffers = SaveEngineSnapshotBuffers();
15542
15543   // finally save all snapshot buffers to single snapshot
15544   SaveSnapshotSingle(buffers);
15545
15546   // save level identification information
15547   setString(&snapshot_level_identifier, leveldir_current->identifier);
15548   snapshot_level_nr = level_nr;
15549 }
15550
15551 boolean CheckSaveEngineSnapshotToList(void)
15552 {
15553   boolean save_snapshot =
15554     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15555      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15556       game.snapshot.changed_action) ||
15557      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15558       game.snapshot.collected_item));
15559
15560   game.snapshot.changed_action = FALSE;
15561   game.snapshot.collected_item = FALSE;
15562   game.snapshot.save_snapshot = save_snapshot;
15563
15564   return save_snapshot;
15565 }
15566
15567 void SaveEngineSnapshotToList(void)
15568 {
15569   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15570       tape.quick_resume)
15571     return;
15572
15573   ListNode *buffers = SaveEngineSnapshotBuffers();
15574
15575   // finally save all snapshot buffers to snapshot list
15576   SaveSnapshotToList(buffers);
15577 }
15578
15579 void SaveEngineSnapshotToListInitial(void)
15580 {
15581   FreeEngineSnapshotList();
15582
15583   SaveEngineSnapshotToList();
15584 }
15585
15586 static void LoadEngineSnapshotValues(void)
15587 {
15588   // restore special values from snapshot structure
15589
15590   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15591     LoadEngineSnapshotValues_RND();
15592   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15593     LoadEngineSnapshotValues_EM();
15594   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15595     LoadEngineSnapshotValues_SP();
15596   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15597     LoadEngineSnapshotValues_MM();
15598 }
15599
15600 void LoadEngineSnapshotSingle(void)
15601 {
15602   LoadSnapshotSingle();
15603
15604   LoadEngineSnapshotValues();
15605 }
15606
15607 static void LoadEngineSnapshot_Undo(int steps)
15608 {
15609   LoadSnapshotFromList_Older(steps);
15610
15611   LoadEngineSnapshotValues();
15612 }
15613
15614 static void LoadEngineSnapshot_Redo(int steps)
15615 {
15616   LoadSnapshotFromList_Newer(steps);
15617
15618   LoadEngineSnapshotValues();
15619 }
15620
15621 boolean CheckEngineSnapshotSingle(void)
15622 {
15623   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15624           snapshot_level_nr == level_nr);
15625 }
15626
15627 boolean CheckEngineSnapshotList(void)
15628 {
15629   return CheckSnapshotList();
15630 }
15631
15632
15633 // ---------- new game button stuff -------------------------------------------
15634
15635 static struct
15636 {
15637   int graphic;
15638   struct XY *pos;
15639   int gadget_id;
15640   boolean *setup_value;
15641   boolean allowed_on_tape;
15642   boolean is_touch_button;
15643   char *infotext;
15644 } gamebutton_info[NUM_GAME_BUTTONS] =
15645 {
15646   {
15647     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15648     GAME_CTRL_ID_STOP,                          NULL,
15649     TRUE, FALSE,                                "stop game"
15650   },
15651   {
15652     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15653     GAME_CTRL_ID_PAUSE,                         NULL,
15654     TRUE, FALSE,                                "pause game"
15655   },
15656   {
15657     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15658     GAME_CTRL_ID_PLAY,                          NULL,
15659     TRUE, FALSE,                                "play game"
15660   },
15661   {
15662     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15663     GAME_CTRL_ID_UNDO,                          NULL,
15664     TRUE, FALSE,                                "undo step"
15665   },
15666   {
15667     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15668     GAME_CTRL_ID_REDO,                          NULL,
15669     TRUE, FALSE,                                "redo step"
15670   },
15671   {
15672     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15673     GAME_CTRL_ID_SAVE,                          NULL,
15674     TRUE, FALSE,                                "save game"
15675   },
15676   {
15677     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15678     GAME_CTRL_ID_PAUSE2,                        NULL,
15679     TRUE, FALSE,                                "pause game"
15680   },
15681   {
15682     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15683     GAME_CTRL_ID_LOAD,                          NULL,
15684     TRUE, FALSE,                                "load game"
15685   },
15686   {
15687     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15688     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15689     FALSE, FALSE,                               "stop game"
15690   },
15691   {
15692     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15693     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15694     FALSE, FALSE,                               "pause game"
15695   },
15696   {
15697     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15698     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15699     FALSE, FALSE,                               "play game"
15700   },
15701   {
15702     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15703     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15704     FALSE, TRUE,                                "stop game"
15705   },
15706   {
15707     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15708     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15709     FALSE, TRUE,                                "pause game"
15710   },
15711   {
15712     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15713     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15714     TRUE, FALSE,                                "background music on/off"
15715   },
15716   {
15717     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15718     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15719     TRUE, FALSE,                                "sound loops on/off"
15720   },
15721   {
15722     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15723     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15724     TRUE, FALSE,                                "normal sounds on/off"
15725   },
15726   {
15727     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15728     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15729     FALSE, FALSE,                               "background music on/off"
15730   },
15731   {
15732     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15733     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15734     FALSE, FALSE,                               "sound loops on/off"
15735   },
15736   {
15737     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15738     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15739     FALSE, FALSE,                               "normal sounds on/off"
15740   }
15741 };
15742
15743 void CreateGameButtons(void)
15744 {
15745   int i;
15746
15747   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15748   {
15749     int graphic = gamebutton_info[i].graphic;
15750     struct GraphicInfo *gfx = &graphic_info[graphic];
15751     struct XY *pos = gamebutton_info[i].pos;
15752     struct GadgetInfo *gi;
15753     int button_type;
15754     boolean checked;
15755     unsigned int event_mask;
15756     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15757     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15758     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15759     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15760     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15761     int gd_x   = gfx->src_x;
15762     int gd_y   = gfx->src_y;
15763     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15764     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15765     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15766     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15767     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15768     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15769     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15770     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15771     int id = i;
15772
15773     if (gfx->bitmap == NULL)
15774     {
15775       game_gadget[id] = NULL;
15776
15777       continue;
15778     }
15779
15780     if (id == GAME_CTRL_ID_STOP ||
15781         id == GAME_CTRL_ID_PANEL_STOP ||
15782         id == GAME_CTRL_ID_TOUCH_STOP ||
15783         id == GAME_CTRL_ID_PLAY ||
15784         id == GAME_CTRL_ID_PANEL_PLAY ||
15785         id == GAME_CTRL_ID_SAVE ||
15786         id == GAME_CTRL_ID_LOAD)
15787     {
15788       button_type = GD_TYPE_NORMAL_BUTTON;
15789       checked = FALSE;
15790       event_mask = GD_EVENT_RELEASED;
15791     }
15792     else if (id == GAME_CTRL_ID_UNDO ||
15793              id == GAME_CTRL_ID_REDO)
15794     {
15795       button_type = GD_TYPE_NORMAL_BUTTON;
15796       checked = FALSE;
15797       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15798     }
15799     else
15800     {
15801       button_type = GD_TYPE_CHECK_BUTTON;
15802       checked = (gamebutton_info[i].setup_value != NULL ?
15803                  *gamebutton_info[i].setup_value : FALSE);
15804       event_mask = GD_EVENT_PRESSED;
15805     }
15806
15807     gi = CreateGadget(GDI_CUSTOM_ID, id,
15808                       GDI_IMAGE_ID, graphic,
15809                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15810                       GDI_X, base_x + x,
15811                       GDI_Y, base_y + y,
15812                       GDI_WIDTH, gfx->width,
15813                       GDI_HEIGHT, gfx->height,
15814                       GDI_TYPE, button_type,
15815                       GDI_STATE, GD_BUTTON_UNPRESSED,
15816                       GDI_CHECKED, checked,
15817                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15818                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15819                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15820                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15821                       GDI_DIRECT_DRAW, FALSE,
15822                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15823                       GDI_EVENT_MASK, event_mask,
15824                       GDI_CALLBACK_ACTION, HandleGameButtons,
15825                       GDI_END);
15826
15827     if (gi == NULL)
15828       Error(ERR_EXIT, "cannot create gadget");
15829
15830     game_gadget[id] = gi;
15831   }
15832 }
15833
15834 void FreeGameButtons(void)
15835 {
15836   int i;
15837
15838   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15839     FreeGadget(game_gadget[i]);
15840 }
15841
15842 static void UnmapGameButtonsAtSamePosition(int id)
15843 {
15844   int i;
15845
15846   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15847     if (i != id &&
15848         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15849         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15850       UnmapGadget(game_gadget[i]);
15851 }
15852
15853 static void UnmapGameButtonsAtSamePosition_All(void)
15854 {
15855   if (setup.show_snapshot_buttons)
15856   {
15857     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15858     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15859     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15860   }
15861   else
15862   {
15863     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15864     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15865     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15866
15867     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15868     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15869     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15870   }
15871 }
15872
15873 static void MapGameButtonsAtSamePosition(int id)
15874 {
15875   int i;
15876
15877   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15878     if (i != id &&
15879         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15880         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15881       MapGadget(game_gadget[i]);
15882
15883   UnmapGameButtonsAtSamePosition_All();
15884 }
15885
15886 void MapUndoRedoButtons(void)
15887 {
15888   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15889   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15890
15891   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15892   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15893 }
15894
15895 void UnmapUndoRedoButtons(void)
15896 {
15897   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15898   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15899
15900   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15901   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15902 }
15903
15904 void ModifyPauseButtons(void)
15905 {
15906   static int ids[] =
15907   {
15908     GAME_CTRL_ID_PAUSE,
15909     GAME_CTRL_ID_PAUSE2,
15910     GAME_CTRL_ID_PANEL_PAUSE,
15911     GAME_CTRL_ID_TOUCH_PAUSE,
15912     -1
15913   };
15914   int i;
15915
15916   for (i = 0; ids[i] > -1; i++)
15917     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15918 }
15919
15920 static void MapGameButtonsExt(boolean on_tape)
15921 {
15922   int i;
15923
15924   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15925     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15926         i != GAME_CTRL_ID_UNDO &&
15927         i != GAME_CTRL_ID_REDO)
15928       MapGadget(game_gadget[i]);
15929
15930   UnmapGameButtonsAtSamePosition_All();
15931
15932   RedrawGameButtons();
15933 }
15934
15935 static void UnmapGameButtonsExt(boolean on_tape)
15936 {
15937   int i;
15938
15939   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15940     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15941       UnmapGadget(game_gadget[i]);
15942 }
15943
15944 static void RedrawGameButtonsExt(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       RedrawGadget(game_gadget[i]);
15951 }
15952
15953 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15954 {
15955   if (gi == NULL)
15956     return;
15957
15958   gi->checked = state;
15959 }
15960
15961 static void RedrawSoundButtonGadget(int id)
15962 {
15963   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15964              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15965              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15966              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15967              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15968              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15969              id);
15970
15971   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15972   RedrawGadget(game_gadget[id2]);
15973 }
15974
15975 void MapGameButtons(void)
15976 {
15977   MapGameButtonsExt(FALSE);
15978 }
15979
15980 void UnmapGameButtons(void)
15981 {
15982   UnmapGameButtonsExt(FALSE);
15983 }
15984
15985 void RedrawGameButtons(void)
15986 {
15987   RedrawGameButtonsExt(FALSE);
15988 }
15989
15990 void MapGameButtonsOnTape(void)
15991 {
15992   MapGameButtonsExt(TRUE);
15993 }
15994
15995 void UnmapGameButtonsOnTape(void)
15996 {
15997   UnmapGameButtonsExt(TRUE);
15998 }
15999
16000 void RedrawGameButtonsOnTape(void)
16001 {
16002   RedrawGameButtonsExt(TRUE);
16003 }
16004
16005 static void GameUndoRedoExt(void)
16006 {
16007   ClearPlayerAction();
16008
16009   tape.pausing = TRUE;
16010
16011   RedrawPlayfield();
16012   UpdateAndDisplayGameControlValues();
16013
16014   DrawCompleteVideoDisplay();
16015   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16016   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16017   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16018
16019   BackToFront();
16020 }
16021
16022 static void GameUndo(int steps)
16023 {
16024   if (!CheckEngineSnapshotList())
16025     return;
16026
16027   LoadEngineSnapshot_Undo(steps);
16028
16029   GameUndoRedoExt();
16030 }
16031
16032 static void GameRedo(int steps)
16033 {
16034   if (!CheckEngineSnapshotList())
16035     return;
16036
16037   LoadEngineSnapshot_Redo(steps);
16038
16039   GameUndoRedoExt();
16040 }
16041
16042 static void HandleGameButtonsExt(int id, int button)
16043 {
16044   static boolean game_undo_executed = FALSE;
16045   int steps = BUTTON_STEPSIZE(button);
16046   boolean handle_game_buttons =
16047     (game_status == GAME_MODE_PLAYING ||
16048      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16049
16050   if (!handle_game_buttons)
16051     return;
16052
16053   switch (id)
16054   {
16055     case GAME_CTRL_ID_STOP:
16056     case GAME_CTRL_ID_PANEL_STOP:
16057     case GAME_CTRL_ID_TOUCH_STOP:
16058       if (game_status == GAME_MODE_MAIN)
16059         break;
16060
16061       if (tape.playing)
16062         TapeStop();
16063       else
16064         RequestQuitGame(TRUE);
16065
16066       break;
16067
16068     case GAME_CTRL_ID_PAUSE:
16069     case GAME_CTRL_ID_PAUSE2:
16070     case GAME_CTRL_ID_PANEL_PAUSE:
16071     case GAME_CTRL_ID_TOUCH_PAUSE:
16072       if (network.enabled && game_status == GAME_MODE_PLAYING)
16073       {
16074         if (tape.pausing)
16075           SendToServer_ContinuePlaying();
16076         else
16077           SendToServer_PausePlaying();
16078       }
16079       else
16080         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16081
16082       game_undo_executed = FALSE;
16083
16084       break;
16085
16086     case GAME_CTRL_ID_PLAY:
16087     case GAME_CTRL_ID_PANEL_PLAY:
16088       if (game_status == GAME_MODE_MAIN)
16089       {
16090         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16091       }
16092       else if (tape.pausing)
16093       {
16094         if (network.enabled)
16095           SendToServer_ContinuePlaying();
16096         else
16097           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16098       }
16099       break;
16100
16101     case GAME_CTRL_ID_UNDO:
16102       // Important: When using "save snapshot when collecting an item" mode,
16103       // load last (current) snapshot for first "undo" after pressing "pause"
16104       // (else the last-but-one snapshot would be loaded, because the snapshot
16105       // pointer already points to the last snapshot when pressing "pause",
16106       // which is fine for "every step/move" mode, but not for "every collect")
16107       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16108           !game_undo_executed)
16109         steps--;
16110
16111       game_undo_executed = TRUE;
16112
16113       GameUndo(steps);
16114       break;
16115
16116     case GAME_CTRL_ID_REDO:
16117       GameRedo(steps);
16118       break;
16119
16120     case GAME_CTRL_ID_SAVE:
16121       TapeQuickSave();
16122       break;
16123
16124     case GAME_CTRL_ID_LOAD:
16125       TapeQuickLoad();
16126       break;
16127
16128     case SOUND_CTRL_ID_MUSIC:
16129     case SOUND_CTRL_ID_PANEL_MUSIC:
16130       if (setup.sound_music)
16131       { 
16132         setup.sound_music = FALSE;
16133
16134         FadeMusic();
16135       }
16136       else if (audio.music_available)
16137       { 
16138         setup.sound = setup.sound_music = TRUE;
16139
16140         SetAudioMode(setup.sound);
16141
16142         if (game_status == GAME_MODE_PLAYING)
16143           PlayLevelMusic();
16144       }
16145
16146       RedrawSoundButtonGadget(id);
16147
16148       break;
16149
16150     case SOUND_CTRL_ID_LOOPS:
16151     case SOUND_CTRL_ID_PANEL_LOOPS:
16152       if (setup.sound_loops)
16153         setup.sound_loops = FALSE;
16154       else if (audio.loops_available)
16155       {
16156         setup.sound = setup.sound_loops = TRUE;
16157
16158         SetAudioMode(setup.sound);
16159       }
16160
16161       RedrawSoundButtonGadget(id);
16162
16163       break;
16164
16165     case SOUND_CTRL_ID_SIMPLE:
16166     case SOUND_CTRL_ID_PANEL_SIMPLE:
16167       if (setup.sound_simple)
16168         setup.sound_simple = FALSE;
16169       else if (audio.sound_available)
16170       {
16171         setup.sound = setup.sound_simple = TRUE;
16172
16173         SetAudioMode(setup.sound);
16174       }
16175
16176       RedrawSoundButtonGadget(id);
16177
16178       break;
16179
16180     default:
16181       break;
16182   }
16183 }
16184
16185 static void HandleGameButtons(struct GadgetInfo *gi)
16186 {
16187   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16188 }
16189
16190 void HandleSoundButtonKeys(Key key)
16191 {
16192   if (key == setup.shortcut.sound_simple)
16193     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16194   else if (key == setup.shortcut.sound_loops)
16195     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16196   else if (key == setup.shortcut.sound_music)
16197     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16198 }