added delay counter data type and adjusted related functions
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1558
1559 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1560 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1561 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1562                                  IS_JUST_CHANGING(x, y))
1563
1564 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1565
1566 // static variables for playfield scan mode (scanning forward or backward)
1567 static int playfield_scan_start_x = 0;
1568 static int playfield_scan_start_y = 0;
1569 static int playfield_scan_delta_x = 1;
1570 static int playfield_scan_delta_y = 1;
1571
1572 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1573                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1574                                      (y) += playfield_scan_delta_y)     \
1575                                 for ((x) = playfield_scan_start_x;      \
1576                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1577                                      (x) += playfield_scan_delta_x)
1578
1579 #ifdef DEBUG
1580 void DEBUG_SetMaximumDynamite(void)
1581 {
1582   int i;
1583
1584   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1585     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1586       local_player->inventory_element[local_player->inventory_size++] =
1587         EL_DYNAMITE;
1588 }
1589 #endif
1590
1591 static void InitPlayfieldScanModeVars(void)
1592 {
1593   if (game.use_reverse_scan_direction)
1594   {
1595     playfield_scan_start_x = lev_fieldx - 1;
1596     playfield_scan_start_y = lev_fieldy - 1;
1597
1598     playfield_scan_delta_x = -1;
1599     playfield_scan_delta_y = -1;
1600   }
1601   else
1602   {
1603     playfield_scan_start_x = 0;
1604     playfield_scan_start_y = 0;
1605
1606     playfield_scan_delta_x = 1;
1607     playfield_scan_delta_y = 1;
1608   }
1609 }
1610
1611 static void InitPlayfieldScanMode(int mode)
1612 {
1613   game.use_reverse_scan_direction =
1614     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1615
1616   InitPlayfieldScanModeVars();
1617 }
1618
1619 static int get_move_delay_from_stepsize(int move_stepsize)
1620 {
1621   move_stepsize =
1622     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1623
1624   // make sure that stepsize value is always a power of 2
1625   move_stepsize = (1 << log_2(move_stepsize));
1626
1627   return TILEX / move_stepsize;
1628 }
1629
1630 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1631                                boolean init_game)
1632 {
1633   int player_nr = player->index_nr;
1634   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1635   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1636
1637   // do no immediately change move delay -- the player might just be moving
1638   player->move_delay_value_next = move_delay;
1639
1640   // information if player can move must be set separately
1641   player->cannot_move = cannot_move;
1642
1643   if (init_game)
1644   {
1645     player->move_delay       = game.initial_move_delay[player_nr];
1646     player->move_delay_value = game.initial_move_delay_value[player_nr];
1647
1648     player->move_delay_value_next = -1;
1649
1650     player->move_delay_reset_counter = 0;
1651   }
1652 }
1653
1654 void GetPlayerConfig(void)
1655 {
1656   GameFrameDelay = setup.game_frame_delay;
1657
1658   if (!audio.sound_available)
1659     setup.sound_simple = FALSE;
1660
1661   if (!audio.loops_available)
1662     setup.sound_loops = FALSE;
1663
1664   if (!audio.music_available)
1665     setup.sound_music = FALSE;
1666
1667   if (!video.fullscreen_available)
1668     setup.fullscreen = FALSE;
1669
1670   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1671
1672   SetAudioMode(setup.sound);
1673 }
1674
1675 int GetElementFromGroupElement(int element)
1676 {
1677   if (IS_GROUP_ELEMENT(element))
1678   {
1679     struct ElementGroupInfo *group = element_info[element].group;
1680     int last_anim_random_frame = gfx.anim_random_frame;
1681     int element_pos;
1682
1683     if (group->choice_mode == ANIM_RANDOM)
1684       gfx.anim_random_frame = RND(group->num_elements_resolved);
1685
1686     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1687                                     group->choice_mode, 0,
1688                                     group->choice_pos);
1689
1690     if (group->choice_mode == ANIM_RANDOM)
1691       gfx.anim_random_frame = last_anim_random_frame;
1692
1693     group->choice_pos++;
1694
1695     element = group->element_resolved[element_pos];
1696   }
1697
1698   return element;
1699 }
1700
1701 static void IncrementSokobanFieldsNeeded(void)
1702 {
1703   if (level.sb_fields_needed)
1704     game.sokoban_fields_still_needed++;
1705 }
1706
1707 static void IncrementSokobanObjectsNeeded(void)
1708 {
1709   if (level.sb_objects_needed)
1710     game.sokoban_objects_still_needed++;
1711 }
1712
1713 static void DecrementSokobanFieldsNeeded(void)
1714 {
1715   if (game.sokoban_fields_still_needed > 0)
1716     game.sokoban_fields_still_needed--;
1717 }
1718
1719 static void DecrementSokobanObjectsNeeded(void)
1720 {
1721   if (game.sokoban_objects_still_needed > 0)
1722     game.sokoban_objects_still_needed--;
1723 }
1724
1725 static void InitPlayerField(int x, int y, int element, boolean init_game)
1726 {
1727   if (element == EL_SP_MURPHY)
1728   {
1729     if (init_game)
1730     {
1731       if (stored_player[0].present)
1732       {
1733         Tile[x][y] = EL_SP_MURPHY_CLONE;
1734
1735         return;
1736       }
1737       else
1738       {
1739         stored_player[0].initial_element = element;
1740         stored_player[0].use_murphy = TRUE;
1741
1742         if (!level.use_artwork_element[0])
1743           stored_player[0].artwork_element = EL_SP_MURPHY;
1744       }
1745
1746       Tile[x][y] = EL_PLAYER_1;
1747     }
1748   }
1749
1750   if (init_game)
1751   {
1752     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1753     int jx = player->jx, jy = player->jy;
1754
1755     player->present = TRUE;
1756
1757     player->block_last_field = (element == EL_SP_MURPHY ?
1758                                 level.sp_block_last_field :
1759                                 level.block_last_field);
1760
1761     // ---------- initialize player's last field block delay ------------------
1762
1763     // always start with reliable default value (no adjustment needed)
1764     player->block_delay_adjustment = 0;
1765
1766     // special case 1: in Supaplex, Murphy blocks last field one more frame
1767     if (player->block_last_field && element == EL_SP_MURPHY)
1768       player->block_delay_adjustment = 1;
1769
1770     // special case 2: in game engines before 3.1.1, blocking was different
1771     if (game.use_block_last_field_bug)
1772       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1773
1774     if (!network.enabled || player->connected_network)
1775     {
1776       player->active = TRUE;
1777
1778       // remove potentially duplicate players
1779       if (StorePlayer[jx][jy] == Tile[x][y])
1780         StorePlayer[jx][jy] = 0;
1781
1782       StorePlayer[x][y] = Tile[x][y];
1783
1784 #if DEBUG_INIT_PLAYER
1785       Debug("game:init:player", "- player element %d activated",
1786             player->element_nr);
1787       Debug("game:init:player", "  (local player is %d and currently %s)",
1788             local_player->element_nr,
1789             local_player->active ? "active" : "not active");
1790     }
1791 #endif
1792
1793     Tile[x][y] = EL_EMPTY;
1794
1795     player->jx = player->last_jx = x;
1796     player->jy = player->last_jy = y;
1797   }
1798
1799   // always check if player was just killed and should be reanimated
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; // if player was just killed, reanimate him
1806   }
1807 }
1808
1809 static void InitField(int x, int y, boolean init_game)
1810 {
1811   int element = Tile[x][y];
1812
1813   switch (element)
1814   {
1815     case EL_SP_MURPHY:
1816     case EL_PLAYER_1:
1817     case EL_PLAYER_2:
1818     case EL_PLAYER_3:
1819     case EL_PLAYER_4:
1820       InitPlayerField(x, y, element, init_game);
1821       break;
1822
1823     case EL_SOKOBAN_FIELD_PLAYER:
1824       element = Tile[x][y] = EL_PLAYER_1;
1825       InitField(x, y, init_game);
1826
1827       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1828       InitField(x, y, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_EMPTY:
1832       IncrementSokobanFieldsNeeded();
1833       break;
1834
1835     case EL_SOKOBAN_OBJECT:
1836       IncrementSokobanObjectsNeeded();
1837       break;
1838
1839     case EL_STONEBLOCK:
1840       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1841         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1842       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1843         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1844       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1845         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1846       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1847         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1848       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1849         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1850       break;
1851
1852     case EL_BUG:
1853     case EL_BUG_RIGHT:
1854     case EL_BUG_UP:
1855     case EL_BUG_LEFT:
1856     case EL_BUG_DOWN:
1857     case EL_SPACESHIP:
1858     case EL_SPACESHIP_RIGHT:
1859     case EL_SPACESHIP_UP:
1860     case EL_SPACESHIP_LEFT:
1861     case EL_SPACESHIP_DOWN:
1862     case EL_BD_BUTTERFLY:
1863     case EL_BD_BUTTERFLY_RIGHT:
1864     case EL_BD_BUTTERFLY_UP:
1865     case EL_BD_BUTTERFLY_LEFT:
1866     case EL_BD_BUTTERFLY_DOWN:
1867     case EL_BD_FIREFLY:
1868     case EL_BD_FIREFLY_RIGHT:
1869     case EL_BD_FIREFLY_UP:
1870     case EL_BD_FIREFLY_LEFT:
1871     case EL_BD_FIREFLY_DOWN:
1872     case EL_PACMAN_RIGHT:
1873     case EL_PACMAN_UP:
1874     case EL_PACMAN_LEFT:
1875     case EL_PACMAN_DOWN:
1876     case EL_YAMYAM:
1877     case EL_YAMYAM_LEFT:
1878     case EL_YAMYAM_RIGHT:
1879     case EL_YAMYAM_UP:
1880     case EL_YAMYAM_DOWN:
1881     case EL_DARK_YAMYAM:
1882     case EL_ROBOT:
1883     case EL_PACMAN:
1884     case EL_SP_SNIKSNAK:
1885     case EL_SP_ELECTRON:
1886     case EL_MOLE:
1887     case EL_MOLE_LEFT:
1888     case EL_MOLE_RIGHT:
1889     case EL_MOLE_UP:
1890     case EL_MOLE_DOWN:
1891     case EL_SPRING_LEFT:
1892     case EL_SPRING_RIGHT:
1893       InitMovDir(x, y);
1894       break;
1895
1896     case EL_AMOEBA_FULL:
1897     case EL_BD_AMOEBA:
1898       InitAmoebaNr(x, y);
1899       break;
1900
1901     case EL_AMOEBA_DROP:
1902       if (y == lev_fieldy - 1)
1903       {
1904         Tile[x][y] = EL_AMOEBA_GROWING;
1905         Store[x][y] = EL_AMOEBA_WET;
1906       }
1907       break;
1908
1909     case EL_DYNAMITE_ACTIVE:
1910     case EL_SP_DISK_RED_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1912     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1913     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1914     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1915       MovDelay[x][y] = 96;
1916       break;
1917
1918     case EL_EM_DYNAMITE_ACTIVE:
1919       MovDelay[x][y] = 32;
1920       break;
1921
1922     case EL_LAMP:
1923       game.lights_still_needed++;
1924       break;
1925
1926     case EL_PENGUIN:
1927       game.friends_still_needed++;
1928       break;
1929
1930     case EL_PIG:
1931     case EL_DRAGON:
1932       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1933       break;
1934
1935     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1944     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1945     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1946     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1947       if (init_game)
1948       {
1949         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1950         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1951         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1952
1953         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1954         {
1955           game.belt_dir[belt_nr] = belt_dir;
1956           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1957         }
1958         else    // more than one switch -- set it like the first switch
1959         {
1960           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1961         }
1962       }
1963       break;
1964
1965     case EL_LIGHT_SWITCH_ACTIVE:
1966       if (init_game)
1967         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1968       break;
1969
1970     case EL_INVISIBLE_STEELWALL:
1971     case EL_INVISIBLE_WALL:
1972     case EL_INVISIBLE_SAND:
1973       if (game.light_time_left > 0 ||
1974           game.lenses_time_left > 0)
1975         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1976       break;
1977
1978     case EL_EMC_MAGIC_BALL:
1979       if (game.ball_active)
1980         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1981       break;
1982
1983     case EL_EMC_MAGIC_BALL_SWITCH:
1984       if (game.ball_active)
1985         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1986       break;
1987
1988     case EL_TRIGGER_PLAYER:
1989     case EL_TRIGGER_ELEMENT:
1990     case EL_TRIGGER_CE_VALUE:
1991     case EL_TRIGGER_CE_SCORE:
1992     case EL_SELF:
1993     case EL_ANY_ELEMENT:
1994     case EL_CURRENT_CE_VALUE:
1995     case EL_CURRENT_CE_SCORE:
1996     case EL_PREV_CE_1:
1997     case EL_PREV_CE_2:
1998     case EL_PREV_CE_3:
1999     case EL_PREV_CE_4:
2000     case EL_PREV_CE_5:
2001     case EL_PREV_CE_6:
2002     case EL_PREV_CE_7:
2003     case EL_PREV_CE_8:
2004     case EL_NEXT_CE_1:
2005     case EL_NEXT_CE_2:
2006     case EL_NEXT_CE_3:
2007     case EL_NEXT_CE_4:
2008     case EL_NEXT_CE_5:
2009     case EL_NEXT_CE_6:
2010     case EL_NEXT_CE_7:
2011     case EL_NEXT_CE_8:
2012       // reference elements should not be used on the playfield
2013       Tile[x][y] = EL_EMPTY;
2014       break;
2015
2016     default:
2017       if (IS_CUSTOM_ELEMENT(element))
2018       {
2019         if (CAN_MOVE(element))
2020           InitMovDir(x, y);
2021
2022         if (!element_info[element].use_last_ce_value || init_game)
2023           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2024       }
2025       else if (IS_GROUP_ELEMENT(element))
2026       {
2027         Tile[x][y] = GetElementFromGroupElement(element);
2028
2029         InitField(x, y, init_game);
2030       }
2031       else if (IS_EMPTY_ELEMENT(element))
2032       {
2033         GfxElementEmpty[x][y] = element;
2034         Tile[x][y] = EL_EMPTY;
2035
2036         if (element_info[element].use_gfx_element)
2037           game.use_masked_elements = TRUE;
2038       }
2039
2040       break;
2041   }
2042
2043   if (!init_game)
2044     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2045 }
2046
2047 static void InitField_WithBug1(int x, int y, boolean init_game)
2048 {
2049   InitField(x, y, init_game);
2050
2051   // not needed to call InitMovDir() -- already done by InitField()!
2052   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2053       CAN_MOVE(Tile[x][y]))
2054     InitMovDir(x, y);
2055 }
2056
2057 static void InitField_WithBug2(int x, int y, boolean init_game)
2058 {
2059   int old_element = Tile[x][y];
2060
2061   InitField(x, y, init_game);
2062
2063   // not needed to call InitMovDir() -- already done by InitField()!
2064   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2065       CAN_MOVE(old_element) &&
2066       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2067     InitMovDir(x, y);
2068
2069   /* this case is in fact a combination of not less than three bugs:
2070      first, it calls InitMovDir() for elements that can move, although this is
2071      already done by InitField(); then, it checks the element that was at this
2072      field _before_ the call to InitField() (which can change it); lastly, it
2073      was not called for "mole with direction" elements, which were treated as
2074      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2075   */
2076 }
2077
2078 static int get_key_element_from_nr(int key_nr)
2079 {
2080   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2081                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2082                           EL_EM_KEY_1 : EL_KEY_1);
2083
2084   return key_base_element + key_nr;
2085 }
2086
2087 static int get_next_dropped_element(struct PlayerInfo *player)
2088 {
2089   return (player->inventory_size > 0 ?
2090           player->inventory_element[player->inventory_size - 1] :
2091           player->inventory_infinite_element != EL_UNDEFINED ?
2092           player->inventory_infinite_element :
2093           player->dynabombs_left > 0 ?
2094           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2095           EL_UNDEFINED);
2096 }
2097
2098 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2099 {
2100   // pos >= 0: get element from bottom of the stack;
2101   // pos <  0: get element from top of the stack
2102
2103   if (pos < 0)
2104   {
2105     int min_inventory_size = -pos;
2106     int inventory_pos = player->inventory_size - min_inventory_size;
2107     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2108
2109     return (player->inventory_size >= min_inventory_size ?
2110             player->inventory_element[inventory_pos] :
2111             player->inventory_infinite_element != EL_UNDEFINED ?
2112             player->inventory_infinite_element :
2113             player->dynabombs_left >= min_dynabombs_left ?
2114             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2115             EL_UNDEFINED);
2116   }
2117   else
2118   {
2119     int min_dynabombs_left = pos + 1;
2120     int min_inventory_size = pos + 1 - player->dynabombs_left;
2121     int inventory_pos = pos - player->dynabombs_left;
2122
2123     return (player->inventory_infinite_element != EL_UNDEFINED ?
2124             player->inventory_infinite_element :
2125             player->dynabombs_left >= min_dynabombs_left ?
2126             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2127             player->inventory_size >= min_inventory_size ?
2128             player->inventory_element[inventory_pos] :
2129             EL_UNDEFINED);
2130   }
2131 }
2132
2133 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2134 {
2135   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2136   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2137   int compare_result;
2138
2139   if (gpo1->sort_priority != gpo2->sort_priority)
2140     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2141   else
2142     compare_result = gpo1->nr - gpo2->nr;
2143
2144   return compare_result;
2145 }
2146
2147 int getPlayerInventorySize(int player_nr)
2148 {
2149   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2150     return game_em.ply[player_nr]->dynamite;
2151   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2152     return game_sp.red_disk_count;
2153   else
2154     return stored_player[player_nr].inventory_size;
2155 }
2156
2157 static void InitGameControlValues(void)
2158 {
2159   int i;
2160
2161   for (i = 0; game_panel_controls[i].nr != -1; i++)
2162   {
2163     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2164     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2165     struct TextPosInfo *pos = gpc->pos;
2166     int nr = gpc->nr;
2167     int type = gpc->type;
2168
2169     if (nr != i)
2170     {
2171       Error("'game_panel_controls' structure corrupted at %d", i);
2172
2173       Fail("this should not happen -- please debug");
2174     }
2175
2176     // force update of game controls after initialization
2177     gpc->value = gpc->last_value = -1;
2178     gpc->frame = gpc->last_frame = -1;
2179     gpc->gfx_frame = -1;
2180
2181     // determine panel value width for later calculation of alignment
2182     if (type == TYPE_INTEGER || type == TYPE_STRING)
2183     {
2184       pos->width = pos->size * getFontWidth(pos->font);
2185       pos->height = getFontHeight(pos->font);
2186     }
2187     else if (type == TYPE_ELEMENT)
2188     {
2189       pos->width = pos->size;
2190       pos->height = pos->size;
2191     }
2192
2193     // fill structure for game panel draw order
2194     gpo->nr = gpc->nr;
2195     gpo->sort_priority = pos->sort_priority;
2196   }
2197
2198   // sort game panel controls according to sort_priority and control number
2199   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2200         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2201 }
2202
2203 static void UpdatePlayfieldElementCount(void)
2204 {
2205   boolean use_element_count = FALSE;
2206   int i, j, x, y;
2207
2208   // first check if it is needed at all to calculate playfield element count
2209   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2210     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2211       use_element_count = TRUE;
2212
2213   if (!use_element_count)
2214     return;
2215
2216   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2217     element_info[i].element_count = 0;
2218
2219   SCAN_PLAYFIELD(x, y)
2220   {
2221     element_info[Tile[x][y]].element_count++;
2222   }
2223
2224   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2225     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2226       if (IS_IN_GROUP(j, i))
2227         element_info[EL_GROUP_START + i].element_count +=
2228           element_info[j].element_count;
2229 }
2230
2231 static void UpdateGameControlValues(void)
2232 {
2233   int i, k;
2234   int time = (game.LevelSolved ?
2235               game.LevelSolved_CountingTime :
2236               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2237               game_em.lev->time :
2238               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2239               game_sp.time_played :
2240               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2241               game_mm.energy_left :
2242               game.no_level_time_limit ? TimePlayed : TimeLeft);
2243   int score = (game.LevelSolved ?
2244                game.LevelSolved_CountingScore :
2245                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246                game_em.lev->score :
2247                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248                game_sp.score :
2249                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2250                game_mm.score :
2251                game.score);
2252   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2253               game_em.lev->gems_needed :
2254               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2255               game_sp.infotrons_still_needed :
2256               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2257               game_mm.kettles_still_needed :
2258               game.gems_still_needed);
2259   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2260                      game_em.lev->gems_needed > 0 :
2261                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2262                      game_sp.infotrons_still_needed > 0 :
2263                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2264                      game_mm.kettles_still_needed > 0 ||
2265                      game_mm.lights_still_needed > 0 :
2266                      game.gems_still_needed > 0 ||
2267                      game.sokoban_fields_still_needed > 0 ||
2268                      game.sokoban_objects_still_needed > 0 ||
2269                      game.lights_still_needed > 0);
2270   int health = (game.LevelSolved ?
2271                 game.LevelSolved_CountingHealth :
2272                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2273                 MM_HEALTH(game_mm.laser_overload_value) :
2274                 game.health);
2275   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2276
2277   UpdatePlayfieldElementCount();
2278
2279   // update game panel control values
2280
2281   // used instead of "level_nr" (for network games)
2282   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2283   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2284
2285   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2286   for (i = 0; i < MAX_NUM_KEYS; i++)
2287     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2288   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2289   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2290
2291   if (game.centered_player_nr == -1)
2292   {
2293     for (i = 0; i < MAX_PLAYERS; i++)
2294     {
2295       // only one player in Supaplex game engine
2296       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2297         break;
2298
2299       for (k = 0; k < MAX_NUM_KEYS; k++)
2300       {
2301         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302         {
2303           if (game_em.ply[i]->keys & (1 << k))
2304             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2305               get_key_element_from_nr(k);
2306         }
2307         else if (stored_player[i].key[k])
2308           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2309             get_key_element_from_nr(k);
2310       }
2311
2312       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2313         getPlayerInventorySize(i);
2314
2315       if (stored_player[i].num_white_keys > 0)
2316         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2317           EL_DC_KEY_WHITE;
2318
2319       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2320         stored_player[i].num_white_keys;
2321     }
2322   }
2323   else
2324   {
2325     int player_nr = game.centered_player_nr;
2326
2327     for (k = 0; k < MAX_NUM_KEYS; k++)
2328     {
2329       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2330       {
2331         if (game_em.ply[player_nr]->keys & (1 << k))
2332           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2333             get_key_element_from_nr(k);
2334       }
2335       else if (stored_player[player_nr].key[k])
2336         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2337           get_key_element_from_nr(k);
2338     }
2339
2340     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2341       getPlayerInventorySize(player_nr);
2342
2343     if (stored_player[player_nr].num_white_keys > 0)
2344       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2345
2346     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2347       stored_player[player_nr].num_white_keys;
2348   }
2349
2350   // re-arrange keys on game panel, if needed or if defined by style settings
2351   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2352   {
2353     int nr = GAME_PANEL_KEY_1 + i;
2354     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2355     struct TextPosInfo *pos = gpc->pos;
2356
2357     // skip check if key is not in the player's inventory
2358     if (gpc->value == EL_EMPTY)
2359       continue;
2360
2361     // check if keys should be arranged on panel from left to right
2362     if (pos->style == STYLE_LEFTMOST_POSITION)
2363     {
2364       // check previous key positions (left from current key)
2365       for (k = 0; k < i; k++)
2366       {
2367         int nr_new = GAME_PANEL_KEY_1 + k;
2368
2369         if (game_panel_controls[nr_new].value == EL_EMPTY)
2370         {
2371           game_panel_controls[nr_new].value = gpc->value;
2372           gpc->value = EL_EMPTY;
2373
2374           break;
2375         }
2376       }
2377     }
2378
2379     // check if "undefined" keys can be placed at some other position
2380     if (pos->x == -1 && pos->y == -1)
2381     {
2382       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2383
2384       // 1st try: display key at the same position as normal or EM keys
2385       if (game_panel_controls[nr_new].value == EL_EMPTY)
2386       {
2387         game_panel_controls[nr_new].value = gpc->value;
2388       }
2389       else
2390       {
2391         // 2nd try: display key at the next free position in the key panel
2392         for (k = 0; k < STD_NUM_KEYS; k++)
2393         {
2394           nr_new = GAME_PANEL_KEY_1 + k;
2395
2396           if (game_panel_controls[nr_new].value == EL_EMPTY)
2397           {
2398             game_panel_controls[nr_new].value = gpc->value;
2399
2400             break;
2401           }
2402         }
2403       }
2404     }
2405   }
2406
2407   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2408   {
2409     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2410       get_inventory_element_from_pos(local_player, i);
2411     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2412       get_inventory_element_from_pos(local_player, -i - 1);
2413   }
2414
2415   game_panel_controls[GAME_PANEL_SCORE].value = score;
2416   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2417
2418   game_panel_controls[GAME_PANEL_TIME].value = time;
2419
2420   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2421   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2422   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2423
2424   if (level.time == 0)
2425     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2426   else
2427     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2428
2429   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2430   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2431
2432   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2433
2434   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2435     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2436      EL_EMPTY);
2437   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2438     local_player->shield_normal_time_left;
2439   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2440     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2441      EL_EMPTY);
2442   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2443     local_player->shield_deadly_time_left;
2444
2445   game_panel_controls[GAME_PANEL_EXIT].value =
2446     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2447
2448   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2449     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2450   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2451     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2452      EL_EMC_MAGIC_BALL_SWITCH);
2453
2454   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2455     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2456   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2457     game.light_time_left;
2458
2459   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2460     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2461   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2462     game.timegate_time_left;
2463
2464   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2465     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2466
2467   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2468     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2469   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2470     game.lenses_time_left;
2471
2472   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2473     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2474   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2475     game.magnify_time_left;
2476
2477   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2478     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2479      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2480      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2481      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2482      EL_BALLOON_SWITCH_NONE);
2483
2484   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2485     local_player->dynabomb_count;
2486   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2487     local_player->dynabomb_size;
2488   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2489     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2490
2491   game_panel_controls[GAME_PANEL_PENGUINS].value =
2492     game.friends_still_needed;
2493
2494   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2495     game.sokoban_objects_still_needed;
2496   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2497     game.sokoban_fields_still_needed;
2498
2499   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2500     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2501
2502   for (i = 0; i < NUM_BELTS; i++)
2503   {
2504     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2505       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2506        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2507     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2508       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2509   }
2510
2511   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2512     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2513   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2514     game.magic_wall_time_left;
2515
2516   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2517     local_player->gravity;
2518
2519   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2520     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2521
2522   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2523     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2524       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2525        game.panel.element[i].id : EL_UNDEFINED);
2526
2527   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2528     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2529       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2530        element_info[game.panel.element_count[i].id].element_count : 0);
2531
2532   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2533     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2534       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2535        element_info[game.panel.ce_score[i].id].collect_score : 0);
2536
2537   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2538     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2539       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2540        element_info[game.panel.ce_score_element[i].id].collect_score :
2541        EL_UNDEFINED);
2542
2543   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2544   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2545   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2546
2547   // update game panel control frames
2548
2549   for (i = 0; game_panel_controls[i].nr != -1; i++)
2550   {
2551     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2552
2553     if (gpc->type == TYPE_ELEMENT)
2554     {
2555       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2556       {
2557         int last_anim_random_frame = gfx.anim_random_frame;
2558         int element = gpc->value;
2559         int graphic = el2panelimg(element);
2560         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2561                                sync_random_frame : INIT_GFX_RANDOM());
2562
2563         if (gpc->value != gpc->last_value)
2564         {
2565           gpc->gfx_frame = 0;
2566           gpc->gfx_random = init_gfx_random;
2567         }
2568         else
2569         {
2570           gpc->gfx_frame++;
2571
2572           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2573               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2574             gpc->gfx_random = init_gfx_random;
2575         }
2576
2577         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2578           gfx.anim_random_frame = gpc->gfx_random;
2579
2580         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2581           gpc->gfx_frame = element_info[element].collect_score;
2582
2583         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2584
2585         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2586           gfx.anim_random_frame = last_anim_random_frame;
2587       }
2588     }
2589     else if (gpc->type == TYPE_GRAPHIC)
2590     {
2591       if (gpc->graphic != IMG_UNDEFINED)
2592       {
2593         int last_anim_random_frame = gfx.anim_random_frame;
2594         int graphic = gpc->graphic;
2595         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2596                                sync_random_frame : INIT_GFX_RANDOM());
2597
2598         if (gpc->value != gpc->last_value)
2599         {
2600           gpc->gfx_frame = 0;
2601           gpc->gfx_random = init_gfx_random;
2602         }
2603         else
2604         {
2605           gpc->gfx_frame++;
2606
2607           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2608               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2609             gpc->gfx_random = init_gfx_random;
2610         }
2611
2612         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2613           gfx.anim_random_frame = gpc->gfx_random;
2614
2615         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2616
2617         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2618           gfx.anim_random_frame = last_anim_random_frame;
2619       }
2620     }
2621   }
2622 }
2623
2624 static void DisplayGameControlValues(void)
2625 {
2626   boolean redraw_panel = FALSE;
2627   int i;
2628
2629   for (i = 0; game_panel_controls[i].nr != -1; i++)
2630   {
2631     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2632
2633     if (PANEL_DEACTIVATED(gpc->pos))
2634       continue;
2635
2636     if (gpc->value == gpc->last_value &&
2637         gpc->frame == gpc->last_frame)
2638       continue;
2639
2640     redraw_panel = TRUE;
2641   }
2642
2643   if (!redraw_panel)
2644     return;
2645
2646   // copy default game door content to main double buffer
2647
2648   // !!! CHECK AGAIN !!!
2649   SetPanelBackground();
2650   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2651   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2652
2653   // redraw game control buttons
2654   RedrawGameButtons();
2655
2656   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2657
2658   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2659   {
2660     int nr = game_panel_order[i].nr;
2661     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2662     struct TextPosInfo *pos = gpc->pos;
2663     int type = gpc->type;
2664     int value = gpc->value;
2665     int frame = gpc->frame;
2666     int size = pos->size;
2667     int font = pos->font;
2668     boolean draw_masked = pos->draw_masked;
2669     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2670
2671     if (PANEL_DEACTIVATED(pos))
2672       continue;
2673
2674     if (pos->class == get_hash_from_key("extra_panel_items") &&
2675         !setup.prefer_extra_panel_items)
2676       continue;
2677
2678     gpc->last_value = value;
2679     gpc->last_frame = frame;
2680
2681     if (type == TYPE_INTEGER)
2682     {
2683       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2684           nr == GAME_PANEL_INVENTORY_COUNT ||
2685           nr == GAME_PANEL_SCORE ||
2686           nr == GAME_PANEL_HIGHSCORE ||
2687           nr == GAME_PANEL_TIME)
2688       {
2689         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2690
2691         if (use_dynamic_size)           // use dynamic number of digits
2692         {
2693           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2694                               nr == GAME_PANEL_INVENTORY_COUNT ||
2695                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2696           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2697                           nr == GAME_PANEL_INVENTORY_COUNT ||
2698                           nr == GAME_PANEL_TIME ? 1 : 2);
2699           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2700                        nr == GAME_PANEL_INVENTORY_COUNT ||
2701                        nr == GAME_PANEL_TIME ? 3 : 5);
2702           int size2 = size1 + size_add;
2703           int font1 = pos->font;
2704           int font2 = pos->font_alt;
2705
2706           size = (value < value_change ? size1 : size2);
2707           font = (value < value_change ? font1 : font2);
2708         }
2709       }
2710
2711       // correct text size if "digits" is zero or less
2712       if (size <= 0)
2713         size = strlen(int2str(value, size));
2714
2715       // dynamically correct text alignment
2716       pos->width = size * getFontWidth(font);
2717
2718       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2719                   int2str(value, size), font, mask_mode);
2720     }
2721     else if (type == TYPE_ELEMENT)
2722     {
2723       int element, graphic;
2724       Bitmap *src_bitmap;
2725       int src_x, src_y;
2726       int width, height;
2727       int dst_x = PANEL_XPOS(pos);
2728       int dst_y = PANEL_YPOS(pos);
2729
2730       if (value != EL_UNDEFINED && value != EL_EMPTY)
2731       {
2732         element = value;
2733         graphic = el2panelimg(value);
2734
2735 #if 0
2736         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2737               element, EL_NAME(element), size);
2738 #endif
2739
2740         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2741           size = TILESIZE;
2742
2743         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2744                               &src_x, &src_y);
2745
2746         width  = graphic_info[graphic].width  * size / TILESIZE;
2747         height = graphic_info[graphic].height * size / TILESIZE;
2748
2749         if (draw_masked)
2750           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2751                            dst_x, dst_y);
2752         else
2753           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2754                      dst_x, dst_y);
2755       }
2756     }
2757     else if (type == TYPE_GRAPHIC)
2758     {
2759       int graphic        = gpc->graphic;
2760       int graphic_active = gpc->graphic_active;
2761       Bitmap *src_bitmap;
2762       int src_x, src_y;
2763       int width, height;
2764       int dst_x = PANEL_XPOS(pos);
2765       int dst_y = PANEL_YPOS(pos);
2766       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2767                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2768
2769       if (graphic != IMG_UNDEFINED && !skip)
2770       {
2771         if (pos->style == STYLE_REVERSE)
2772           value = 100 - value;
2773
2774         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2775
2776         if (pos->direction & MV_HORIZONTAL)
2777         {
2778           width  = graphic_info[graphic_active].width * value / 100;
2779           height = graphic_info[graphic_active].height;
2780
2781           if (pos->direction == MV_LEFT)
2782           {
2783             src_x += graphic_info[graphic_active].width - width;
2784             dst_x += graphic_info[graphic_active].width - width;
2785           }
2786         }
2787         else
2788         {
2789           width  = graphic_info[graphic_active].width;
2790           height = graphic_info[graphic_active].height * value / 100;
2791
2792           if (pos->direction == MV_UP)
2793           {
2794             src_y += graphic_info[graphic_active].height - height;
2795             dst_y += graphic_info[graphic_active].height - height;
2796           }
2797         }
2798
2799         if (draw_masked)
2800           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2801                            dst_x, dst_y);
2802         else
2803           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2804                      dst_x, dst_y);
2805
2806         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2807
2808         if (pos->direction & MV_HORIZONTAL)
2809         {
2810           if (pos->direction == MV_RIGHT)
2811           {
2812             src_x += width;
2813             dst_x += width;
2814           }
2815           else
2816           {
2817             dst_x = PANEL_XPOS(pos);
2818           }
2819
2820           width = graphic_info[graphic].width - width;
2821         }
2822         else
2823         {
2824           if (pos->direction == MV_DOWN)
2825           {
2826             src_y += height;
2827             dst_y += height;
2828           }
2829           else
2830           {
2831             dst_y = PANEL_YPOS(pos);
2832           }
2833
2834           height = graphic_info[graphic].height - height;
2835         }
2836
2837         if (draw_masked)
2838           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2839                            dst_x, dst_y);
2840         else
2841           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2842                      dst_x, dst_y);
2843       }
2844     }
2845     else if (type == TYPE_STRING)
2846     {
2847       boolean active = (value != 0);
2848       char *state_normal = "off";
2849       char *state_active = "on";
2850       char *state = (active ? state_active : state_normal);
2851       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2852                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2853                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2854                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2855
2856       if (nr == GAME_PANEL_GRAVITY_STATE)
2857       {
2858         int font1 = pos->font;          // (used for normal state)
2859         int font2 = pos->font_alt;      // (used for active state)
2860
2861         font = (active ? font2 : font1);
2862       }
2863
2864       if (s != NULL)
2865       {
2866         char *s_cut;
2867
2868         if (size <= 0)
2869         {
2870           // don't truncate output if "chars" is zero or less
2871           size = strlen(s);
2872
2873           // dynamically correct text alignment
2874           pos->width = size * getFontWidth(font);
2875         }
2876
2877         s_cut = getStringCopyN(s, size);
2878
2879         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2880                     s_cut, font, mask_mode);
2881
2882         free(s_cut);
2883       }
2884     }
2885
2886     redraw_mask |= REDRAW_DOOR_1;
2887   }
2888
2889   SetGameStatus(GAME_MODE_PLAYING);
2890 }
2891
2892 void UpdateAndDisplayGameControlValues(void)
2893 {
2894   if (tape.deactivate_display)
2895     return;
2896
2897   UpdateGameControlValues();
2898   DisplayGameControlValues();
2899 }
2900
2901 void UpdateGameDoorValues(void)
2902 {
2903   UpdateGameControlValues();
2904 }
2905
2906 void DrawGameDoorValues(void)
2907 {
2908   DisplayGameControlValues();
2909 }
2910
2911
2912 // ============================================================================
2913 // InitGameEngine()
2914 // ----------------------------------------------------------------------------
2915 // initialize game engine due to level / tape version number
2916 // ============================================================================
2917
2918 static void InitGameEngine(void)
2919 {
2920   int i, j, k, l, x, y;
2921
2922   // set game engine from tape file when re-playing, else from level file
2923   game.engine_version = (tape.playing ? tape.engine_version :
2924                          level.game_version);
2925
2926   // set single or multi-player game mode (needed for re-playing tapes)
2927   game.team_mode = setup.team_mode;
2928
2929   if (tape.playing)
2930   {
2931     int num_players = 0;
2932
2933     for (i = 0; i < MAX_PLAYERS; i++)
2934       if (tape.player_participates[i])
2935         num_players++;
2936
2937     // multi-player tapes contain input data for more than one player
2938     game.team_mode = (num_players > 1);
2939   }
2940
2941 #if 0
2942   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2943         level.game_version);
2944   Debug("game:init:level", "          tape.file_version   == %06d",
2945         tape.file_version);
2946   Debug("game:init:level", "          tape.game_version   == %06d",
2947         tape.game_version);
2948   Debug("game:init:level", "          tape.engine_version == %06d",
2949         tape.engine_version);
2950   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2951         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2952 #endif
2953
2954   // --------------------------------------------------------------------------
2955   // set flags for bugs and changes according to active game engine version
2956   // --------------------------------------------------------------------------
2957
2958   /*
2959     Summary of bugfix:
2960     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2961
2962     Bug was introduced in version:
2963     2.0.1
2964
2965     Bug was fixed in version:
2966     4.2.0.0
2967
2968     Description:
2969     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2970     but the property "can fall" was missing, which caused some levels to be
2971     unsolvable. This was fixed in version 4.2.0.0.
2972
2973     Affected levels/tapes:
2974     An example for a tape that was fixed by this bugfix is tape 029 from the
2975     level set "rnd_sam_bateman".
2976     The wrong behaviour will still be used for all levels or tapes that were
2977     created/recorded with it. An example for this is tape 023 from the level
2978     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2979   */
2980
2981   boolean use_amoeba_dropping_cannot_fall_bug =
2982     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2983       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2984      (tape.playing &&
2985       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2986       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2987
2988   /*
2989     Summary of bugfix/change:
2990     Fixed move speed of elements entering or leaving magic wall.
2991
2992     Fixed/changed in version:
2993     2.0.1
2994
2995     Description:
2996     Before 2.0.1, move speed of elements entering or leaving magic wall was
2997     twice as fast as it is now.
2998     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2999
3000     Affected levels/tapes:
3001     The first condition is generally needed for all levels/tapes before version
3002     2.0.1, which might use the old behaviour before it was changed; known tapes
3003     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3004     The second condition is an exception from the above case and is needed for
3005     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3006     above, but before it was known that this change would break tapes like the
3007     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3008     although the engine version while recording maybe was before 2.0.1. There
3009     are a lot of tapes that are affected by this exception, like tape 006 from
3010     the level set "rnd_conor_mancone".
3011   */
3012
3013   boolean use_old_move_stepsize_for_magic_wall =
3014     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3015      !(tape.playing &&
3016        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3017        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3018
3019   /*
3020     Summary of bugfix/change:
3021     Fixed handling for custom elements that change when pushed by the player.
3022
3023     Fixed/changed in version:
3024     3.1.0
3025
3026     Description:
3027     Before 3.1.0, custom elements that "change when pushing" changed directly
3028     after the player started pushing them (until then handled in "DigField()").
3029     Since 3.1.0, these custom elements are not changed until the "pushing"
3030     move of the element is finished (now handled in "ContinueMoving()").
3031
3032     Affected levels/tapes:
3033     The first condition is generally needed for all levels/tapes before version
3034     3.1.0, which might use the old behaviour before it was changed; known tapes
3035     that are affected are some tapes from the level set "Walpurgis Gardens" by
3036     Jamie Cullen.
3037     The second condition is an exception from the above case and is needed for
3038     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3039     above (including some development versions of 3.1.0), but before it was
3040     known that this change would break tapes like the above and was fixed in
3041     3.1.1, so that the changed behaviour was active although the engine version
3042     while recording maybe was before 3.1.0. There is at least one tape that is
3043     affected by this exception, which is the tape for the one-level set "Bug
3044     Machine" by Juergen Bonhagen.
3045   */
3046
3047   game.use_change_when_pushing_bug =
3048     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3049      !(tape.playing &&
3050        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3051        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3052
3053   /*
3054     Summary of bugfix/change:
3055     Fixed handling for blocking the field the player leaves when moving.
3056
3057     Fixed/changed in version:
3058     3.1.1
3059
3060     Description:
3061     Before 3.1.1, when "block last field when moving" was enabled, the field
3062     the player is leaving when moving was blocked for the time of the move,
3063     and was directly unblocked afterwards. This resulted in the last field
3064     being blocked for exactly one less than the number of frames of one player
3065     move. Additionally, even when blocking was disabled, the last field was
3066     blocked for exactly one frame.
3067     Since 3.1.1, due to changes in player movement handling, the last field
3068     is not blocked at all when blocking is disabled. When blocking is enabled,
3069     the last field is blocked for exactly the number of frames of one player
3070     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3071     last field is blocked for exactly one more than the number of frames of
3072     one player move.
3073
3074     Affected levels/tapes:
3075     (!!! yet to be determined -- probably many !!!)
3076   */
3077
3078   game.use_block_last_field_bug =
3079     (game.engine_version < VERSION_IDENT(3,1,1,0));
3080
3081   /* various special flags and settings for native Emerald Mine game engine */
3082
3083   game_em.use_single_button =
3084     (game.engine_version > VERSION_IDENT(4,0,0,2));
3085
3086   game_em.use_snap_key_bug =
3087     (game.engine_version < VERSION_IDENT(4,0,1,0));
3088
3089   game_em.use_random_bug =
3090     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3091
3092   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3093
3094   game_em.use_old_explosions            = use_old_em_engine;
3095   game_em.use_old_android               = use_old_em_engine;
3096   game_em.use_old_push_elements         = use_old_em_engine;
3097   game_em.use_old_push_into_acid        = use_old_em_engine;
3098
3099   game_em.use_wrap_around               = !use_old_em_engine;
3100
3101   // --------------------------------------------------------------------------
3102
3103   // set maximal allowed number of custom element changes per game frame
3104   game.max_num_changes_per_frame = 1;
3105
3106   // default scan direction: scan playfield from top/left to bottom/right
3107   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3108
3109   // dynamically adjust element properties according to game engine version
3110   InitElementPropertiesEngine(game.engine_version);
3111
3112   // ---------- initialize special element properties -------------------------
3113
3114   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3115   if (use_amoeba_dropping_cannot_fall_bug)
3116     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3117
3118   // ---------- initialize player's initial move delay ------------------------
3119
3120   // dynamically adjust player properties according to level information
3121   for (i = 0; i < MAX_PLAYERS; i++)
3122     game.initial_move_delay_value[i] =
3123       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3124
3125   // dynamically adjust player properties according to game engine version
3126   for (i = 0; i < MAX_PLAYERS; i++)
3127     game.initial_move_delay[i] =
3128       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3129        game.initial_move_delay_value[i] : 0);
3130
3131   // ---------- initialize player's initial push delay ------------------------
3132
3133   // dynamically adjust player properties according to game engine version
3134   game.initial_push_delay_value =
3135     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3136
3137   // ---------- initialize changing elements ----------------------------------
3138
3139   // initialize changing elements information
3140   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3141   {
3142     struct ElementInfo *ei = &element_info[i];
3143
3144     // this pointer might have been changed in the level editor
3145     ei->change = &ei->change_page[0];
3146
3147     if (!IS_CUSTOM_ELEMENT(i))
3148     {
3149       ei->change->target_element = EL_EMPTY_SPACE;
3150       ei->change->delay_fixed = 0;
3151       ei->change->delay_random = 0;
3152       ei->change->delay_frames = 1;
3153     }
3154
3155     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3156     {
3157       ei->has_change_event[j] = FALSE;
3158
3159       ei->event_page_nr[j] = 0;
3160       ei->event_page[j] = &ei->change_page[0];
3161     }
3162   }
3163
3164   // add changing elements from pre-defined list
3165   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3166   {
3167     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3168     struct ElementInfo *ei = &element_info[ch_delay->element];
3169
3170     ei->change->target_element       = ch_delay->target_element;
3171     ei->change->delay_fixed          = ch_delay->change_delay;
3172
3173     ei->change->pre_change_function  = ch_delay->pre_change_function;
3174     ei->change->change_function      = ch_delay->change_function;
3175     ei->change->post_change_function = ch_delay->post_change_function;
3176
3177     ei->change->can_change = TRUE;
3178     ei->change->can_change_or_has_action = TRUE;
3179
3180     ei->has_change_event[CE_DELAY] = TRUE;
3181
3182     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3183     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3184   }
3185
3186   // ---------- initialize internal run-time variables ------------------------
3187
3188   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3189   {
3190     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3191
3192     for (j = 0; j < ei->num_change_pages; j++)
3193     {
3194       ei->change_page[j].can_change_or_has_action =
3195         (ei->change_page[j].can_change |
3196          ei->change_page[j].has_action);
3197     }
3198   }
3199
3200   // add change events from custom element configuration
3201   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3202   {
3203     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3204
3205     for (j = 0; j < ei->num_change_pages; j++)
3206     {
3207       if (!ei->change_page[j].can_change_or_has_action)
3208         continue;
3209
3210       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3211       {
3212         // only add event page for the first page found with this event
3213         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3214         {
3215           ei->has_change_event[k] = TRUE;
3216
3217           ei->event_page_nr[k] = j;
3218           ei->event_page[k] = &ei->change_page[j];
3219         }
3220       }
3221     }
3222   }
3223
3224   // ---------- initialize reference elements in change conditions ------------
3225
3226   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3227   {
3228     int element = EL_CUSTOM_START + i;
3229     struct ElementInfo *ei = &element_info[element];
3230
3231     for (j = 0; j < ei->num_change_pages; j++)
3232     {
3233       int trigger_element = ei->change_page[j].initial_trigger_element;
3234
3235       if (trigger_element >= EL_PREV_CE_8 &&
3236           trigger_element <= EL_NEXT_CE_8)
3237         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3238
3239       ei->change_page[j].trigger_element = trigger_element;
3240     }
3241   }
3242
3243   // ---------- initialize run-time trigger player and element ----------------
3244
3245   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3246   {
3247     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3248
3249     for (j = 0; j < ei->num_change_pages; j++)
3250     {
3251       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3252       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3253       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3254       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3255       ei->change_page[j].actual_trigger_ce_value = 0;
3256       ei->change_page[j].actual_trigger_ce_score = 0;
3257     }
3258   }
3259
3260   // ---------- initialize trigger events -------------------------------------
3261
3262   // initialize trigger events information
3263   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3264     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3265       trigger_events[i][j] = FALSE;
3266
3267   // add trigger events from element change event properties
3268   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3269   {
3270     struct ElementInfo *ei = &element_info[i];
3271
3272     for (j = 0; j < ei->num_change_pages; j++)
3273     {
3274       if (!ei->change_page[j].can_change_or_has_action)
3275         continue;
3276
3277       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3278       {
3279         int trigger_element = ei->change_page[j].trigger_element;
3280
3281         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3282         {
3283           if (ei->change_page[j].has_event[k])
3284           {
3285             if (IS_GROUP_ELEMENT(trigger_element))
3286             {
3287               struct ElementGroupInfo *group =
3288                 element_info[trigger_element].group;
3289
3290               for (l = 0; l < group->num_elements_resolved; l++)
3291                 trigger_events[group->element_resolved[l]][k] = TRUE;
3292             }
3293             else if (trigger_element == EL_ANY_ELEMENT)
3294               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3295                 trigger_events[l][k] = TRUE;
3296             else
3297               trigger_events[trigger_element][k] = TRUE;
3298           }
3299         }
3300       }
3301     }
3302   }
3303
3304   // ---------- initialize push delay -----------------------------------------
3305
3306   // initialize push delay values to default
3307   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3308   {
3309     if (!IS_CUSTOM_ELEMENT(i))
3310     {
3311       // set default push delay values (corrected since version 3.0.7-1)
3312       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3313       {
3314         element_info[i].push_delay_fixed = 2;
3315         element_info[i].push_delay_random = 8;
3316       }
3317       else
3318       {
3319         element_info[i].push_delay_fixed = 8;
3320         element_info[i].push_delay_random = 8;
3321       }
3322     }
3323   }
3324
3325   // set push delay value for certain elements from pre-defined list
3326   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3327   {
3328     int e = push_delay_list[i].element;
3329
3330     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3331     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3332   }
3333
3334   // set push delay value for Supaplex elements for newer engine versions
3335   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3336   {
3337     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3338     {
3339       if (IS_SP_ELEMENT(i))
3340       {
3341         // set SP push delay to just enough to push under a falling zonk
3342         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3343
3344         element_info[i].push_delay_fixed  = delay;
3345         element_info[i].push_delay_random = 0;
3346       }
3347     }
3348   }
3349
3350   // ---------- initialize move stepsize --------------------------------------
3351
3352   // initialize move stepsize values to default
3353   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3354     if (!IS_CUSTOM_ELEMENT(i))
3355       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3356
3357   // set move stepsize value for certain elements from pre-defined list
3358   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3359   {
3360     int e = move_stepsize_list[i].element;
3361
3362     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3363
3364     // set move stepsize value for certain elements for older engine versions
3365     if (use_old_move_stepsize_for_magic_wall)
3366     {
3367       if (e == EL_MAGIC_WALL_FILLING ||
3368           e == EL_MAGIC_WALL_EMPTYING ||
3369           e == EL_BD_MAGIC_WALL_FILLING ||
3370           e == EL_BD_MAGIC_WALL_EMPTYING)
3371         element_info[e].move_stepsize *= 2;
3372     }
3373   }
3374
3375   // ---------- initialize collect score --------------------------------------
3376
3377   // initialize collect score values for custom elements from initial value
3378   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379     if (IS_CUSTOM_ELEMENT(i))
3380       element_info[i].collect_score = element_info[i].collect_score_initial;
3381
3382   // ---------- initialize collect count --------------------------------------
3383
3384   // initialize collect count values for non-custom elements
3385   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3386     if (!IS_CUSTOM_ELEMENT(i))
3387       element_info[i].collect_count_initial = 0;
3388
3389   // add collect count values for all elements from pre-defined list
3390   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3391     element_info[collect_count_list[i].element].collect_count_initial =
3392       collect_count_list[i].count;
3393
3394   // ---------- initialize access direction -----------------------------------
3395
3396   // initialize access direction values to default (access from every side)
3397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3398     if (!IS_CUSTOM_ELEMENT(i))
3399       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3400
3401   // set access direction value for certain elements from pre-defined list
3402   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3403     element_info[access_direction_list[i].element].access_direction =
3404       access_direction_list[i].direction;
3405
3406   // ---------- initialize explosion content ----------------------------------
3407   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3408   {
3409     if (IS_CUSTOM_ELEMENT(i))
3410       continue;
3411
3412     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3413     {
3414       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3415
3416       element_info[i].content.e[x][y] =
3417         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3418          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3419          i == EL_PLAYER_3 ? EL_EMERALD :
3420          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3421          i == EL_MOLE ? EL_EMERALD_RED :
3422          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3423          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3424          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3425          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3426          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3427          i == EL_WALL_EMERALD ? EL_EMERALD :
3428          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3429          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3430          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3431          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3432          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3433          i == EL_WALL_PEARL ? EL_PEARL :
3434          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3435          EL_EMPTY);
3436     }
3437   }
3438
3439   // ---------- initialize recursion detection --------------------------------
3440   recursion_loop_depth = 0;
3441   recursion_loop_detected = FALSE;
3442   recursion_loop_element = EL_UNDEFINED;
3443
3444   // ---------- initialize graphics engine ------------------------------------
3445   game.scroll_delay_value =
3446     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3447      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3448      !setup.forced_scroll_delay           ? 0 :
3449      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3450   game.scroll_delay_value =
3451     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3452
3453   // ---------- initialize game engine snapshots ------------------------------
3454   for (i = 0; i < MAX_PLAYERS; i++)
3455     game.snapshot.last_action[i] = 0;
3456   game.snapshot.changed_action = FALSE;
3457   game.snapshot.collected_item = FALSE;
3458   game.snapshot.mode =
3459     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3460      SNAPSHOT_MODE_EVERY_STEP :
3461      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3462      SNAPSHOT_MODE_EVERY_MOVE :
3463      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3464      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3465   game.snapshot.save_snapshot = FALSE;
3466
3467   // ---------- initialize level time for Supaplex engine ---------------------
3468   // Supaplex levels with time limit currently unsupported -- should be added
3469   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3470     level.time = 0;
3471
3472   // ---------- initialize flags for handling game actions --------------------
3473
3474   // set flags for game actions to default values
3475   game.use_key_actions = TRUE;
3476   game.use_mouse_actions = FALSE;
3477
3478   // when using Mirror Magic game engine, handle mouse events only
3479   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3480   {
3481     game.use_key_actions = FALSE;
3482     game.use_mouse_actions = TRUE;
3483   }
3484
3485   // check for custom elements with mouse click events
3486   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3487   {
3488     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3489     {
3490       int element = EL_CUSTOM_START + i;
3491
3492       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3493           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3494           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3495           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3496         game.use_mouse_actions = TRUE;
3497     }
3498   }
3499 }
3500
3501 static int get_num_special_action(int element, int action_first,
3502                                   int action_last)
3503 {
3504   int num_special_action = 0;
3505   int i, j;
3506
3507   for (i = action_first; i <= action_last; i++)
3508   {
3509     boolean found = FALSE;
3510
3511     for (j = 0; j < NUM_DIRECTIONS; j++)
3512       if (el_act_dir2img(element, i, j) !=
3513           el_act_dir2img(element, ACTION_DEFAULT, j))
3514         found = TRUE;
3515
3516     if (found)
3517       num_special_action++;
3518     else
3519       break;
3520   }
3521
3522   return num_special_action;
3523 }
3524
3525
3526 // ============================================================================
3527 // InitGame()
3528 // ----------------------------------------------------------------------------
3529 // initialize and start new game
3530 // ============================================================================
3531
3532 #if DEBUG_INIT_PLAYER
3533 static void DebugPrintPlayerStatus(char *message)
3534 {
3535   int i;
3536
3537   if (!options.debug)
3538     return;
3539
3540   Debug("game:init:player", "%s:", message);
3541
3542   for (i = 0; i < MAX_PLAYERS; i++)
3543   {
3544     struct PlayerInfo *player = &stored_player[i];
3545
3546     Debug("game:init:player",
3547           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3548           i + 1,
3549           player->present,
3550           player->connected,
3551           player->connected_locally,
3552           player->connected_network,
3553           player->active,
3554           (local_player == player ? " (local player)" : ""));
3555   }
3556 }
3557 #endif
3558
3559 void InitGame(void)
3560 {
3561   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3562   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3563   int fade_mask = REDRAW_FIELD;
3564
3565   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3566   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3567   int initial_move_dir = MV_DOWN;
3568   int i, j, x, y;
3569
3570   // required here to update video display before fading (FIX THIS)
3571   DrawMaskedBorder(REDRAW_DOOR_2);
3572
3573   if (!game.restart_level)
3574     CloseDoor(DOOR_CLOSE_1);
3575
3576   SetGameStatus(GAME_MODE_PLAYING);
3577
3578   if (level_editor_test_game)
3579     FadeSkipNextFadeOut();
3580   else
3581     FadeSetEnterScreen();
3582
3583   if (CheckFadeAll())
3584     fade_mask = REDRAW_ALL;
3585
3586   FadeLevelSoundsAndMusic();
3587
3588   ExpireSoundLoops(TRUE);
3589
3590   FadeOut(fade_mask);
3591
3592   if (level_editor_test_game)
3593     FadeSkipNextFadeIn();
3594
3595   // needed if different viewport properties defined for playing
3596   ChangeViewportPropertiesIfNeeded();
3597
3598   ClearField();
3599
3600   DrawCompleteVideoDisplay();
3601
3602   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3603
3604   InitGameEngine();
3605   InitGameControlValues();
3606
3607   if (tape.recording)
3608   {
3609     // initialize tape actions from game when recording tape
3610     tape.use_key_actions   = game.use_key_actions;
3611     tape.use_mouse_actions = game.use_mouse_actions;
3612
3613     // initialize visible playfield size when recording tape (for team mode)
3614     tape.scr_fieldx = SCR_FIELDX;
3615     tape.scr_fieldy = SCR_FIELDY;
3616   }
3617
3618   // don't play tapes over network
3619   network_playing = (network.enabled && !tape.playing);
3620
3621   for (i = 0; i < MAX_PLAYERS; i++)
3622   {
3623     struct PlayerInfo *player = &stored_player[i];
3624
3625     player->index_nr = i;
3626     player->index_bit = (1 << i);
3627     player->element_nr = EL_PLAYER_1 + i;
3628
3629     player->present = FALSE;
3630     player->active = FALSE;
3631     player->mapped = FALSE;
3632
3633     player->killed = FALSE;
3634     player->reanimated = FALSE;
3635     player->buried = FALSE;
3636
3637     player->action = 0;
3638     player->effective_action = 0;
3639     player->programmed_action = 0;
3640     player->snap_action = 0;
3641
3642     player->mouse_action.lx = 0;
3643     player->mouse_action.ly = 0;
3644     player->mouse_action.button = 0;
3645     player->mouse_action.button_hint = 0;
3646
3647     player->effective_mouse_action.lx = 0;
3648     player->effective_mouse_action.ly = 0;
3649     player->effective_mouse_action.button = 0;
3650     player->effective_mouse_action.button_hint = 0;
3651
3652     for (j = 0; j < MAX_NUM_KEYS; j++)
3653       player->key[j] = FALSE;
3654
3655     player->num_white_keys = 0;
3656
3657     player->dynabomb_count = 0;
3658     player->dynabomb_size = 1;
3659     player->dynabombs_left = 0;
3660     player->dynabomb_xl = FALSE;
3661
3662     player->MovDir = initial_move_dir;
3663     player->MovPos = 0;
3664     player->GfxPos = 0;
3665     player->GfxDir = initial_move_dir;
3666     player->GfxAction = ACTION_DEFAULT;
3667     player->Frame = 0;
3668     player->StepFrame = 0;
3669
3670     player->initial_element = player->element_nr;
3671     player->artwork_element =
3672       (level.use_artwork_element[i] ? level.artwork_element[i] :
3673        player->element_nr);
3674     player->use_murphy = FALSE;
3675
3676     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3677     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3678
3679     player->gravity = level.initial_player_gravity[i];
3680
3681     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3682
3683     player->actual_frame_counter.count = 0;
3684     player->actual_frame_counter.value = 1;
3685
3686     player->step_counter = 0;
3687
3688     player->last_move_dir = initial_move_dir;
3689
3690     player->is_active = FALSE;
3691
3692     player->is_waiting = FALSE;
3693     player->is_moving = FALSE;
3694     player->is_auto_moving = FALSE;
3695     player->is_digging = FALSE;
3696     player->is_snapping = FALSE;
3697     player->is_collecting = FALSE;
3698     player->is_pushing = FALSE;
3699     player->is_switching = FALSE;
3700     player->is_dropping = FALSE;
3701     player->is_dropping_pressed = FALSE;
3702
3703     player->is_bored = FALSE;
3704     player->is_sleeping = FALSE;
3705
3706     player->was_waiting = TRUE;
3707     player->was_moving = FALSE;
3708     player->was_snapping = FALSE;
3709     player->was_dropping = FALSE;
3710
3711     player->force_dropping = FALSE;
3712
3713     player->frame_counter_bored = -1;
3714     player->frame_counter_sleeping = -1;
3715
3716     player->anim_delay_counter = 0;
3717     player->post_delay_counter = 0;
3718
3719     player->dir_waiting = initial_move_dir;
3720     player->action_waiting = ACTION_DEFAULT;
3721     player->last_action_waiting = ACTION_DEFAULT;
3722     player->special_action_bored = ACTION_DEFAULT;
3723     player->special_action_sleeping = ACTION_DEFAULT;
3724
3725     player->switch_x = -1;
3726     player->switch_y = -1;
3727
3728     player->drop_x = -1;
3729     player->drop_y = -1;
3730
3731     player->show_envelope = 0;
3732
3733     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3734
3735     player->push_delay       = -1;      // initialized when pushing starts
3736     player->push_delay_value = game.initial_push_delay_value;
3737
3738     player->drop_delay = 0;
3739     player->drop_pressed_delay = 0;
3740
3741     player->last_jx = -1;
3742     player->last_jy = -1;
3743     player->jx = -1;
3744     player->jy = -1;
3745
3746     player->shield_normal_time_left = 0;
3747     player->shield_deadly_time_left = 0;
3748
3749     player->last_removed_element = EL_UNDEFINED;
3750
3751     player->inventory_infinite_element = EL_UNDEFINED;
3752     player->inventory_size = 0;
3753
3754     if (level.use_initial_inventory[i])
3755     {
3756       for (j = 0; j < level.initial_inventory_size[i]; j++)
3757       {
3758         int element = level.initial_inventory_content[i][j];
3759         int collect_count = element_info[element].collect_count_initial;
3760         int k;
3761
3762         if (!IS_CUSTOM_ELEMENT(element))
3763           collect_count = 1;
3764
3765         if (collect_count == 0)
3766           player->inventory_infinite_element = element;
3767         else
3768           for (k = 0; k < collect_count; k++)
3769             if (player->inventory_size < MAX_INVENTORY_SIZE)
3770               player->inventory_element[player->inventory_size++] = element;
3771       }
3772     }
3773
3774     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3775     SnapField(player, 0, 0);
3776
3777     map_player_action[i] = i;
3778   }
3779
3780   network_player_action_received = FALSE;
3781
3782   // initial null action
3783   if (network_playing)
3784     SendToServer_MovePlayer(MV_NONE);
3785
3786   FrameCounter = 0;
3787   TimeFrames = 0;
3788   TimePlayed = 0;
3789   TimeLeft = level.time;
3790   TapeTime = 0;
3791
3792   ScreenMovDir = MV_NONE;
3793   ScreenMovPos = 0;
3794   ScreenGfxPos = 0;
3795
3796   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3797
3798   game.robot_wheel_x = -1;
3799   game.robot_wheel_y = -1;
3800
3801   game.exit_x = -1;
3802   game.exit_y = -1;
3803
3804   game.all_players_gone = FALSE;
3805
3806   game.LevelSolved = FALSE;
3807   game.GameOver = FALSE;
3808
3809   game.GamePlayed = !tape.playing;
3810
3811   game.LevelSolved_GameWon = FALSE;
3812   game.LevelSolved_GameEnd = FALSE;
3813   game.LevelSolved_SaveTape = FALSE;
3814   game.LevelSolved_SaveScore = FALSE;
3815
3816   game.LevelSolved_CountingTime = 0;
3817   game.LevelSolved_CountingScore = 0;
3818   game.LevelSolved_CountingHealth = 0;
3819
3820   game.panel.active = TRUE;
3821
3822   game.no_level_time_limit = (level.time == 0);
3823   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3824
3825   game.yamyam_content_nr = 0;
3826   game.robot_wheel_active = FALSE;
3827   game.magic_wall_active = FALSE;
3828   game.magic_wall_time_left = 0;
3829   game.light_time_left = 0;
3830   game.timegate_time_left = 0;
3831   game.switchgate_pos = 0;
3832   game.wind_direction = level.wind_direction_initial;
3833
3834   game.time_final = 0;
3835   game.score_time_final = 0;
3836
3837   game.score = 0;
3838   game.score_final = 0;
3839
3840   game.health = MAX_HEALTH;
3841   game.health_final = MAX_HEALTH;
3842
3843   game.gems_still_needed = level.gems_needed;
3844   game.sokoban_fields_still_needed = 0;
3845   game.sokoban_objects_still_needed = 0;
3846   game.lights_still_needed = 0;
3847   game.players_still_needed = 0;
3848   game.friends_still_needed = 0;
3849
3850   game.lenses_time_left = 0;
3851   game.magnify_time_left = 0;
3852
3853   game.ball_active = level.ball_active_initial;
3854   game.ball_content_nr = 0;
3855
3856   game.explosions_delayed = TRUE;
3857
3858   game.envelope_active = FALSE;
3859
3860   // special case: set custom artwork setting to initial value
3861   game.use_masked_elements = game.use_masked_elements_initial;
3862
3863   for (i = 0; i < NUM_BELTS; i++)
3864   {
3865     game.belt_dir[i] = MV_NONE;
3866     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3867   }
3868
3869   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3870     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3871
3872 #if DEBUG_INIT_PLAYER
3873   DebugPrintPlayerStatus("Player status at level initialization");
3874 #endif
3875
3876   SCAN_PLAYFIELD(x, y)
3877   {
3878     Tile[x][y] = Last[x][y] = level.field[x][y];
3879     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3880     ChangeDelay[x][y] = 0;
3881     ChangePage[x][y] = -1;
3882     CustomValue[x][y] = 0;              // initialized in InitField()
3883     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3884     AmoebaNr[x][y] = 0;
3885     WasJustMoving[x][y] = 0;
3886     WasJustFalling[x][y] = 0;
3887     CheckCollision[x][y] = 0;
3888     CheckImpact[x][y] = 0;
3889     Stop[x][y] = FALSE;
3890     Pushed[x][y] = FALSE;
3891
3892     ChangeCount[x][y] = 0;
3893     ChangeEvent[x][y] = -1;
3894
3895     ExplodePhase[x][y] = 0;
3896     ExplodeDelay[x][y] = 0;
3897     ExplodeField[x][y] = EX_TYPE_NONE;
3898
3899     RunnerVisit[x][y] = 0;
3900     PlayerVisit[x][y] = 0;
3901
3902     GfxFrame[x][y] = 0;
3903     GfxRandom[x][y] = INIT_GFX_RANDOM();
3904     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3905     GfxElement[x][y] = EL_UNDEFINED;
3906     GfxElementEmpty[x][y] = EL_EMPTY;
3907     GfxAction[x][y] = ACTION_DEFAULT;
3908     GfxDir[x][y] = MV_NONE;
3909     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3910   }
3911
3912   SCAN_PLAYFIELD(x, y)
3913   {
3914     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3915       emulate_bd = FALSE;
3916     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3917       emulate_sp = FALSE;
3918
3919     InitField(x, y, TRUE);
3920
3921     ResetGfxAnimation(x, y);
3922   }
3923
3924   InitBeltMovement();
3925
3926   for (i = 0; i < MAX_PLAYERS; i++)
3927   {
3928     struct PlayerInfo *player = &stored_player[i];
3929
3930     // set number of special actions for bored and sleeping animation
3931     player->num_special_action_bored =
3932       get_num_special_action(player->artwork_element,
3933                              ACTION_BORING_1, ACTION_BORING_LAST);
3934     player->num_special_action_sleeping =
3935       get_num_special_action(player->artwork_element,
3936                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3937   }
3938
3939   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3940                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3941
3942   // initialize type of slippery elements
3943   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3944   {
3945     if (!IS_CUSTOM_ELEMENT(i))
3946     {
3947       // default: elements slip down either to the left or right randomly
3948       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3949
3950       // SP style elements prefer to slip down on the left side
3951       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3952         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3953
3954       // BD style elements prefer to slip down on the left side
3955       if (game.emulation == EMU_BOULDERDASH)
3956         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3957     }
3958   }
3959
3960   // initialize explosion and ignition delay
3961   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3962   {
3963     if (!IS_CUSTOM_ELEMENT(i))
3964     {
3965       int num_phase = 8;
3966       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3967                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3968                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3969       int last_phase = (num_phase + 1) * delay;
3970       int half_phase = (num_phase / 2) * delay;
3971
3972       element_info[i].explosion_delay = last_phase - 1;
3973       element_info[i].ignition_delay = half_phase;
3974
3975       if (i == EL_BLACK_ORB)
3976         element_info[i].ignition_delay = 1;
3977     }
3978   }
3979
3980   // correct non-moving belts to start moving left
3981   for (i = 0; i < NUM_BELTS; i++)
3982     if (game.belt_dir[i] == MV_NONE)
3983       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3984
3985 #if USE_NEW_PLAYER_ASSIGNMENTS
3986   // use preferred player also in local single-player mode
3987   if (!network.enabled && !game.team_mode)
3988   {
3989     int new_index_nr = setup.network_player_nr;
3990
3991     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3992     {
3993       for (i = 0; i < MAX_PLAYERS; i++)
3994         stored_player[i].connected_locally = FALSE;
3995
3996       stored_player[new_index_nr].connected_locally = TRUE;
3997     }
3998   }
3999
4000   for (i = 0; i < MAX_PLAYERS; i++)
4001   {
4002     stored_player[i].connected = FALSE;
4003
4004     // in network game mode, the local player might not be the first player
4005     if (stored_player[i].connected_locally)
4006       local_player = &stored_player[i];
4007   }
4008
4009   if (!network.enabled)
4010     local_player->connected = TRUE;
4011
4012   if (tape.playing)
4013   {
4014     for (i = 0; i < MAX_PLAYERS; i++)
4015       stored_player[i].connected = tape.player_participates[i];
4016   }
4017   else if (network.enabled)
4018   {
4019     // add team mode players connected over the network (needed for correct
4020     // assignment of player figures from level to locally playing players)
4021
4022     for (i = 0; i < MAX_PLAYERS; i++)
4023       if (stored_player[i].connected_network)
4024         stored_player[i].connected = TRUE;
4025   }
4026   else if (game.team_mode)
4027   {
4028     // try to guess locally connected team mode players (needed for correct
4029     // assignment of player figures from level to locally playing players)
4030
4031     for (i = 0; i < MAX_PLAYERS; i++)
4032       if (setup.input[i].use_joystick ||
4033           setup.input[i].key.left != KSYM_UNDEFINED)
4034         stored_player[i].connected = TRUE;
4035   }
4036
4037 #if DEBUG_INIT_PLAYER
4038   DebugPrintPlayerStatus("Player status after level initialization");
4039 #endif
4040
4041 #if DEBUG_INIT_PLAYER
4042   Debug("game:init:player", "Reassigning players ...");
4043 #endif
4044
4045   // check if any connected player was not found in playfield
4046   for (i = 0; i < MAX_PLAYERS; i++)
4047   {
4048     struct PlayerInfo *player = &stored_player[i];
4049
4050     if (player->connected && !player->present)
4051     {
4052       struct PlayerInfo *field_player = NULL;
4053
4054 #if DEBUG_INIT_PLAYER
4055       Debug("game:init:player",
4056             "- looking for field player for player %d ...", i + 1);
4057 #endif
4058
4059       // assign first free player found that is present in the playfield
4060
4061       // first try: look for unmapped playfield player that is not connected
4062       for (j = 0; j < MAX_PLAYERS; j++)
4063         if (field_player == NULL &&
4064             stored_player[j].present &&
4065             !stored_player[j].mapped &&
4066             !stored_player[j].connected)
4067           field_player = &stored_player[j];
4068
4069       // second try: look for *any* unmapped playfield player
4070       for (j = 0; j < MAX_PLAYERS; j++)
4071         if (field_player == NULL &&
4072             stored_player[j].present &&
4073             !stored_player[j].mapped)
4074           field_player = &stored_player[j];
4075
4076       if (field_player != NULL)
4077       {
4078         int jx = field_player->jx, jy = field_player->jy;
4079
4080 #if DEBUG_INIT_PLAYER
4081         Debug("game:init:player", "- found player %d",
4082               field_player->index_nr + 1);
4083 #endif
4084
4085         player->present = FALSE;
4086         player->active = FALSE;
4087
4088         field_player->present = TRUE;
4089         field_player->active = TRUE;
4090
4091         /*
4092         player->initial_element = field_player->initial_element;
4093         player->artwork_element = field_player->artwork_element;
4094
4095         player->block_last_field       = field_player->block_last_field;
4096         player->block_delay_adjustment = field_player->block_delay_adjustment;
4097         */
4098
4099         StorePlayer[jx][jy] = field_player->element_nr;
4100
4101         field_player->jx = field_player->last_jx = jx;
4102         field_player->jy = field_player->last_jy = jy;
4103
4104         if (local_player == player)
4105           local_player = field_player;
4106
4107         map_player_action[field_player->index_nr] = i;
4108
4109         field_player->mapped = TRUE;
4110
4111 #if DEBUG_INIT_PLAYER
4112         Debug("game:init:player", "- map_player_action[%d] == %d",
4113               field_player->index_nr + 1, i + 1);
4114 #endif
4115       }
4116     }
4117
4118     if (player->connected && player->present)
4119       player->mapped = TRUE;
4120   }
4121
4122 #if DEBUG_INIT_PLAYER
4123   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4124 #endif
4125
4126 #else
4127
4128   // check if any connected player was not found in playfield
4129   for (i = 0; i < MAX_PLAYERS; i++)
4130   {
4131     struct PlayerInfo *player = &stored_player[i];
4132
4133     if (player->connected && !player->present)
4134     {
4135       for (j = 0; j < MAX_PLAYERS; j++)
4136       {
4137         struct PlayerInfo *field_player = &stored_player[j];
4138         int jx = field_player->jx, jy = field_player->jy;
4139
4140         // assign first free player found that is present in the playfield
4141         if (field_player->present && !field_player->connected)
4142         {
4143           player->present = TRUE;
4144           player->active = TRUE;
4145
4146           field_player->present = FALSE;
4147           field_player->active = FALSE;
4148
4149           player->initial_element = field_player->initial_element;
4150           player->artwork_element = field_player->artwork_element;
4151
4152           player->block_last_field       = field_player->block_last_field;
4153           player->block_delay_adjustment = field_player->block_delay_adjustment;
4154
4155           StorePlayer[jx][jy] = player->element_nr;
4156
4157           player->jx = player->last_jx = jx;
4158           player->jy = player->last_jy = jy;
4159
4160           break;
4161         }
4162       }
4163     }
4164   }
4165 #endif
4166
4167 #if 0
4168   Debug("game:init:player", "local_player->present == %d",
4169         local_player->present);
4170 #endif
4171
4172   // set focus to local player for network games, else to all players
4173   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4174   game.centered_player_nr_next = game.centered_player_nr;
4175   game.set_centered_player = FALSE;
4176   game.set_centered_player_wrap = FALSE;
4177
4178   if (network_playing && tape.recording)
4179   {
4180     // store client dependent player focus when recording network games
4181     tape.centered_player_nr_next = game.centered_player_nr_next;
4182     tape.set_centered_player = TRUE;
4183   }
4184
4185   if (tape.playing)
4186   {
4187     // when playing a tape, eliminate all players who do not participate
4188
4189 #if USE_NEW_PLAYER_ASSIGNMENTS
4190
4191     if (!game.team_mode)
4192     {
4193       for (i = 0; i < MAX_PLAYERS; i++)
4194       {
4195         if (stored_player[i].active &&
4196             !tape.player_participates[map_player_action[i]])
4197         {
4198           struct PlayerInfo *player = &stored_player[i];
4199           int jx = player->jx, jy = player->jy;
4200
4201 #if DEBUG_INIT_PLAYER
4202           Debug("game:init:player", "Removing player %d at (%d, %d)",
4203                 i + 1, jx, jy);
4204 #endif
4205
4206           player->active = FALSE;
4207           StorePlayer[jx][jy] = 0;
4208           Tile[jx][jy] = EL_EMPTY;
4209         }
4210       }
4211     }
4212
4213 #else
4214
4215     for (i = 0; i < MAX_PLAYERS; i++)
4216     {
4217       if (stored_player[i].active &&
4218           !tape.player_participates[i])
4219       {
4220         struct PlayerInfo *player = &stored_player[i];
4221         int jx = player->jx, jy = player->jy;
4222
4223         player->active = FALSE;
4224         StorePlayer[jx][jy] = 0;
4225         Tile[jx][jy] = EL_EMPTY;
4226       }
4227     }
4228 #endif
4229   }
4230   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4231   {
4232     // when in single player mode, eliminate all but the local player
4233
4234     for (i = 0; i < MAX_PLAYERS; i++)
4235     {
4236       struct PlayerInfo *player = &stored_player[i];
4237
4238       if (player->active && player != local_player)
4239       {
4240         int jx = player->jx, jy = player->jy;
4241
4242         player->active = FALSE;
4243         player->present = FALSE;
4244
4245         StorePlayer[jx][jy] = 0;
4246         Tile[jx][jy] = EL_EMPTY;
4247       }
4248     }
4249   }
4250
4251   for (i = 0; i < MAX_PLAYERS; i++)
4252     if (stored_player[i].active)
4253       game.players_still_needed++;
4254
4255   if (level.solved_by_one_player)
4256     game.players_still_needed = 1;
4257
4258   // when recording the game, store which players take part in the game
4259   if (tape.recording)
4260   {
4261 #if USE_NEW_PLAYER_ASSIGNMENTS
4262     for (i = 0; i < MAX_PLAYERS; i++)
4263       if (stored_player[i].connected)
4264         tape.player_participates[i] = TRUE;
4265 #else
4266     for (i = 0; i < MAX_PLAYERS; i++)
4267       if (stored_player[i].active)
4268         tape.player_participates[i] = TRUE;
4269 #endif
4270   }
4271
4272 #if DEBUG_INIT_PLAYER
4273   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4274 #endif
4275
4276   if (BorderElement == EL_EMPTY)
4277   {
4278     SBX_Left = 0;
4279     SBX_Right = lev_fieldx - SCR_FIELDX;
4280     SBY_Upper = 0;
4281     SBY_Lower = lev_fieldy - SCR_FIELDY;
4282   }
4283   else
4284   {
4285     SBX_Left = -1;
4286     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4287     SBY_Upper = -1;
4288     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4289   }
4290
4291   if (full_lev_fieldx <= SCR_FIELDX)
4292     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4293   if (full_lev_fieldy <= SCR_FIELDY)
4294     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4295
4296   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4297     SBX_Left--;
4298   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4299     SBY_Upper--;
4300
4301   // if local player not found, look for custom element that might create
4302   // the player (make some assumptions about the right custom element)
4303   if (!local_player->present)
4304   {
4305     int start_x = 0, start_y = 0;
4306     int found_rating = 0;
4307     int found_element = EL_UNDEFINED;
4308     int player_nr = local_player->index_nr;
4309
4310     SCAN_PLAYFIELD(x, y)
4311     {
4312       int element = Tile[x][y];
4313       int content;
4314       int xx, yy;
4315       boolean is_player;
4316
4317       if (level.use_start_element[player_nr] &&
4318           level.start_element[player_nr] == element &&
4319           found_rating < 4)
4320       {
4321         start_x = x;
4322         start_y = y;
4323
4324         found_rating = 4;
4325         found_element = element;
4326       }
4327
4328       if (!IS_CUSTOM_ELEMENT(element))
4329         continue;
4330
4331       if (CAN_CHANGE(element))
4332       {
4333         for (i = 0; i < element_info[element].num_change_pages; i++)
4334         {
4335           // check for player created from custom element as single target
4336           content = element_info[element].change_page[i].target_element;
4337           is_player = IS_PLAYER_ELEMENT(content);
4338
4339           if (is_player && (found_rating < 3 ||
4340                             (found_rating == 3 && element < found_element)))
4341           {
4342             start_x = x;
4343             start_y = y;
4344
4345             found_rating = 3;
4346             found_element = element;
4347           }
4348         }
4349       }
4350
4351       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4352       {
4353         // check for player created from custom element as explosion content
4354         content = element_info[element].content.e[xx][yy];
4355         is_player = IS_PLAYER_ELEMENT(content);
4356
4357         if (is_player && (found_rating < 2 ||
4358                           (found_rating == 2 && element < found_element)))
4359         {
4360           start_x = x + xx - 1;
4361           start_y = y + yy - 1;
4362
4363           found_rating = 2;
4364           found_element = element;
4365         }
4366
4367         if (!CAN_CHANGE(element))
4368           continue;
4369
4370         for (i = 0; i < element_info[element].num_change_pages; i++)
4371         {
4372           // check for player created from custom element as extended target
4373           content =
4374             element_info[element].change_page[i].target_content.e[xx][yy];
4375
4376           is_player = IS_PLAYER_ELEMENT(content);
4377
4378           if (is_player && (found_rating < 1 ||
4379                             (found_rating == 1 && element < found_element)))
4380           {
4381             start_x = x + xx - 1;
4382             start_y = y + yy - 1;
4383
4384             found_rating = 1;
4385             found_element = element;
4386           }
4387         }
4388       }
4389     }
4390
4391     scroll_x = SCROLL_POSITION_X(start_x);
4392     scroll_y = SCROLL_POSITION_Y(start_y);
4393   }
4394   else
4395   {
4396     scroll_x = SCROLL_POSITION_X(local_player->jx);
4397     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4398   }
4399
4400   // !!! FIX THIS (START) !!!
4401   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4402   {
4403     InitGameEngine_EM();
4404   }
4405   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4406   {
4407     InitGameEngine_SP();
4408   }
4409   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4410   {
4411     InitGameEngine_MM();
4412   }
4413   else
4414   {
4415     DrawLevel(REDRAW_FIELD);
4416     DrawAllPlayers();
4417
4418     // after drawing the level, correct some elements
4419     if (game.timegate_time_left == 0)
4420       CloseAllOpenTimegates();
4421   }
4422
4423   // blit playfield from scroll buffer to normal back buffer for fading in
4424   BlitScreenToBitmap(backbuffer);
4425   // !!! FIX THIS (END) !!!
4426
4427   DrawMaskedBorder(fade_mask);
4428
4429   FadeIn(fade_mask);
4430
4431 #if 1
4432   // full screen redraw is required at this point in the following cases:
4433   // - special editor door undrawn when game was started from level editor
4434   // - drawing area (playfield) was changed and has to be removed completely
4435   redraw_mask = REDRAW_ALL;
4436   BackToFront();
4437 #endif
4438
4439   if (!game.restart_level)
4440   {
4441     // copy default game door content to main double buffer
4442
4443     // !!! CHECK AGAIN !!!
4444     SetPanelBackground();
4445     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4446     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4447   }
4448
4449   SetPanelBackground();
4450   SetDrawBackgroundMask(REDRAW_DOOR_1);
4451
4452   UpdateAndDisplayGameControlValues();
4453
4454   if (!game.restart_level)
4455   {
4456     UnmapGameButtons();
4457     UnmapTapeButtons();
4458
4459     FreeGameButtons();
4460     CreateGameButtons();
4461
4462     MapGameButtons();
4463     MapTapeButtons();
4464
4465     // copy actual game door content to door double buffer for OpenDoor()
4466     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4467
4468     OpenDoor(DOOR_OPEN_ALL);
4469
4470     KeyboardAutoRepeatOffUnlessAutoplay();
4471
4472 #if DEBUG_INIT_PLAYER
4473     DebugPrintPlayerStatus("Player status (final)");
4474 #endif
4475   }
4476
4477   UnmapAllGadgets();
4478
4479   MapGameButtons();
4480   MapTapeButtons();
4481
4482   if (!game.restart_level && !tape.playing)
4483   {
4484     LevelStats_incPlayed(level_nr);
4485
4486     SaveLevelSetup_SeriesInfo();
4487   }
4488
4489   game.restart_level = FALSE;
4490   game.restart_game_message = NULL;
4491
4492   game.request_active = FALSE;
4493   game.request_active_or_moving = FALSE;
4494
4495   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4496     InitGameActions_MM();
4497
4498   SaveEngineSnapshotToListInitial();
4499
4500   if (!game.restart_level)
4501   {
4502     PlaySound(SND_GAME_STARTING);
4503
4504     if (setup.sound_music)
4505       PlayLevelMusic();
4506   }
4507
4508   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4509 }
4510
4511 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4512                         int actual_player_x, int actual_player_y)
4513 {
4514   // this is used for non-R'n'D game engines to update certain engine values
4515
4516   // needed to determine if sounds are played within the visible screen area
4517   scroll_x = actual_scroll_x;
4518   scroll_y = actual_scroll_y;
4519
4520   // needed to get player position for "follow finger" playing input method
4521   local_player->jx = actual_player_x;
4522   local_player->jy = actual_player_y;
4523 }
4524
4525 void InitMovDir(int x, int y)
4526 {
4527   int i, element = Tile[x][y];
4528   static int xy[4][2] =
4529   {
4530     {  0, +1 },
4531     { +1,  0 },
4532     {  0, -1 },
4533     { -1,  0 }
4534   };
4535   static int direction[3][4] =
4536   {
4537     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4538     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4539     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4540   };
4541
4542   switch (element)
4543   {
4544     case EL_BUG_RIGHT:
4545     case EL_BUG_UP:
4546     case EL_BUG_LEFT:
4547     case EL_BUG_DOWN:
4548       Tile[x][y] = EL_BUG;
4549       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4550       break;
4551
4552     case EL_SPACESHIP_RIGHT:
4553     case EL_SPACESHIP_UP:
4554     case EL_SPACESHIP_LEFT:
4555     case EL_SPACESHIP_DOWN:
4556       Tile[x][y] = EL_SPACESHIP;
4557       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4558       break;
4559
4560     case EL_BD_BUTTERFLY_RIGHT:
4561     case EL_BD_BUTTERFLY_UP:
4562     case EL_BD_BUTTERFLY_LEFT:
4563     case EL_BD_BUTTERFLY_DOWN:
4564       Tile[x][y] = EL_BD_BUTTERFLY;
4565       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4566       break;
4567
4568     case EL_BD_FIREFLY_RIGHT:
4569     case EL_BD_FIREFLY_UP:
4570     case EL_BD_FIREFLY_LEFT:
4571     case EL_BD_FIREFLY_DOWN:
4572       Tile[x][y] = EL_BD_FIREFLY;
4573       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4574       break;
4575
4576     case EL_PACMAN_RIGHT:
4577     case EL_PACMAN_UP:
4578     case EL_PACMAN_LEFT:
4579     case EL_PACMAN_DOWN:
4580       Tile[x][y] = EL_PACMAN;
4581       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4582       break;
4583
4584     case EL_YAMYAM_LEFT:
4585     case EL_YAMYAM_RIGHT:
4586     case EL_YAMYAM_UP:
4587     case EL_YAMYAM_DOWN:
4588       Tile[x][y] = EL_YAMYAM;
4589       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4590       break;
4591
4592     case EL_SP_SNIKSNAK:
4593       MovDir[x][y] = MV_UP;
4594       break;
4595
4596     case EL_SP_ELECTRON:
4597       MovDir[x][y] = MV_LEFT;
4598       break;
4599
4600     case EL_MOLE_LEFT:
4601     case EL_MOLE_RIGHT:
4602     case EL_MOLE_UP:
4603     case EL_MOLE_DOWN:
4604       Tile[x][y] = EL_MOLE;
4605       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4606       break;
4607
4608     case EL_SPRING_LEFT:
4609     case EL_SPRING_RIGHT:
4610       Tile[x][y] = EL_SPRING;
4611       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4612       break;
4613
4614     default:
4615       if (IS_CUSTOM_ELEMENT(element))
4616       {
4617         struct ElementInfo *ei = &element_info[element];
4618         int move_direction_initial = ei->move_direction_initial;
4619         int move_pattern = ei->move_pattern;
4620
4621         if (move_direction_initial == MV_START_PREVIOUS)
4622         {
4623           if (MovDir[x][y] != MV_NONE)
4624             return;
4625
4626           move_direction_initial = MV_START_AUTOMATIC;
4627         }
4628
4629         if (move_direction_initial == MV_START_RANDOM)
4630           MovDir[x][y] = 1 << RND(4);
4631         else if (move_direction_initial & MV_ANY_DIRECTION)
4632           MovDir[x][y] = move_direction_initial;
4633         else if (move_pattern == MV_ALL_DIRECTIONS ||
4634                  move_pattern == MV_TURNING_LEFT ||
4635                  move_pattern == MV_TURNING_RIGHT ||
4636                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4637                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4638                  move_pattern == MV_TURNING_RANDOM)
4639           MovDir[x][y] = 1 << RND(4);
4640         else if (move_pattern == MV_HORIZONTAL)
4641           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4642         else if (move_pattern == MV_VERTICAL)
4643           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4644         else if (move_pattern & MV_ANY_DIRECTION)
4645           MovDir[x][y] = element_info[element].move_pattern;
4646         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4647                  move_pattern == MV_ALONG_RIGHT_SIDE)
4648         {
4649           // use random direction as default start direction
4650           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4651             MovDir[x][y] = 1 << RND(4);
4652
4653           for (i = 0; i < NUM_DIRECTIONS; i++)
4654           {
4655             int x1 = x + xy[i][0];
4656             int y1 = y + xy[i][1];
4657
4658             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4659             {
4660               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4661                 MovDir[x][y] = direction[0][i];
4662               else
4663                 MovDir[x][y] = direction[1][i];
4664
4665               break;
4666             }
4667           }
4668         }                
4669       }
4670       else
4671       {
4672         MovDir[x][y] = 1 << RND(4);
4673
4674         if (element != EL_BUG &&
4675             element != EL_SPACESHIP &&
4676             element != EL_BD_BUTTERFLY &&
4677             element != EL_BD_FIREFLY)
4678           break;
4679
4680         for (i = 0; i < NUM_DIRECTIONS; i++)
4681         {
4682           int x1 = x + xy[i][0];
4683           int y1 = y + xy[i][1];
4684
4685           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4686           {
4687             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4688             {
4689               MovDir[x][y] = direction[0][i];
4690               break;
4691             }
4692             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4693                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4694             {
4695               MovDir[x][y] = direction[1][i];
4696               break;
4697             }
4698           }
4699         }
4700       }
4701       break;
4702   }
4703
4704   GfxDir[x][y] = MovDir[x][y];
4705 }
4706
4707 void InitAmoebaNr(int x, int y)
4708 {
4709   int i;
4710   int group_nr = AmoebaNeighbourNr(x, y);
4711
4712   if (group_nr == 0)
4713   {
4714     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4715     {
4716       if (AmoebaCnt[i] == 0)
4717       {
4718         group_nr = i;
4719         break;
4720       }
4721     }
4722   }
4723
4724   AmoebaNr[x][y] = group_nr;
4725   AmoebaCnt[group_nr]++;
4726   AmoebaCnt2[group_nr]++;
4727 }
4728
4729 static void LevelSolved_SetFinalGameValues(void)
4730 {
4731   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4732   game.score_time_final = (level.use_step_counter ? TimePlayed :
4733                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4734
4735   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4736                       game_em.lev->score :
4737                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4738                       game_mm.score :
4739                       game.score);
4740
4741   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4742                        MM_HEALTH(game_mm.laser_overload_value) :
4743                        game.health);
4744
4745   game.LevelSolved_CountingTime = game.time_final;
4746   game.LevelSolved_CountingScore = game.score_final;
4747   game.LevelSolved_CountingHealth = game.health_final;
4748 }
4749
4750 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4751 {
4752   game.LevelSolved_CountingTime = time;
4753   game.LevelSolved_CountingScore = score;
4754   game.LevelSolved_CountingHealth = health;
4755
4756   game_panel_controls[GAME_PANEL_TIME].value = time;
4757   game_panel_controls[GAME_PANEL_SCORE].value = score;
4758   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4759
4760   DisplayGameControlValues();
4761 }
4762
4763 static void LevelSolved(void)
4764 {
4765   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4766       game.players_still_needed > 0)
4767     return;
4768
4769   game.LevelSolved = TRUE;
4770   game.GameOver = TRUE;
4771
4772   // needed here to display correct panel values while player walks into exit
4773   LevelSolved_SetFinalGameValues();
4774 }
4775
4776 void GameWon(void)
4777 {
4778   static int time_count_steps;
4779   static int time, time_final;
4780   static float score, score_final; // needed for time score < 10 for 10 seconds
4781   static int health, health_final;
4782   static int game_over_delay_1 = 0;
4783   static int game_over_delay_2 = 0;
4784   static int game_over_delay_3 = 0;
4785   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4786   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4787
4788   if (!game.LevelSolved_GameWon)
4789   {
4790     int i;
4791
4792     // do not start end game actions before the player stops moving (to exit)
4793     if (local_player->active && local_player->MovPos)
4794       return;
4795
4796     // calculate final game values after player finished walking into exit
4797     LevelSolved_SetFinalGameValues();
4798
4799     game.LevelSolved_GameWon = TRUE;
4800     game.LevelSolved_SaveTape = tape.recording;
4801     game.LevelSolved_SaveScore = !tape.playing;
4802
4803     if (!tape.playing)
4804     {
4805       LevelStats_incSolved(level_nr);
4806
4807       SaveLevelSetup_SeriesInfo();
4808     }
4809
4810     if (tape.auto_play)         // tape might already be stopped here
4811       tape.auto_play_level_solved = TRUE;
4812
4813     TapeStop();
4814
4815     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4816     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4817     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4818
4819     time = time_final = game.time_final;
4820     score = score_final = game.score_final;
4821     health = health_final = game.health_final;
4822
4823     // update game panel values before (delayed) counting of score (if any)
4824     LevelSolved_DisplayFinalGameValues(time, score, health);
4825
4826     // if level has time score defined, calculate new final game values
4827     if (time_score > 0)
4828     {
4829       int time_final_max = 999;
4830       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4831       int time_frames = 0;
4832       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4833       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4834
4835       if (TimeLeft > 0)
4836       {
4837         time_final = 0;
4838         time_frames = time_frames_left;
4839       }
4840       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4841       {
4842         time_final = time_final_max;
4843         time_frames = time_frames_final_max - time_frames_played;
4844       }
4845
4846       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4847
4848       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4849
4850       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4851       {
4852         health_final = 0;
4853         score_final += health * time_score;
4854       }
4855
4856       game.score_final = score_final;
4857       game.health_final = health_final;
4858     }
4859
4860     // if not counting score after game, immediately update game panel values
4861     if (level_editor_test_game || !setup.count_score_after_game)
4862     {
4863       time = time_final;
4864       score = score_final;
4865
4866       LevelSolved_DisplayFinalGameValues(time, score, health);
4867     }
4868
4869     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4870     {
4871       // check if last player has left the level
4872       if (game.exit_x >= 0 &&
4873           game.exit_y >= 0)
4874       {
4875         int x = game.exit_x;
4876         int y = game.exit_y;
4877         int element = Tile[x][y];
4878
4879         // close exit door after last player
4880         if ((game.all_players_gone &&
4881              (element == EL_EXIT_OPEN ||
4882               element == EL_SP_EXIT_OPEN ||
4883               element == EL_STEEL_EXIT_OPEN)) ||
4884             element == EL_EM_EXIT_OPEN ||
4885             element == EL_EM_STEEL_EXIT_OPEN)
4886         {
4887
4888           Tile[x][y] =
4889             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4890              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4891              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4892              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4893              EL_EM_STEEL_EXIT_CLOSING);
4894
4895           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4896         }
4897
4898         // player disappears
4899         DrawLevelField(x, y);
4900       }
4901
4902       for (i = 0; i < MAX_PLAYERS; i++)
4903       {
4904         struct PlayerInfo *player = &stored_player[i];
4905
4906         if (player->present)
4907         {
4908           RemovePlayer(player);
4909
4910           // player disappears
4911           DrawLevelField(player->jx, player->jy);
4912         }
4913       }
4914     }
4915
4916     PlaySound(SND_GAME_WINNING);
4917   }
4918
4919   if (setup.count_score_after_game)
4920   {
4921     if (time != time_final)
4922     {
4923       if (game_over_delay_1 > 0)
4924       {
4925         game_over_delay_1--;
4926
4927         return;
4928       }
4929
4930       int time_to_go = ABS(time_final - time);
4931       int time_count_dir = (time < time_final ? +1 : -1);
4932
4933       if (time_to_go < time_count_steps)
4934         time_count_steps = 1;
4935
4936       time  += time_count_steps * time_count_dir;
4937       score += time_count_steps * time_score;
4938
4939       // set final score to correct rounding differences after counting score
4940       if (time == time_final)
4941         score = score_final;
4942
4943       LevelSolved_DisplayFinalGameValues(time, score, health);
4944
4945       if (time == time_final)
4946         StopSound(SND_GAME_LEVELTIME_BONUS);
4947       else if (setup.sound_loops)
4948         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4949       else
4950         PlaySound(SND_GAME_LEVELTIME_BONUS);
4951
4952       return;
4953     }
4954
4955     if (health != health_final)
4956     {
4957       if (game_over_delay_2 > 0)
4958       {
4959         game_over_delay_2--;
4960
4961         return;
4962       }
4963
4964       int health_count_dir = (health < health_final ? +1 : -1);
4965
4966       health += health_count_dir;
4967       score  += time_score;
4968
4969       LevelSolved_DisplayFinalGameValues(time, score, health);
4970
4971       if (health == health_final)
4972         StopSound(SND_GAME_LEVELTIME_BONUS);
4973       else if (setup.sound_loops)
4974         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4975       else
4976         PlaySound(SND_GAME_LEVELTIME_BONUS);
4977
4978       return;
4979     }
4980   }
4981
4982   game.panel.active = FALSE;
4983
4984   if (game_over_delay_3 > 0)
4985   {
4986     game_over_delay_3--;
4987
4988     return;
4989   }
4990
4991   GameEnd();
4992 }
4993
4994 void GameEnd(void)
4995 {
4996   // used instead of "level_nr" (needed for network games)
4997   int last_level_nr = levelset.level_nr;
4998   boolean tape_saved = FALSE;
4999
5000   game.LevelSolved_GameEnd = TRUE;
5001
5002   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5003   {
5004     // make sure that request dialog to save tape does not open door again
5005     if (!global.use_envelope_request)
5006       CloseDoor(DOOR_CLOSE_1);
5007
5008     // ask to save tape
5009     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5010
5011     // set unique basename for score tape (also saved in high score table)
5012     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5013   }
5014
5015   // if no tape is to be saved, close both doors simultaneously
5016   CloseDoor(DOOR_CLOSE_ALL);
5017
5018   if (level_editor_test_game || score_info_tape_play)
5019   {
5020     SetGameStatus(GAME_MODE_MAIN);
5021
5022     DrawMainMenu();
5023
5024     return;
5025   }
5026
5027   if (!game.LevelSolved_SaveScore)
5028   {
5029     SetGameStatus(GAME_MODE_MAIN);
5030
5031     DrawMainMenu();
5032
5033     return;
5034   }
5035
5036   if (level_nr == leveldir_current->handicap_level)
5037   {
5038     leveldir_current->handicap_level++;
5039
5040     SaveLevelSetup_SeriesInfo();
5041   }
5042
5043   // save score and score tape before potentially erasing tape below
5044   NewHighScore(last_level_nr, tape_saved);
5045
5046   if (setup.increment_levels &&
5047       level_nr < leveldir_current->last_level &&
5048       !network_playing)
5049   {
5050     level_nr++;         // advance to next level
5051     TapeErase();        // start with empty tape
5052
5053     if (setup.auto_play_next_level)
5054     {
5055       scores.continue_playing = TRUE;
5056       scores.next_level_nr = level_nr;
5057
5058       LoadLevel(level_nr);
5059
5060       SaveLevelSetup_SeriesInfo();
5061     }
5062   }
5063
5064   if (scores.last_added >= 0 && setup.show_scores_after_game)
5065   {
5066     SetGameStatus(GAME_MODE_SCORES);
5067
5068     DrawHallOfFame(last_level_nr);
5069   }
5070   else if (scores.continue_playing)
5071   {
5072     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5073   }
5074   else
5075   {
5076     SetGameStatus(GAME_MODE_MAIN);
5077
5078     DrawMainMenu();
5079   }
5080 }
5081
5082 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5083                          boolean one_score_entry_per_name)
5084 {
5085   int i;
5086
5087   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5088     return -1;
5089
5090   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5091   {
5092     struct ScoreEntry *entry = &list->entry[i];
5093     boolean score_is_better = (new_entry->score >  entry->score);
5094     boolean score_is_equal  = (new_entry->score == entry->score);
5095     boolean time_is_better  = (new_entry->time  <  entry->time);
5096     boolean time_is_equal   = (new_entry->time  == entry->time);
5097     boolean better_by_score = (score_is_better ||
5098                                (score_is_equal && time_is_better));
5099     boolean better_by_time  = (time_is_better ||
5100                                (time_is_equal && score_is_better));
5101     boolean is_better = (level.rate_time_over_score ? better_by_time :
5102                          better_by_score);
5103     boolean entry_is_empty = (entry->score == 0 &&
5104                               entry->time == 0);
5105
5106     // prevent adding server score entries if also existing in local score file
5107     // (special case: historic score entries have an empty tape basename entry)
5108     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5109         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5110     {
5111       // special case: use server score instead of local score value if higher
5112       // (historic scores might have been truncated to 16-bit values locally)
5113       if (score_is_better)
5114         entry->score = new_entry->score;
5115
5116       return -1;
5117     }
5118
5119     if (is_better || entry_is_empty)
5120     {
5121       // player has made it to the hall of fame
5122
5123       if (i < MAX_SCORE_ENTRIES - 1)
5124       {
5125         int m = MAX_SCORE_ENTRIES - 1;
5126         int l;
5127
5128         if (one_score_entry_per_name)
5129         {
5130           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5131             if (strEqual(list->entry[l].name, new_entry->name))
5132               m = l;
5133
5134           if (m == i)   // player's new highscore overwrites his old one
5135             goto put_into_list;
5136         }
5137
5138         for (l = m; l > i; l--)
5139           list->entry[l] = list->entry[l - 1];
5140       }
5141
5142       put_into_list:
5143
5144       *entry = *new_entry;
5145
5146       return i;
5147     }
5148     else if (one_score_entry_per_name &&
5149              strEqual(entry->name, new_entry->name))
5150     {
5151       // player already in high score list with better score or time
5152
5153       return -1;
5154     }
5155   }
5156
5157   // special case: new score is beyond the last high score list position
5158   return MAX_SCORE_ENTRIES;
5159 }
5160
5161 void NewHighScore(int level_nr, boolean tape_saved)
5162 {
5163   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5164   boolean one_per_name = FALSE;
5165
5166   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5167   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5168
5169   new_entry.score = game.score_final;
5170   new_entry.time = game.score_time_final;
5171
5172   LoadScore(level_nr);
5173
5174   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5175
5176   if (scores.last_added >= MAX_SCORE_ENTRIES)
5177   {
5178     scores.last_added = MAX_SCORE_ENTRIES - 1;
5179     scores.force_last_added = TRUE;
5180
5181     scores.entry[scores.last_added] = new_entry;
5182
5183     // store last added local score entry (before merging server scores)
5184     scores.last_added_local = scores.last_added;
5185
5186     return;
5187   }
5188
5189   if (scores.last_added < 0)
5190     return;
5191
5192   SaveScore(level_nr);
5193
5194   // store last added local score entry (before merging server scores)
5195   scores.last_added_local = scores.last_added;
5196
5197   if (!game.LevelSolved_SaveTape)
5198     return;
5199
5200   SaveScoreTape(level_nr);
5201
5202   if (setup.ask_for_using_api_server)
5203   {
5204     setup.use_api_server =
5205       Request("Upload your score and tape to the high score server?", REQ_ASK);
5206
5207     if (!setup.use_api_server)
5208       Request("Not using high score server! Use setup menu to enable again!",
5209               REQ_CONFIRM);
5210
5211     runtime.use_api_server = setup.use_api_server;
5212
5213     // after asking for using API server once, do not ask again
5214     setup.ask_for_using_api_server = FALSE;
5215
5216     SaveSetup_ServerSetup();
5217   }
5218
5219   SaveServerScore(level_nr, tape_saved);
5220 }
5221
5222 void MergeServerScore(void)
5223 {
5224   struct ScoreEntry last_added_entry;
5225   boolean one_per_name = FALSE;
5226   int i;
5227
5228   if (scores.last_added >= 0)
5229     last_added_entry = scores.entry[scores.last_added];
5230
5231   for (i = 0; i < server_scores.num_entries; i++)
5232   {
5233     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5234
5235     if (pos >= 0 && pos <= scores.last_added)
5236       scores.last_added++;
5237   }
5238
5239   if (scores.last_added >= MAX_SCORE_ENTRIES)
5240   {
5241     scores.last_added = MAX_SCORE_ENTRIES - 1;
5242     scores.force_last_added = TRUE;
5243
5244     scores.entry[scores.last_added] = last_added_entry;
5245   }
5246 }
5247
5248 static int getElementMoveStepsizeExt(int x, int y, int direction)
5249 {
5250   int element = Tile[x][y];
5251   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5252   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5253   int horiz_move = (dx != 0);
5254   int sign = (horiz_move ? dx : dy);
5255   int step = sign * element_info[element].move_stepsize;
5256
5257   // special values for move stepsize for spring and things on conveyor belt
5258   if (horiz_move)
5259   {
5260     if (CAN_FALL(element) &&
5261         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5262       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5263     else if (element == EL_SPRING)
5264       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5265   }
5266
5267   return step;
5268 }
5269
5270 static int getElementMoveStepsize(int x, int y)
5271 {
5272   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5273 }
5274
5275 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5276 {
5277   if (player->GfxAction != action || player->GfxDir != dir)
5278   {
5279     player->GfxAction = action;
5280     player->GfxDir = dir;
5281     player->Frame = 0;
5282     player->StepFrame = 0;
5283   }
5284 }
5285
5286 static void ResetGfxFrame(int x, int y)
5287 {
5288   // profiling showed that "autotest" spends 10~20% of its time in this function
5289   if (DrawingDeactivatedField())
5290     return;
5291
5292   int element = Tile[x][y];
5293   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5294
5295   if (graphic_info[graphic].anim_global_sync)
5296     GfxFrame[x][y] = FrameCounter;
5297   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5298     GfxFrame[x][y] = CustomValue[x][y];
5299   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5300     GfxFrame[x][y] = element_info[element].collect_score;
5301   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5302     GfxFrame[x][y] = ChangeDelay[x][y];
5303 }
5304
5305 static void ResetGfxAnimation(int x, int y)
5306 {
5307   GfxAction[x][y] = ACTION_DEFAULT;
5308   GfxDir[x][y] = MovDir[x][y];
5309   GfxFrame[x][y] = 0;
5310
5311   ResetGfxFrame(x, y);
5312 }
5313
5314 static void ResetRandomAnimationValue(int x, int y)
5315 {
5316   GfxRandom[x][y] = INIT_GFX_RANDOM();
5317 }
5318
5319 static void InitMovingField(int x, int y, int direction)
5320 {
5321   int element = Tile[x][y];
5322   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5323   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5324   int newx = x + dx;
5325   int newy = y + dy;
5326   boolean is_moving_before, is_moving_after;
5327
5328   // check if element was/is moving or being moved before/after mode change
5329   is_moving_before = (WasJustMoving[x][y] != 0);
5330   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5331
5332   // reset animation only for moving elements which change direction of moving
5333   // or which just started or stopped moving
5334   // (else CEs with property "can move" / "not moving" are reset each frame)
5335   if (is_moving_before != is_moving_after ||
5336       direction != MovDir[x][y])
5337     ResetGfxAnimation(x, y);
5338
5339   MovDir[x][y] = direction;
5340   GfxDir[x][y] = direction;
5341
5342   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5343                      direction == MV_DOWN && CAN_FALL(element) ?
5344                      ACTION_FALLING : ACTION_MOVING);
5345
5346   // this is needed for CEs with property "can move" / "not moving"
5347
5348   if (is_moving_after)
5349   {
5350     if (Tile[newx][newy] == EL_EMPTY)
5351       Tile[newx][newy] = EL_BLOCKED;
5352
5353     MovDir[newx][newy] = MovDir[x][y];
5354
5355     CustomValue[newx][newy] = CustomValue[x][y];
5356
5357     GfxFrame[newx][newy] = GfxFrame[x][y];
5358     GfxRandom[newx][newy] = GfxRandom[x][y];
5359     GfxAction[newx][newy] = GfxAction[x][y];
5360     GfxDir[newx][newy] = GfxDir[x][y];
5361   }
5362 }
5363
5364 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5365 {
5366   int direction = MovDir[x][y];
5367   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5368   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5369
5370   *goes_to_x = newx;
5371   *goes_to_y = newy;
5372 }
5373
5374 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5375 {
5376   int oldx = x, oldy = y;
5377   int direction = MovDir[x][y];
5378
5379   if (direction == MV_LEFT)
5380     oldx++;
5381   else if (direction == MV_RIGHT)
5382     oldx--;
5383   else if (direction == MV_UP)
5384     oldy++;
5385   else if (direction == MV_DOWN)
5386     oldy--;
5387
5388   *comes_from_x = oldx;
5389   *comes_from_y = oldy;
5390 }
5391
5392 static int MovingOrBlocked2Element(int x, int y)
5393 {
5394   int element = Tile[x][y];
5395
5396   if (element == EL_BLOCKED)
5397   {
5398     int oldx, oldy;
5399
5400     Blocked2Moving(x, y, &oldx, &oldy);
5401     return Tile[oldx][oldy];
5402   }
5403   else
5404     return element;
5405 }
5406
5407 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5408 {
5409   // like MovingOrBlocked2Element(), but if element is moving
5410   // and (x,y) is the field the moving element is just leaving,
5411   // return EL_BLOCKED instead of the element value
5412   int element = Tile[x][y];
5413
5414   if (IS_MOVING(x, y))
5415   {
5416     if (element == EL_BLOCKED)
5417     {
5418       int oldx, oldy;
5419
5420       Blocked2Moving(x, y, &oldx, &oldy);
5421       return Tile[oldx][oldy];
5422     }
5423     else
5424       return EL_BLOCKED;
5425   }
5426   else
5427     return element;
5428 }
5429
5430 static void RemoveField(int x, int y)
5431 {
5432   Tile[x][y] = EL_EMPTY;
5433
5434   MovPos[x][y] = 0;
5435   MovDir[x][y] = 0;
5436   MovDelay[x][y] = 0;
5437
5438   CustomValue[x][y] = 0;
5439
5440   AmoebaNr[x][y] = 0;
5441   ChangeDelay[x][y] = 0;
5442   ChangePage[x][y] = -1;
5443   Pushed[x][y] = FALSE;
5444
5445   GfxElement[x][y] = EL_UNDEFINED;
5446   GfxAction[x][y] = ACTION_DEFAULT;
5447   GfxDir[x][y] = MV_NONE;
5448 }
5449
5450 static void RemoveMovingField(int x, int y)
5451 {
5452   int oldx = x, oldy = y, newx = x, newy = y;
5453   int element = Tile[x][y];
5454   int next_element = EL_UNDEFINED;
5455
5456   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5457     return;
5458
5459   if (IS_MOVING(x, y))
5460   {
5461     Moving2Blocked(x, y, &newx, &newy);
5462
5463     if (Tile[newx][newy] != EL_BLOCKED)
5464     {
5465       // element is moving, but target field is not free (blocked), but
5466       // already occupied by something different (example: acid pool);
5467       // in this case, only remove the moving field, but not the target
5468
5469       RemoveField(oldx, oldy);
5470
5471       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5472
5473       TEST_DrawLevelField(oldx, oldy);
5474
5475       return;
5476     }
5477   }
5478   else if (element == EL_BLOCKED)
5479   {
5480     Blocked2Moving(x, y, &oldx, &oldy);
5481     if (!IS_MOVING(oldx, oldy))
5482       return;
5483   }
5484
5485   if (element == EL_BLOCKED &&
5486       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5487        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5488        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5489        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5490        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5491        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5492     next_element = get_next_element(Tile[oldx][oldy]);
5493
5494   RemoveField(oldx, oldy);
5495   RemoveField(newx, newy);
5496
5497   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5498
5499   if (next_element != EL_UNDEFINED)
5500     Tile[oldx][oldy] = next_element;
5501
5502   TEST_DrawLevelField(oldx, oldy);
5503   TEST_DrawLevelField(newx, newy);
5504 }
5505
5506 void DrawDynamite(int x, int y)
5507 {
5508   int sx = SCREENX(x), sy = SCREENY(y);
5509   int graphic = el2img(Tile[x][y]);
5510   int frame;
5511
5512   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5513     return;
5514
5515   if (IS_WALKABLE_INSIDE(Back[x][y]))
5516     return;
5517
5518   if (Back[x][y])
5519     DrawLevelElement(x, y, Back[x][y]);
5520   else if (Store[x][y])
5521     DrawLevelElement(x, y, Store[x][y]);
5522   else if (game.use_masked_elements)
5523     DrawLevelElement(x, y, EL_EMPTY);
5524
5525   frame = getGraphicAnimationFrameXY(graphic, x, y);
5526
5527   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5528     DrawGraphicThruMask(sx, sy, graphic, frame);
5529   else
5530     DrawGraphic(sx, sy, graphic, frame);
5531 }
5532
5533 static void CheckDynamite(int x, int y)
5534 {
5535   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5536   {
5537     MovDelay[x][y]--;
5538
5539     if (MovDelay[x][y] != 0)
5540     {
5541       DrawDynamite(x, y);
5542       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5543
5544       return;
5545     }
5546   }
5547
5548   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5549
5550   Bang(x, y);
5551 }
5552
5553 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5554 {
5555   boolean num_checked_players = 0;
5556   int i;
5557
5558   for (i = 0; i < MAX_PLAYERS; i++)
5559   {
5560     if (stored_player[i].active)
5561     {
5562       int sx = stored_player[i].jx;
5563       int sy = stored_player[i].jy;
5564
5565       if (num_checked_players == 0)
5566       {
5567         *sx1 = *sx2 = sx;
5568         *sy1 = *sy2 = sy;
5569       }
5570       else
5571       {
5572         *sx1 = MIN(*sx1, sx);
5573         *sy1 = MIN(*sy1, sy);
5574         *sx2 = MAX(*sx2, sx);
5575         *sy2 = MAX(*sy2, sy);
5576       }
5577
5578       num_checked_players++;
5579     }
5580   }
5581 }
5582
5583 static boolean checkIfAllPlayersFitToScreen_RND(void)
5584 {
5585   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5586
5587   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5588
5589   return (sx2 - sx1 < SCR_FIELDX &&
5590           sy2 - sy1 < SCR_FIELDY);
5591 }
5592
5593 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5594 {
5595   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5596
5597   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5598
5599   *sx = (sx1 + sx2) / 2;
5600   *sy = (sy1 + sy2) / 2;
5601 }
5602
5603 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5604                                boolean center_screen, boolean quick_relocation)
5605 {
5606   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5607   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5608   boolean no_delay = (tape.warp_forward);
5609   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5610   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5611   int new_scroll_x, new_scroll_y;
5612
5613   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5614   {
5615     // case 1: quick relocation inside visible screen (without scrolling)
5616
5617     RedrawPlayfield();
5618
5619     return;
5620   }
5621
5622   if (!level.shifted_relocation || center_screen)
5623   {
5624     // relocation _with_ centering of screen
5625
5626     new_scroll_x = SCROLL_POSITION_X(x);
5627     new_scroll_y = SCROLL_POSITION_Y(y);
5628   }
5629   else
5630   {
5631     // relocation _without_ centering of screen
5632
5633     int center_scroll_x = SCROLL_POSITION_X(old_x);
5634     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5635     int offset_x = x + (scroll_x - center_scroll_x);
5636     int offset_y = y + (scroll_y - center_scroll_y);
5637
5638     // for new screen position, apply previous offset to center position
5639     new_scroll_x = SCROLL_POSITION_X(offset_x);
5640     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5641   }
5642
5643   if (quick_relocation)
5644   {
5645     // case 2: quick relocation (redraw without visible scrolling)
5646
5647     scroll_x = new_scroll_x;
5648     scroll_y = new_scroll_y;
5649
5650     RedrawPlayfield();
5651
5652     return;
5653   }
5654
5655   // case 3: visible relocation (with scrolling to new position)
5656
5657   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5658
5659   SetVideoFrameDelay(wait_delay_value);
5660
5661   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5662   {
5663     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5664     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5665
5666     if (dx == 0 && dy == 0)             // no scrolling needed at all
5667       break;
5668
5669     scroll_x -= dx;
5670     scroll_y -= dy;
5671
5672     // set values for horizontal/vertical screen scrolling (half tile size)
5673     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5674     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5675     int pos_x = dx * TILEX / 2;
5676     int pos_y = dy * TILEY / 2;
5677     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5678     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5679
5680     ScrollLevel(dx, dy);
5681     DrawAllPlayers();
5682
5683     // scroll in two steps of half tile size to make things smoother
5684     BlitScreenToBitmapExt_RND(window, fx, fy);
5685
5686     // scroll second step to align at full tile size
5687     BlitScreenToBitmap(window);
5688   }
5689
5690   DrawAllPlayers();
5691   BackToFront();
5692
5693   SetVideoFrameDelay(frame_delay_value_old);
5694 }
5695
5696 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5697 {
5698   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5699   int player_nr = GET_PLAYER_NR(el_player);
5700   struct PlayerInfo *player = &stored_player[player_nr];
5701   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5702   boolean no_delay = (tape.warp_forward);
5703   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5704   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5705   int old_jx = player->jx;
5706   int old_jy = player->jy;
5707   int old_element = Tile[old_jx][old_jy];
5708   int element = Tile[jx][jy];
5709   boolean player_relocated = (old_jx != jx || old_jy != jy);
5710
5711   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5712   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5713   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5714   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5715   int leave_side_horiz = move_dir_horiz;
5716   int leave_side_vert  = move_dir_vert;
5717   int enter_side = enter_side_horiz | enter_side_vert;
5718   int leave_side = leave_side_horiz | leave_side_vert;
5719
5720   if (player->buried)           // do not reanimate dead player
5721     return;
5722
5723   if (!player_relocated)        // no need to relocate the player
5724     return;
5725
5726   if (IS_PLAYER(jx, jy))        // player already placed at new position
5727   {
5728     RemoveField(jx, jy);        // temporarily remove newly placed player
5729     DrawLevelField(jx, jy);
5730   }
5731
5732   if (player->present)
5733   {
5734     while (player->MovPos)
5735     {
5736       ScrollPlayer(player, SCROLL_GO_ON);
5737       ScrollScreen(NULL, SCROLL_GO_ON);
5738
5739       AdvanceFrameAndPlayerCounters(player->index_nr);
5740
5741       DrawPlayer(player);
5742
5743       BackToFront_WithFrameDelay(wait_delay_value);
5744     }
5745
5746     DrawPlayer(player);         // needed here only to cleanup last field
5747     DrawLevelField(player->jx, player->jy);     // remove player graphic
5748
5749     player->is_moving = FALSE;
5750   }
5751
5752   if (IS_CUSTOM_ELEMENT(old_element))
5753     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5754                                CE_LEFT_BY_PLAYER,
5755                                player->index_bit, leave_side);
5756
5757   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5758                                       CE_PLAYER_LEAVES_X,
5759                                       player->index_bit, leave_side);
5760
5761   Tile[jx][jy] = el_player;
5762   InitPlayerField(jx, jy, el_player, TRUE);
5763
5764   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5765      possible that the relocation target field did not contain a player element,
5766      but a walkable element, to which the new player was relocated -- in this
5767      case, restore that (already initialized!) element on the player field */
5768   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5769   {
5770     Tile[jx][jy] = element;     // restore previously existing element
5771   }
5772
5773   // only visually relocate centered player
5774   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5775                      FALSE, level.instant_relocation);
5776
5777   TestIfPlayerTouchesBadThing(jx, jy);
5778   TestIfPlayerTouchesCustomElement(jx, jy);
5779
5780   if (IS_CUSTOM_ELEMENT(element))
5781     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5782                                player->index_bit, enter_side);
5783
5784   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5785                                       player->index_bit, enter_side);
5786
5787   if (player->is_switching)
5788   {
5789     /* ensure that relocation while still switching an element does not cause
5790        a new element to be treated as also switched directly after relocation
5791        (this is important for teleporter switches that teleport the player to
5792        a place where another teleporter switch is in the same direction, which
5793        would then incorrectly be treated as immediately switched before the
5794        direction key that caused the switch was released) */
5795
5796     player->switch_x += jx - old_jx;
5797     player->switch_y += jy - old_jy;
5798   }
5799 }
5800
5801 static void Explode(int ex, int ey, int phase, int mode)
5802 {
5803   int x, y;
5804   int last_phase;
5805   int border_element;
5806
5807   // !!! eliminate this variable !!!
5808   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5809
5810   if (game.explosions_delayed)
5811   {
5812     ExplodeField[ex][ey] = mode;
5813     return;
5814   }
5815
5816   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5817   {
5818     int center_element = Tile[ex][ey];
5819     int artwork_element, explosion_element;     // set these values later
5820
5821     // remove things displayed in background while burning dynamite
5822     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5823       Back[ex][ey] = 0;
5824
5825     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5826     {
5827       // put moving element to center field (and let it explode there)
5828       center_element = MovingOrBlocked2Element(ex, ey);
5829       RemoveMovingField(ex, ey);
5830       Tile[ex][ey] = center_element;
5831     }
5832
5833     // now "center_element" is finally determined -- set related values now
5834     artwork_element = center_element;           // for custom player artwork
5835     explosion_element = center_element;         // for custom player artwork
5836
5837     if (IS_PLAYER(ex, ey))
5838     {
5839       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5840
5841       artwork_element = stored_player[player_nr].artwork_element;
5842
5843       if (level.use_explosion_element[player_nr])
5844       {
5845         explosion_element = level.explosion_element[player_nr];
5846         artwork_element = explosion_element;
5847       }
5848     }
5849
5850     if (mode == EX_TYPE_NORMAL ||
5851         mode == EX_TYPE_CENTER ||
5852         mode == EX_TYPE_CROSS)
5853       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5854
5855     last_phase = element_info[explosion_element].explosion_delay + 1;
5856
5857     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5858     {
5859       int xx = x - ex + 1;
5860       int yy = y - ey + 1;
5861       int element;
5862
5863       if (!IN_LEV_FIELD(x, y) ||
5864           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5865           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5866         continue;
5867
5868       element = Tile[x][y];
5869
5870       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5871       {
5872         element = MovingOrBlocked2Element(x, y);
5873
5874         if (!IS_EXPLOSION_PROOF(element))
5875           RemoveMovingField(x, y);
5876       }
5877
5878       // indestructible elements can only explode in center (but not flames)
5879       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5880                                            mode == EX_TYPE_BORDER)) ||
5881           element == EL_FLAMES)
5882         continue;
5883
5884       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5885          behaviour, for example when touching a yamyam that explodes to rocks
5886          with active deadly shield, a rock is created under the player !!! */
5887       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5888 #if 0
5889       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5890           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5891            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5892 #else
5893       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5894 #endif
5895       {
5896         if (IS_ACTIVE_BOMB(element))
5897         {
5898           // re-activate things under the bomb like gate or penguin
5899           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5900           Back[x][y] = 0;
5901         }
5902
5903         continue;
5904       }
5905
5906       // save walkable background elements while explosion on same tile
5907       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5908           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5909         Back[x][y] = element;
5910
5911       // ignite explodable elements reached by other explosion
5912       if (element == EL_EXPLOSION)
5913         element = Store2[x][y];
5914
5915       if (AmoebaNr[x][y] &&
5916           (element == EL_AMOEBA_FULL ||
5917            element == EL_BD_AMOEBA ||
5918            element == EL_AMOEBA_GROWING))
5919       {
5920         AmoebaCnt[AmoebaNr[x][y]]--;
5921         AmoebaCnt2[AmoebaNr[x][y]]--;
5922       }
5923
5924       RemoveField(x, y);
5925
5926       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5927       {
5928         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5929
5930         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5931
5932         if (PLAYERINFO(ex, ey)->use_murphy)
5933           Store[x][y] = EL_EMPTY;
5934       }
5935
5936       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5937       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5938       else if (IS_PLAYER_ELEMENT(center_element))
5939         Store[x][y] = EL_EMPTY;
5940       else if (center_element == EL_YAMYAM)
5941         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5942       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5943         Store[x][y] = element_info[center_element].content.e[xx][yy];
5944 #if 1
5945       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5946       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5947       // otherwise) -- FIX THIS !!!
5948       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5949         Store[x][y] = element_info[element].content.e[1][1];
5950 #else
5951       else if (!CAN_EXPLODE(element))
5952         Store[x][y] = element_info[element].content.e[1][1];
5953 #endif
5954       else
5955         Store[x][y] = EL_EMPTY;
5956
5957       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5958           center_element == EL_AMOEBA_TO_DIAMOND)
5959         Store2[x][y] = element;
5960
5961       Tile[x][y] = EL_EXPLOSION;
5962       GfxElement[x][y] = artwork_element;
5963
5964       ExplodePhase[x][y] = 1;
5965       ExplodeDelay[x][y] = last_phase;
5966
5967       Stop[x][y] = TRUE;
5968     }
5969
5970     if (center_element == EL_YAMYAM)
5971       game.yamyam_content_nr =
5972         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5973
5974     return;
5975   }
5976
5977   if (Stop[ex][ey])
5978     return;
5979
5980   x = ex;
5981   y = ey;
5982
5983   if (phase == 1)
5984     GfxFrame[x][y] = 0;         // restart explosion animation
5985
5986   last_phase = ExplodeDelay[x][y];
5987
5988   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5989
5990   // this can happen if the player leaves an explosion just in time
5991   if (GfxElement[x][y] == EL_UNDEFINED)
5992     GfxElement[x][y] = EL_EMPTY;
5993
5994   border_element = Store2[x][y];
5995   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5996     border_element = StorePlayer[x][y];
5997
5998   if (phase == element_info[border_element].ignition_delay ||
5999       phase == last_phase)
6000   {
6001     boolean border_explosion = FALSE;
6002
6003     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6004         !PLAYER_EXPLOSION_PROTECTED(x, y))
6005     {
6006       KillPlayerUnlessExplosionProtected(x, y);
6007       border_explosion = TRUE;
6008     }
6009     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6010     {
6011       Tile[x][y] = Store2[x][y];
6012       Store2[x][y] = 0;
6013       Bang(x, y);
6014       border_explosion = TRUE;
6015     }
6016     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6017     {
6018       AmoebaToDiamond(x, y);
6019       Store2[x][y] = 0;
6020       border_explosion = TRUE;
6021     }
6022
6023     // if an element just explodes due to another explosion (chain-reaction),
6024     // do not immediately end the new explosion when it was the last frame of
6025     // the explosion (as it would be done in the following "if"-statement!)
6026     if (border_explosion && phase == last_phase)
6027       return;
6028   }
6029
6030   // this can happen if the player was just killed by an explosion
6031   if (GfxElement[x][y] == EL_UNDEFINED)
6032     GfxElement[x][y] = EL_EMPTY;
6033
6034   if (phase == last_phase)
6035   {
6036     int element;
6037
6038     element = Tile[x][y] = Store[x][y];
6039     Store[x][y] = Store2[x][y] = 0;
6040     GfxElement[x][y] = EL_UNDEFINED;
6041
6042     // player can escape from explosions and might therefore be still alive
6043     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6044         element <= EL_PLAYER_IS_EXPLODING_4)
6045     {
6046       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6047       int explosion_element = EL_PLAYER_1 + player_nr;
6048       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6049       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6050
6051       if (level.use_explosion_element[player_nr])
6052         explosion_element = level.explosion_element[player_nr];
6053
6054       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6055                     element_info[explosion_element].content.e[xx][yy]);
6056     }
6057
6058     // restore probably existing indestructible background element
6059     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6060       element = Tile[x][y] = Back[x][y];
6061     Back[x][y] = 0;
6062
6063     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6064     GfxDir[x][y] = MV_NONE;
6065     ChangeDelay[x][y] = 0;
6066     ChangePage[x][y] = -1;
6067
6068     CustomValue[x][y] = 0;
6069
6070     InitField_WithBug2(x, y, FALSE);
6071
6072     TEST_DrawLevelField(x, y);
6073
6074     TestIfElementTouchesCustomElement(x, y);
6075
6076     if (GFX_CRUMBLED(element))
6077       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6078
6079     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6080       StorePlayer[x][y] = 0;
6081
6082     if (IS_PLAYER_ELEMENT(element))
6083       RelocatePlayer(x, y, element);
6084   }
6085   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6086   {
6087     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6088     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6089
6090     if (phase == delay)
6091       TEST_DrawLevelFieldCrumbled(x, y);
6092
6093     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6094     {
6095       DrawLevelElement(x, y, Back[x][y]);
6096       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6097     }
6098     else if (IS_WALKABLE_UNDER(Back[x][y]))
6099     {
6100       DrawLevelGraphic(x, y, graphic, frame);
6101       DrawLevelElementThruMask(x, y, Back[x][y]);
6102     }
6103     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6104       DrawLevelGraphic(x, y, graphic, frame);
6105   }
6106 }
6107
6108 static void DynaExplode(int ex, int ey)
6109 {
6110   int i, j;
6111   int dynabomb_element = Tile[ex][ey];
6112   int dynabomb_size = 1;
6113   boolean dynabomb_xl = FALSE;
6114   struct PlayerInfo *player;
6115   static int xy[4][2] =
6116   {
6117     { 0, -1 },
6118     { -1, 0 },
6119     { +1, 0 },
6120     { 0, +1 }
6121   };
6122
6123   if (IS_ACTIVE_BOMB(dynabomb_element))
6124   {
6125     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6126     dynabomb_size = player->dynabomb_size;
6127     dynabomb_xl = player->dynabomb_xl;
6128     player->dynabombs_left++;
6129   }
6130
6131   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6132
6133   for (i = 0; i < NUM_DIRECTIONS; i++)
6134   {
6135     for (j = 1; j <= dynabomb_size; j++)
6136     {
6137       int x = ex + j * xy[i][0];
6138       int y = ey + j * xy[i][1];
6139       int element;
6140
6141       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6142         break;
6143
6144       element = Tile[x][y];
6145
6146       // do not restart explosions of fields with active bombs
6147       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6148         continue;
6149
6150       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6151
6152       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6153           !IS_DIGGABLE(element) && !dynabomb_xl)
6154         break;
6155     }
6156   }
6157 }
6158
6159 void Bang(int x, int y)
6160 {
6161   int element = MovingOrBlocked2Element(x, y);
6162   int explosion_type = EX_TYPE_NORMAL;
6163
6164   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6165   {
6166     struct PlayerInfo *player = PLAYERINFO(x, y);
6167
6168     element = Tile[x][y] = player->initial_element;
6169
6170     if (level.use_explosion_element[player->index_nr])
6171     {
6172       int explosion_element = level.explosion_element[player->index_nr];
6173
6174       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6175         explosion_type = EX_TYPE_CROSS;
6176       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6177         explosion_type = EX_TYPE_CENTER;
6178     }
6179   }
6180
6181   switch (element)
6182   {
6183     case EL_BUG:
6184     case EL_SPACESHIP:
6185     case EL_BD_BUTTERFLY:
6186     case EL_BD_FIREFLY:
6187     case EL_YAMYAM:
6188     case EL_DARK_YAMYAM:
6189     case EL_ROBOT:
6190     case EL_PACMAN:
6191     case EL_MOLE:
6192       RaiseScoreElement(element);
6193       break;
6194
6195     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6196     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6197     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6198     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6199     case EL_DYNABOMB_INCREASE_NUMBER:
6200     case EL_DYNABOMB_INCREASE_SIZE:
6201     case EL_DYNABOMB_INCREASE_POWER:
6202       explosion_type = EX_TYPE_DYNA;
6203       break;
6204
6205     case EL_DC_LANDMINE:
6206       explosion_type = EX_TYPE_CENTER;
6207       break;
6208
6209     case EL_PENGUIN:
6210     case EL_LAMP:
6211     case EL_LAMP_ACTIVE:
6212     case EL_AMOEBA_TO_DIAMOND:
6213       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6214         explosion_type = EX_TYPE_CENTER;
6215       break;
6216
6217     default:
6218       if (element_info[element].explosion_type == EXPLODES_CROSS)
6219         explosion_type = EX_TYPE_CROSS;
6220       else if (element_info[element].explosion_type == EXPLODES_1X1)
6221         explosion_type = EX_TYPE_CENTER;
6222       break;
6223   }
6224
6225   if (explosion_type == EX_TYPE_DYNA)
6226     DynaExplode(x, y);
6227   else
6228     Explode(x, y, EX_PHASE_START, explosion_type);
6229
6230   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6231 }
6232
6233 static void SplashAcid(int x, int y)
6234 {
6235   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6236       (!IN_LEV_FIELD(x - 1, y - 2) ||
6237        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6238     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6239
6240   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6241       (!IN_LEV_FIELD(x + 1, y - 2) ||
6242        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6243     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6244
6245   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6246 }
6247
6248 static void InitBeltMovement(void)
6249 {
6250   static int belt_base_element[4] =
6251   {
6252     EL_CONVEYOR_BELT_1_LEFT,
6253     EL_CONVEYOR_BELT_2_LEFT,
6254     EL_CONVEYOR_BELT_3_LEFT,
6255     EL_CONVEYOR_BELT_4_LEFT
6256   };
6257   static int belt_base_active_element[4] =
6258   {
6259     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6260     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6261     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6262     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6263   };
6264
6265   int x, y, i, j;
6266
6267   // set frame order for belt animation graphic according to belt direction
6268   for (i = 0; i < NUM_BELTS; i++)
6269   {
6270     int belt_nr = i;
6271
6272     for (j = 0; j < NUM_BELT_PARTS; j++)
6273     {
6274       int element = belt_base_active_element[belt_nr] + j;
6275       int graphic_1 = el2img(element);
6276       int graphic_2 = el2panelimg(element);
6277
6278       if (game.belt_dir[i] == MV_LEFT)
6279       {
6280         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6281         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6282       }
6283       else
6284       {
6285         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6286         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6287       }
6288     }
6289   }
6290
6291   SCAN_PLAYFIELD(x, y)
6292   {
6293     int element = Tile[x][y];
6294
6295     for (i = 0; i < NUM_BELTS; i++)
6296     {
6297       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6298       {
6299         int e_belt_nr = getBeltNrFromBeltElement(element);
6300         int belt_nr = i;
6301
6302         if (e_belt_nr == belt_nr)
6303         {
6304           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6305
6306           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6307         }
6308       }
6309     }
6310   }
6311 }
6312
6313 static void ToggleBeltSwitch(int x, int y)
6314 {
6315   static int belt_base_element[4] =
6316   {
6317     EL_CONVEYOR_BELT_1_LEFT,
6318     EL_CONVEYOR_BELT_2_LEFT,
6319     EL_CONVEYOR_BELT_3_LEFT,
6320     EL_CONVEYOR_BELT_4_LEFT
6321   };
6322   static int belt_base_active_element[4] =
6323   {
6324     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6325     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6326     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6327     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6328   };
6329   static int belt_base_switch_element[4] =
6330   {
6331     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6332     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6333     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6334     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6335   };
6336   static int belt_move_dir[4] =
6337   {
6338     MV_LEFT,
6339     MV_NONE,
6340     MV_RIGHT,
6341     MV_NONE,
6342   };
6343
6344   int element = Tile[x][y];
6345   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6346   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6347   int belt_dir = belt_move_dir[belt_dir_nr];
6348   int xx, yy, i;
6349
6350   if (!IS_BELT_SWITCH(element))
6351     return;
6352
6353   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6354   game.belt_dir[belt_nr] = belt_dir;
6355
6356   if (belt_dir_nr == 3)
6357     belt_dir_nr = 1;
6358
6359   // set frame order for belt animation graphic according to belt direction
6360   for (i = 0; i < NUM_BELT_PARTS; i++)
6361   {
6362     int element = belt_base_active_element[belt_nr] + i;
6363     int graphic_1 = el2img(element);
6364     int graphic_2 = el2panelimg(element);
6365
6366     if (belt_dir == MV_LEFT)
6367     {
6368       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6369       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6370     }
6371     else
6372     {
6373       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6374       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6375     }
6376   }
6377
6378   SCAN_PLAYFIELD(xx, yy)
6379   {
6380     int element = Tile[xx][yy];
6381
6382     if (IS_BELT_SWITCH(element))
6383     {
6384       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6385
6386       if (e_belt_nr == belt_nr)
6387       {
6388         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6389         TEST_DrawLevelField(xx, yy);
6390       }
6391     }
6392     else if (IS_BELT(element) && belt_dir != MV_NONE)
6393     {
6394       int e_belt_nr = getBeltNrFromBeltElement(element);
6395
6396       if (e_belt_nr == belt_nr)
6397       {
6398         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6399
6400         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6401         TEST_DrawLevelField(xx, yy);
6402       }
6403     }
6404     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6405     {
6406       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6407
6408       if (e_belt_nr == belt_nr)
6409       {
6410         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6411
6412         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6413         TEST_DrawLevelField(xx, yy);
6414       }
6415     }
6416   }
6417 }
6418
6419 static void ToggleSwitchgateSwitch(int x, int y)
6420 {
6421   int xx, yy;
6422
6423   game.switchgate_pos = !game.switchgate_pos;
6424
6425   SCAN_PLAYFIELD(xx, yy)
6426   {
6427     int element = Tile[xx][yy];
6428
6429     if (element == EL_SWITCHGATE_SWITCH_UP)
6430     {
6431       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6432       TEST_DrawLevelField(xx, yy);
6433     }
6434     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6435     {
6436       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6437       TEST_DrawLevelField(xx, yy);
6438     }
6439     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6440     {
6441       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6442       TEST_DrawLevelField(xx, yy);
6443     }
6444     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6445     {
6446       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6447       TEST_DrawLevelField(xx, yy);
6448     }
6449     else if (element == EL_SWITCHGATE_OPEN ||
6450              element == EL_SWITCHGATE_OPENING)
6451     {
6452       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6453
6454       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6455     }
6456     else if (element == EL_SWITCHGATE_CLOSED ||
6457              element == EL_SWITCHGATE_CLOSING)
6458     {
6459       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6460
6461       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6462     }
6463   }
6464 }
6465
6466 static int getInvisibleActiveFromInvisibleElement(int element)
6467 {
6468   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6469           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6470           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6471           element);
6472 }
6473
6474 static int getInvisibleFromInvisibleActiveElement(int element)
6475 {
6476   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6477           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6478           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6479           element);
6480 }
6481
6482 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6483 {
6484   int x, y;
6485
6486   SCAN_PLAYFIELD(x, y)
6487   {
6488     int element = Tile[x][y];
6489
6490     if (element == EL_LIGHT_SWITCH &&
6491         game.light_time_left > 0)
6492     {
6493       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6494       TEST_DrawLevelField(x, y);
6495     }
6496     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6497              game.light_time_left == 0)
6498     {
6499       Tile[x][y] = EL_LIGHT_SWITCH;
6500       TEST_DrawLevelField(x, y);
6501     }
6502     else if (element == EL_EMC_DRIPPER &&
6503              game.light_time_left > 0)
6504     {
6505       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6506       TEST_DrawLevelField(x, y);
6507     }
6508     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6509              game.light_time_left == 0)
6510     {
6511       Tile[x][y] = EL_EMC_DRIPPER;
6512       TEST_DrawLevelField(x, y);
6513     }
6514     else if (element == EL_INVISIBLE_STEELWALL ||
6515              element == EL_INVISIBLE_WALL ||
6516              element == EL_INVISIBLE_SAND)
6517     {
6518       if (game.light_time_left > 0)
6519         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6520
6521       TEST_DrawLevelField(x, y);
6522
6523       // uncrumble neighbour fields, if needed
6524       if (element == EL_INVISIBLE_SAND)
6525         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6526     }
6527     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6528              element == EL_INVISIBLE_WALL_ACTIVE ||
6529              element == EL_INVISIBLE_SAND_ACTIVE)
6530     {
6531       if (game.light_time_left == 0)
6532         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6533
6534       TEST_DrawLevelField(x, y);
6535
6536       // re-crumble neighbour fields, if needed
6537       if (element == EL_INVISIBLE_SAND)
6538         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6539     }
6540   }
6541 }
6542
6543 static void RedrawAllInvisibleElementsForLenses(void)
6544 {
6545   int x, y;
6546
6547   SCAN_PLAYFIELD(x, y)
6548   {
6549     int element = Tile[x][y];
6550
6551     if (element == EL_EMC_DRIPPER &&
6552         game.lenses_time_left > 0)
6553     {
6554       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6555       TEST_DrawLevelField(x, y);
6556     }
6557     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6558              game.lenses_time_left == 0)
6559     {
6560       Tile[x][y] = EL_EMC_DRIPPER;
6561       TEST_DrawLevelField(x, y);
6562     }
6563     else if (element == EL_INVISIBLE_STEELWALL ||
6564              element == EL_INVISIBLE_WALL ||
6565              element == EL_INVISIBLE_SAND)
6566     {
6567       if (game.lenses_time_left > 0)
6568         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6569
6570       TEST_DrawLevelField(x, y);
6571
6572       // uncrumble neighbour fields, if needed
6573       if (element == EL_INVISIBLE_SAND)
6574         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6575     }
6576     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6577              element == EL_INVISIBLE_WALL_ACTIVE ||
6578              element == EL_INVISIBLE_SAND_ACTIVE)
6579     {
6580       if (game.lenses_time_left == 0)
6581         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6582
6583       TEST_DrawLevelField(x, y);
6584
6585       // re-crumble neighbour fields, if needed
6586       if (element == EL_INVISIBLE_SAND)
6587         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6588     }
6589   }
6590 }
6591
6592 static void RedrawAllInvisibleElementsForMagnifier(void)
6593 {
6594   int x, y;
6595
6596   SCAN_PLAYFIELD(x, y)
6597   {
6598     int element = Tile[x][y];
6599
6600     if (element == EL_EMC_FAKE_GRASS &&
6601         game.magnify_time_left > 0)
6602     {
6603       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6604       TEST_DrawLevelField(x, y);
6605     }
6606     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6607              game.magnify_time_left == 0)
6608     {
6609       Tile[x][y] = EL_EMC_FAKE_GRASS;
6610       TEST_DrawLevelField(x, y);
6611     }
6612     else if (IS_GATE_GRAY(element) &&
6613              game.magnify_time_left > 0)
6614     {
6615       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6616                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6617                     IS_EM_GATE_GRAY(element) ?
6618                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6619                     IS_EMC_GATE_GRAY(element) ?
6620                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6621                     IS_DC_GATE_GRAY(element) ?
6622                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6623                     element);
6624       TEST_DrawLevelField(x, y);
6625     }
6626     else if (IS_GATE_GRAY_ACTIVE(element) &&
6627              game.magnify_time_left == 0)
6628     {
6629       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6630                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6631                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6632                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6633                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6634                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6635                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6636                     EL_DC_GATE_WHITE_GRAY :
6637                     element);
6638       TEST_DrawLevelField(x, y);
6639     }
6640   }
6641 }
6642
6643 static void ToggleLightSwitch(int x, int y)
6644 {
6645   int element = Tile[x][y];
6646
6647   game.light_time_left =
6648     (element == EL_LIGHT_SWITCH ?
6649      level.time_light * FRAMES_PER_SECOND : 0);
6650
6651   RedrawAllLightSwitchesAndInvisibleElements();
6652 }
6653
6654 static void ActivateTimegateSwitch(int x, int y)
6655 {
6656   int xx, yy;
6657
6658   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6659
6660   SCAN_PLAYFIELD(xx, yy)
6661   {
6662     int element = Tile[xx][yy];
6663
6664     if (element == EL_TIMEGATE_CLOSED ||
6665         element == EL_TIMEGATE_CLOSING)
6666     {
6667       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6668       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6669     }
6670
6671     /*
6672     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6673     {
6674       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6675       TEST_DrawLevelField(xx, yy);
6676     }
6677     */
6678
6679   }
6680
6681   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6682                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6683 }
6684
6685 static void Impact(int x, int y)
6686 {
6687   boolean last_line = (y == lev_fieldy - 1);
6688   boolean object_hit = FALSE;
6689   boolean impact = (last_line || object_hit);
6690   int element = Tile[x][y];
6691   int smashed = EL_STEELWALL;
6692
6693   if (!last_line)       // check if element below was hit
6694   {
6695     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6696       return;
6697
6698     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6699                                          MovDir[x][y + 1] != MV_DOWN ||
6700                                          MovPos[x][y + 1] <= TILEY / 2));
6701
6702     // do not smash moving elements that left the smashed field in time
6703     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6704         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6705       object_hit = FALSE;
6706
6707 #if USE_QUICKSAND_IMPACT_BUGFIX
6708     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6709     {
6710       RemoveMovingField(x, y + 1);
6711       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6712       Tile[x][y + 2] = EL_ROCK;
6713       TEST_DrawLevelField(x, y + 2);
6714
6715       object_hit = TRUE;
6716     }
6717
6718     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6719     {
6720       RemoveMovingField(x, y + 1);
6721       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6722       Tile[x][y + 2] = EL_ROCK;
6723       TEST_DrawLevelField(x, y + 2);
6724
6725       object_hit = TRUE;
6726     }
6727 #endif
6728
6729     if (object_hit)
6730       smashed = MovingOrBlocked2Element(x, y + 1);
6731
6732     impact = (last_line || object_hit);
6733   }
6734
6735   if (!last_line && smashed == EL_ACID) // element falls into acid
6736   {
6737     SplashAcid(x, y + 1);
6738     return;
6739   }
6740
6741   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6742   // only reset graphic animation if graphic really changes after impact
6743   if (impact &&
6744       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6745   {
6746     ResetGfxAnimation(x, y);
6747     TEST_DrawLevelField(x, y);
6748   }
6749
6750   if (impact && CAN_EXPLODE_IMPACT(element))
6751   {
6752     Bang(x, y);
6753     return;
6754   }
6755   else if (impact && element == EL_PEARL &&
6756            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6757   {
6758     ResetGfxAnimation(x, y);
6759
6760     Tile[x][y] = EL_PEARL_BREAKING;
6761     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6762     return;
6763   }
6764   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6765   {
6766     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6767
6768     return;
6769   }
6770
6771   if (impact && element == EL_AMOEBA_DROP)
6772   {
6773     if (object_hit && IS_PLAYER(x, y + 1))
6774       KillPlayerUnlessEnemyProtected(x, y + 1);
6775     else if (object_hit && smashed == EL_PENGUIN)
6776       Bang(x, y + 1);
6777     else
6778     {
6779       Tile[x][y] = EL_AMOEBA_GROWING;
6780       Store[x][y] = EL_AMOEBA_WET;
6781
6782       ResetRandomAnimationValue(x, y);
6783     }
6784     return;
6785   }
6786
6787   if (object_hit)               // check which object was hit
6788   {
6789     if ((CAN_PASS_MAGIC_WALL(element) && 
6790          (smashed == EL_MAGIC_WALL ||
6791           smashed == EL_BD_MAGIC_WALL)) ||
6792         (CAN_PASS_DC_MAGIC_WALL(element) &&
6793          smashed == EL_DC_MAGIC_WALL))
6794     {
6795       int xx, yy;
6796       int activated_magic_wall =
6797         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6798          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6799          EL_DC_MAGIC_WALL_ACTIVE);
6800
6801       // activate magic wall / mill
6802       SCAN_PLAYFIELD(xx, yy)
6803       {
6804         if (Tile[xx][yy] == smashed)
6805           Tile[xx][yy] = activated_magic_wall;
6806       }
6807
6808       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6809       game.magic_wall_active = TRUE;
6810
6811       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6812                             SND_MAGIC_WALL_ACTIVATING :
6813                             smashed == EL_BD_MAGIC_WALL ?
6814                             SND_BD_MAGIC_WALL_ACTIVATING :
6815                             SND_DC_MAGIC_WALL_ACTIVATING));
6816     }
6817
6818     if (IS_PLAYER(x, y + 1))
6819     {
6820       if (CAN_SMASH_PLAYER(element))
6821       {
6822         KillPlayerUnlessEnemyProtected(x, y + 1);
6823         return;
6824       }
6825     }
6826     else if (smashed == EL_PENGUIN)
6827     {
6828       if (CAN_SMASH_PLAYER(element))
6829       {
6830         Bang(x, y + 1);
6831         return;
6832       }
6833     }
6834     else if (element == EL_BD_DIAMOND)
6835     {
6836       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6837       {
6838         Bang(x, y + 1);
6839         return;
6840       }
6841     }
6842     else if (((element == EL_SP_INFOTRON ||
6843                element == EL_SP_ZONK) &&
6844               (smashed == EL_SP_SNIKSNAK ||
6845                smashed == EL_SP_ELECTRON ||
6846                smashed == EL_SP_DISK_ORANGE)) ||
6847              (element == EL_SP_INFOTRON &&
6848               smashed == EL_SP_DISK_YELLOW))
6849     {
6850       Bang(x, y + 1);
6851       return;
6852     }
6853     else if (CAN_SMASH_EVERYTHING(element))
6854     {
6855       if (IS_CLASSIC_ENEMY(smashed) ||
6856           CAN_EXPLODE_SMASHED(smashed))
6857       {
6858         Bang(x, y + 1);
6859         return;
6860       }
6861       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6862       {
6863         if (smashed == EL_LAMP ||
6864             smashed == EL_LAMP_ACTIVE)
6865         {
6866           Bang(x, y + 1);
6867           return;
6868         }
6869         else if (smashed == EL_NUT)
6870         {
6871           Tile[x][y + 1] = EL_NUT_BREAKING;
6872           PlayLevelSound(x, y, SND_NUT_BREAKING);
6873           RaiseScoreElement(EL_NUT);
6874           return;
6875         }
6876         else if (smashed == EL_PEARL)
6877         {
6878           ResetGfxAnimation(x, y);
6879
6880           Tile[x][y + 1] = EL_PEARL_BREAKING;
6881           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6882           return;
6883         }
6884         else if (smashed == EL_DIAMOND)
6885         {
6886           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6887           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6888           return;
6889         }
6890         else if (IS_BELT_SWITCH(smashed))
6891         {
6892           ToggleBeltSwitch(x, y + 1);
6893         }
6894         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6895                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6896                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6897                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6898         {
6899           ToggleSwitchgateSwitch(x, y + 1);
6900         }
6901         else if (smashed == EL_LIGHT_SWITCH ||
6902                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6903         {
6904           ToggleLightSwitch(x, y + 1);
6905         }
6906         else
6907         {
6908           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6909
6910           CheckElementChangeBySide(x, y + 1, smashed, element,
6911                                    CE_SWITCHED, CH_SIDE_TOP);
6912           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6913                                             CH_SIDE_TOP);
6914         }
6915       }
6916       else
6917       {
6918         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6919       }
6920     }
6921   }
6922
6923   // play sound of magic wall / mill
6924   if (!last_line &&
6925       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6926        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6927        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6928   {
6929     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6930       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6931     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6932       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6933     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6934       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6935
6936     return;
6937   }
6938
6939   // play sound of object that hits the ground
6940   if (last_line || object_hit)
6941     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6942 }
6943
6944 static void TurnRoundExt(int x, int y)
6945 {
6946   static struct
6947   {
6948     int dx, dy;
6949   } move_xy[] =
6950   {
6951     {  0,  0 },
6952     { -1,  0 },
6953     { +1,  0 },
6954     {  0,  0 },
6955     {  0, -1 },
6956     {  0,  0 }, { 0, 0 }, { 0, 0 },
6957     {  0, +1 }
6958   };
6959   static struct
6960   {
6961     int left, right, back;
6962   } turn[] =
6963   {
6964     { 0,        0,              0        },
6965     { MV_DOWN,  MV_UP,          MV_RIGHT },
6966     { MV_UP,    MV_DOWN,        MV_LEFT  },
6967     { 0,        0,              0        },
6968     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6969     { 0,        0,              0        },
6970     { 0,        0,              0        },
6971     { 0,        0,              0        },
6972     { MV_RIGHT, MV_LEFT,        MV_UP    }
6973   };
6974
6975   int element = Tile[x][y];
6976   int move_pattern = element_info[element].move_pattern;
6977
6978   int old_move_dir = MovDir[x][y];
6979   int left_dir  = turn[old_move_dir].left;
6980   int right_dir = turn[old_move_dir].right;
6981   int back_dir  = turn[old_move_dir].back;
6982
6983   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6984   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6985   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6986   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6987
6988   int left_x  = x + left_dx,  left_y  = y + left_dy;
6989   int right_x = x + right_dx, right_y = y + right_dy;
6990   int move_x  = x + move_dx,  move_y  = y + move_dy;
6991
6992   int xx, yy;
6993
6994   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6995   {
6996     TestIfBadThingTouchesOtherBadThing(x, y);
6997
6998     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6999       MovDir[x][y] = right_dir;
7000     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7001       MovDir[x][y] = left_dir;
7002
7003     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7004       MovDelay[x][y] = 9;
7005     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7006       MovDelay[x][y] = 1;
7007   }
7008   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7009   {
7010     TestIfBadThingTouchesOtherBadThing(x, y);
7011
7012     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7013       MovDir[x][y] = left_dir;
7014     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7015       MovDir[x][y] = right_dir;
7016
7017     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7018       MovDelay[x][y] = 9;
7019     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7020       MovDelay[x][y] = 1;
7021   }
7022   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7023   {
7024     TestIfBadThingTouchesOtherBadThing(x, y);
7025
7026     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7027       MovDir[x][y] = left_dir;
7028     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7029       MovDir[x][y] = right_dir;
7030
7031     if (MovDir[x][y] != old_move_dir)
7032       MovDelay[x][y] = 9;
7033   }
7034   else if (element == EL_YAMYAM)
7035   {
7036     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7037     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7038
7039     if (can_turn_left && can_turn_right)
7040       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7041     else if (can_turn_left)
7042       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7043     else if (can_turn_right)
7044       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7045     else
7046       MovDir[x][y] = back_dir;
7047
7048     MovDelay[x][y] = 16 + 16 * RND(3);
7049   }
7050   else if (element == EL_DARK_YAMYAM)
7051   {
7052     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7053                                                          left_x, left_y);
7054     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7055                                                          right_x, right_y);
7056
7057     if (can_turn_left && can_turn_right)
7058       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7059     else if (can_turn_left)
7060       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7061     else if (can_turn_right)
7062       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7063     else
7064       MovDir[x][y] = back_dir;
7065
7066     MovDelay[x][y] = 16 + 16 * RND(3);
7067   }
7068   else if (element == EL_PACMAN)
7069   {
7070     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7071     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7072
7073     if (can_turn_left && can_turn_right)
7074       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7075     else if (can_turn_left)
7076       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7077     else if (can_turn_right)
7078       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7079     else
7080       MovDir[x][y] = back_dir;
7081
7082     MovDelay[x][y] = 6 + RND(40);
7083   }
7084   else if (element == EL_PIG)
7085   {
7086     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7087     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7088     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7089     boolean should_turn_left, should_turn_right, should_move_on;
7090     int rnd_value = 24;
7091     int rnd = RND(rnd_value);
7092
7093     should_turn_left = (can_turn_left &&
7094                         (!can_move_on ||
7095                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7096                                                    y + back_dy + left_dy)));
7097     should_turn_right = (can_turn_right &&
7098                          (!can_move_on ||
7099                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7100                                                     y + back_dy + right_dy)));
7101     should_move_on = (can_move_on &&
7102                       (!can_turn_left ||
7103                        !can_turn_right ||
7104                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7105                                                  y + move_dy + left_dy) ||
7106                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7107                                                  y + move_dy + right_dy)));
7108
7109     if (should_turn_left || should_turn_right || should_move_on)
7110     {
7111       if (should_turn_left && should_turn_right && should_move_on)
7112         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7113                         rnd < 2 * rnd_value / 3 ? right_dir :
7114                         old_move_dir);
7115       else if (should_turn_left && should_turn_right)
7116         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7117       else if (should_turn_left && should_move_on)
7118         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7119       else if (should_turn_right && should_move_on)
7120         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7121       else if (should_turn_left)
7122         MovDir[x][y] = left_dir;
7123       else if (should_turn_right)
7124         MovDir[x][y] = right_dir;
7125       else if (should_move_on)
7126         MovDir[x][y] = old_move_dir;
7127     }
7128     else if (can_move_on && rnd > rnd_value / 8)
7129       MovDir[x][y] = old_move_dir;
7130     else if (can_turn_left && can_turn_right)
7131       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7132     else if (can_turn_left && rnd > rnd_value / 8)
7133       MovDir[x][y] = left_dir;
7134     else if (can_turn_right && rnd > rnd_value/8)
7135       MovDir[x][y] = right_dir;
7136     else
7137       MovDir[x][y] = back_dir;
7138
7139     xx = x + move_xy[MovDir[x][y]].dx;
7140     yy = y + move_xy[MovDir[x][y]].dy;
7141
7142     if (!IN_LEV_FIELD(xx, yy) ||
7143         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7144       MovDir[x][y] = old_move_dir;
7145
7146     MovDelay[x][y] = 0;
7147   }
7148   else if (element == EL_DRAGON)
7149   {
7150     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7151     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7152     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7153     int rnd_value = 24;
7154     int rnd = RND(rnd_value);
7155
7156     if (can_move_on && rnd > rnd_value / 8)
7157       MovDir[x][y] = old_move_dir;
7158     else if (can_turn_left && can_turn_right)
7159       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7160     else if (can_turn_left && rnd > rnd_value / 8)
7161       MovDir[x][y] = left_dir;
7162     else if (can_turn_right && rnd > rnd_value / 8)
7163       MovDir[x][y] = right_dir;
7164     else
7165       MovDir[x][y] = back_dir;
7166
7167     xx = x + move_xy[MovDir[x][y]].dx;
7168     yy = y + move_xy[MovDir[x][y]].dy;
7169
7170     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7171       MovDir[x][y] = old_move_dir;
7172
7173     MovDelay[x][y] = 0;
7174   }
7175   else if (element == EL_MOLE)
7176   {
7177     boolean can_move_on =
7178       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7179                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7180                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7181     if (!can_move_on)
7182     {
7183       boolean can_turn_left =
7184         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7185                               IS_AMOEBOID(Tile[left_x][left_y])));
7186
7187       boolean can_turn_right =
7188         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7189                               IS_AMOEBOID(Tile[right_x][right_y])));
7190
7191       if (can_turn_left && can_turn_right)
7192         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7193       else if (can_turn_left)
7194         MovDir[x][y] = left_dir;
7195       else
7196         MovDir[x][y] = right_dir;
7197     }
7198
7199     if (MovDir[x][y] != old_move_dir)
7200       MovDelay[x][y] = 9;
7201   }
7202   else if (element == EL_BALLOON)
7203   {
7204     MovDir[x][y] = game.wind_direction;
7205     MovDelay[x][y] = 0;
7206   }
7207   else if (element == EL_SPRING)
7208   {
7209     if (MovDir[x][y] & MV_HORIZONTAL)
7210     {
7211       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7212           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7213       {
7214         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7215         ResetGfxAnimation(move_x, move_y);
7216         TEST_DrawLevelField(move_x, move_y);
7217
7218         MovDir[x][y] = back_dir;
7219       }
7220       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7221                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7222         MovDir[x][y] = MV_NONE;
7223     }
7224
7225     MovDelay[x][y] = 0;
7226   }
7227   else if (element == EL_ROBOT ||
7228            element == EL_SATELLITE ||
7229            element == EL_PENGUIN ||
7230            element == EL_EMC_ANDROID)
7231   {
7232     int attr_x = -1, attr_y = -1;
7233
7234     if (game.all_players_gone)
7235     {
7236       attr_x = game.exit_x;
7237       attr_y = game.exit_y;
7238     }
7239     else
7240     {
7241       int i;
7242
7243       for (i = 0; i < MAX_PLAYERS; i++)
7244       {
7245         struct PlayerInfo *player = &stored_player[i];
7246         int jx = player->jx, jy = player->jy;
7247
7248         if (!player->active)
7249           continue;
7250
7251         if (attr_x == -1 ||
7252             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7253         {
7254           attr_x = jx;
7255           attr_y = jy;
7256         }
7257       }
7258     }
7259
7260     if (element == EL_ROBOT &&
7261         game.robot_wheel_x >= 0 &&
7262         game.robot_wheel_y >= 0 &&
7263         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7264          game.engine_version < VERSION_IDENT(3,1,0,0)))
7265     {
7266       attr_x = game.robot_wheel_x;
7267       attr_y = game.robot_wheel_y;
7268     }
7269
7270     if (element == EL_PENGUIN)
7271     {
7272       int i;
7273       static int xy[4][2] =
7274       {
7275         { 0, -1 },
7276         { -1, 0 },
7277         { +1, 0 },
7278         { 0, +1 }
7279       };
7280
7281       for (i = 0; i < NUM_DIRECTIONS; i++)
7282       {
7283         int ex = x + xy[i][0];
7284         int ey = y + xy[i][1];
7285
7286         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7287                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7288                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7289                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7290         {
7291           attr_x = ex;
7292           attr_y = ey;
7293           break;
7294         }
7295       }
7296     }
7297
7298     MovDir[x][y] = MV_NONE;
7299     if (attr_x < x)
7300       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7301     else if (attr_x > x)
7302       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7303     if (attr_y < y)
7304       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7305     else if (attr_y > y)
7306       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7307
7308     if (element == EL_ROBOT)
7309     {
7310       int newx, newy;
7311
7312       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7313         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7314       Moving2Blocked(x, y, &newx, &newy);
7315
7316       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7317         MovDelay[x][y] = 8 + 8 * !RND(3);
7318       else
7319         MovDelay[x][y] = 16;
7320     }
7321     else if (element == EL_PENGUIN)
7322     {
7323       int newx, newy;
7324
7325       MovDelay[x][y] = 1;
7326
7327       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7328       {
7329         boolean first_horiz = RND(2);
7330         int new_move_dir = MovDir[x][y];
7331
7332         MovDir[x][y] =
7333           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7334         Moving2Blocked(x, y, &newx, &newy);
7335
7336         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7337           return;
7338
7339         MovDir[x][y] =
7340           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7341         Moving2Blocked(x, y, &newx, &newy);
7342
7343         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7344           return;
7345
7346         MovDir[x][y] = old_move_dir;
7347         return;
7348       }
7349     }
7350     else if (element == EL_SATELLITE)
7351     {
7352       int newx, newy;
7353
7354       MovDelay[x][y] = 1;
7355
7356       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7357       {
7358         boolean first_horiz = RND(2);
7359         int new_move_dir = MovDir[x][y];
7360
7361         MovDir[x][y] =
7362           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7363         Moving2Blocked(x, y, &newx, &newy);
7364
7365         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7366           return;
7367
7368         MovDir[x][y] =
7369           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7370         Moving2Blocked(x, y, &newx, &newy);
7371
7372         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7373           return;
7374
7375         MovDir[x][y] = old_move_dir;
7376         return;
7377       }
7378     }
7379     else if (element == EL_EMC_ANDROID)
7380     {
7381       static int check_pos[16] =
7382       {
7383         -1,             //  0 => (invalid)
7384         7,              //  1 => MV_LEFT
7385         3,              //  2 => MV_RIGHT
7386         -1,             //  3 => (invalid)
7387         1,              //  4 =>            MV_UP
7388         0,              //  5 => MV_LEFT  | MV_UP
7389         2,              //  6 => MV_RIGHT | MV_UP
7390         -1,             //  7 => (invalid)
7391         5,              //  8 =>            MV_DOWN
7392         6,              //  9 => MV_LEFT  | MV_DOWN
7393         4,              // 10 => MV_RIGHT | MV_DOWN
7394         -1,             // 11 => (invalid)
7395         -1,             // 12 => (invalid)
7396         -1,             // 13 => (invalid)
7397         -1,             // 14 => (invalid)
7398         -1,             // 15 => (invalid)
7399       };
7400       static struct
7401       {
7402         int dx, dy;
7403         int dir;
7404       } check_xy[8] =
7405       {
7406         { -1, -1,       MV_LEFT  | MV_UP   },
7407         {  0, -1,                  MV_UP   },
7408         { +1, -1,       MV_RIGHT | MV_UP   },
7409         { +1,  0,       MV_RIGHT           },
7410         { +1, +1,       MV_RIGHT | MV_DOWN },
7411         {  0, +1,                  MV_DOWN },
7412         { -1, +1,       MV_LEFT  | MV_DOWN },
7413         { -1,  0,       MV_LEFT            },
7414       };
7415       int start_pos, check_order;
7416       boolean can_clone = FALSE;
7417       int i;
7418
7419       // check if there is any free field around current position
7420       for (i = 0; i < 8; i++)
7421       {
7422         int newx = x + check_xy[i].dx;
7423         int newy = y + check_xy[i].dy;
7424
7425         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7426         {
7427           can_clone = TRUE;
7428
7429           break;
7430         }
7431       }
7432
7433       if (can_clone)            // randomly find an element to clone
7434       {
7435         can_clone = FALSE;
7436
7437         start_pos = check_pos[RND(8)];
7438         check_order = (RND(2) ? -1 : +1);
7439
7440         for (i = 0; i < 8; i++)
7441         {
7442           int pos_raw = start_pos + i * check_order;
7443           int pos = (pos_raw + 8) % 8;
7444           int newx = x + check_xy[pos].dx;
7445           int newy = y + check_xy[pos].dy;
7446
7447           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7448           {
7449             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7450             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7451
7452             Store[x][y] = Tile[newx][newy];
7453
7454             can_clone = TRUE;
7455
7456             break;
7457           }
7458         }
7459       }
7460
7461       if (can_clone)            // randomly find a direction to move
7462       {
7463         can_clone = FALSE;
7464
7465         start_pos = check_pos[RND(8)];
7466         check_order = (RND(2) ? -1 : +1);
7467
7468         for (i = 0; i < 8; i++)
7469         {
7470           int pos_raw = start_pos + i * check_order;
7471           int pos = (pos_raw + 8) % 8;
7472           int newx = x + check_xy[pos].dx;
7473           int newy = y + check_xy[pos].dy;
7474           int new_move_dir = check_xy[pos].dir;
7475
7476           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7477           {
7478             MovDir[x][y] = new_move_dir;
7479             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7480
7481             can_clone = TRUE;
7482
7483             break;
7484           }
7485         }
7486       }
7487
7488       if (can_clone)            // cloning and moving successful
7489         return;
7490
7491       // cannot clone -- try to move towards player
7492
7493       start_pos = check_pos[MovDir[x][y] & 0x0f];
7494       check_order = (RND(2) ? -1 : +1);
7495
7496       for (i = 0; i < 3; i++)
7497       {
7498         // first check start_pos, then previous/next or (next/previous) pos
7499         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7500         int pos = (pos_raw + 8) % 8;
7501         int newx = x + check_xy[pos].dx;
7502         int newy = y + check_xy[pos].dy;
7503         int new_move_dir = check_xy[pos].dir;
7504
7505         if (IS_PLAYER(newx, newy))
7506           break;
7507
7508         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7509         {
7510           MovDir[x][y] = new_move_dir;
7511           MovDelay[x][y] = level.android_move_time * 8 + 1;
7512
7513           break;
7514         }
7515       }
7516     }
7517   }
7518   else if (move_pattern == MV_TURNING_LEFT ||
7519            move_pattern == MV_TURNING_RIGHT ||
7520            move_pattern == MV_TURNING_LEFT_RIGHT ||
7521            move_pattern == MV_TURNING_RIGHT_LEFT ||
7522            move_pattern == MV_TURNING_RANDOM ||
7523            move_pattern == MV_ALL_DIRECTIONS)
7524   {
7525     boolean can_turn_left =
7526       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7527     boolean can_turn_right =
7528       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7529
7530     if (element_info[element].move_stepsize == 0)       // "not moving"
7531       return;
7532
7533     if (move_pattern == MV_TURNING_LEFT)
7534       MovDir[x][y] = left_dir;
7535     else if (move_pattern == MV_TURNING_RIGHT)
7536       MovDir[x][y] = right_dir;
7537     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7538       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7539     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7540       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7541     else if (move_pattern == MV_TURNING_RANDOM)
7542       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7543                       can_turn_right && !can_turn_left ? right_dir :
7544                       RND(2) ? left_dir : right_dir);
7545     else if (can_turn_left && can_turn_right)
7546       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7547     else if (can_turn_left)
7548       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7549     else if (can_turn_right)
7550       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7551     else
7552       MovDir[x][y] = back_dir;
7553
7554     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7555   }
7556   else if (move_pattern == MV_HORIZONTAL ||
7557            move_pattern == MV_VERTICAL)
7558   {
7559     if (move_pattern & old_move_dir)
7560       MovDir[x][y] = back_dir;
7561     else if (move_pattern == MV_HORIZONTAL)
7562       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7563     else if (move_pattern == MV_VERTICAL)
7564       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7565
7566     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7567   }
7568   else if (move_pattern & MV_ANY_DIRECTION)
7569   {
7570     MovDir[x][y] = move_pattern;
7571     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7572   }
7573   else if (move_pattern & MV_WIND_DIRECTION)
7574   {
7575     MovDir[x][y] = game.wind_direction;
7576     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7577   }
7578   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7579   {
7580     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7581       MovDir[x][y] = left_dir;
7582     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7583       MovDir[x][y] = right_dir;
7584
7585     if (MovDir[x][y] != old_move_dir)
7586       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7587   }
7588   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7589   {
7590     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7591       MovDir[x][y] = right_dir;
7592     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7593       MovDir[x][y] = left_dir;
7594
7595     if (MovDir[x][y] != old_move_dir)
7596       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7597   }
7598   else if (move_pattern == MV_TOWARDS_PLAYER ||
7599            move_pattern == MV_AWAY_FROM_PLAYER)
7600   {
7601     int attr_x = -1, attr_y = -1;
7602     int newx, newy;
7603     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7604
7605     if (game.all_players_gone)
7606     {
7607       attr_x = game.exit_x;
7608       attr_y = game.exit_y;
7609     }
7610     else
7611     {
7612       int i;
7613
7614       for (i = 0; i < MAX_PLAYERS; i++)
7615       {
7616         struct PlayerInfo *player = &stored_player[i];
7617         int jx = player->jx, jy = player->jy;
7618
7619         if (!player->active)
7620           continue;
7621
7622         if (attr_x == -1 ||
7623             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7624         {
7625           attr_x = jx;
7626           attr_y = jy;
7627         }
7628       }
7629     }
7630
7631     MovDir[x][y] = MV_NONE;
7632     if (attr_x < x)
7633       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7634     else if (attr_x > x)
7635       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7636     if (attr_y < y)
7637       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7638     else if (attr_y > y)
7639       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7640
7641     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7642
7643     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7644     {
7645       boolean first_horiz = RND(2);
7646       int new_move_dir = MovDir[x][y];
7647
7648       if (element_info[element].move_stepsize == 0)     // "not moving"
7649       {
7650         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7651         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7652
7653         return;
7654       }
7655
7656       MovDir[x][y] =
7657         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7658       Moving2Blocked(x, y, &newx, &newy);
7659
7660       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7661         return;
7662
7663       MovDir[x][y] =
7664         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7665       Moving2Blocked(x, y, &newx, &newy);
7666
7667       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7668         return;
7669
7670       MovDir[x][y] = old_move_dir;
7671     }
7672   }
7673   else if (move_pattern == MV_WHEN_PUSHED ||
7674            move_pattern == MV_WHEN_DROPPED)
7675   {
7676     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7677       MovDir[x][y] = MV_NONE;
7678
7679     MovDelay[x][y] = 0;
7680   }
7681   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7682   {
7683     static int test_xy[7][2] =
7684     {
7685       { 0, -1 },
7686       { -1, 0 },
7687       { +1, 0 },
7688       { 0, +1 },
7689       { 0, -1 },
7690       { -1, 0 },
7691       { +1, 0 },
7692     };
7693     static int test_dir[7] =
7694     {
7695       MV_UP,
7696       MV_LEFT,
7697       MV_RIGHT,
7698       MV_DOWN,
7699       MV_UP,
7700       MV_LEFT,
7701       MV_RIGHT,
7702     };
7703     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7704     int move_preference = -1000000;     // start with very low preference
7705     int new_move_dir = MV_NONE;
7706     int start_test = RND(4);
7707     int i;
7708
7709     for (i = 0; i < NUM_DIRECTIONS; i++)
7710     {
7711       int move_dir = test_dir[start_test + i];
7712       int move_dir_preference;
7713
7714       xx = x + test_xy[start_test + i][0];
7715       yy = y + test_xy[start_test + i][1];
7716
7717       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7718           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7719       {
7720         new_move_dir = move_dir;
7721
7722         break;
7723       }
7724
7725       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7726         continue;
7727
7728       move_dir_preference = -1 * RunnerVisit[xx][yy];
7729       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7730         move_dir_preference = PlayerVisit[xx][yy];
7731
7732       if (move_dir_preference > move_preference)
7733       {
7734         // prefer field that has not been visited for the longest time
7735         move_preference = move_dir_preference;
7736         new_move_dir = move_dir;
7737       }
7738       else if (move_dir_preference == move_preference &&
7739                move_dir == old_move_dir)
7740       {
7741         // prefer last direction when all directions are preferred equally
7742         move_preference = move_dir_preference;
7743         new_move_dir = move_dir;
7744       }
7745     }
7746
7747     MovDir[x][y] = new_move_dir;
7748     if (old_move_dir != new_move_dir)
7749       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7750   }
7751 }
7752
7753 static void TurnRound(int x, int y)
7754 {
7755   int direction = MovDir[x][y];
7756
7757   TurnRoundExt(x, y);
7758
7759   GfxDir[x][y] = MovDir[x][y];
7760
7761   if (direction != MovDir[x][y])
7762     GfxFrame[x][y] = 0;
7763
7764   if (MovDelay[x][y])
7765     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7766
7767   ResetGfxFrame(x, y);
7768 }
7769
7770 static boolean JustBeingPushed(int x, int y)
7771 {
7772   int i;
7773
7774   for (i = 0; i < MAX_PLAYERS; i++)
7775   {
7776     struct PlayerInfo *player = &stored_player[i];
7777
7778     if (player->active && player->is_pushing && player->MovPos)
7779     {
7780       int next_jx = player->jx + (player->jx - player->last_jx);
7781       int next_jy = player->jy + (player->jy - player->last_jy);
7782
7783       if (x == next_jx && y == next_jy)
7784         return TRUE;
7785     }
7786   }
7787
7788   return FALSE;
7789 }
7790
7791 static void StartMoving(int x, int y)
7792 {
7793   boolean started_moving = FALSE;       // some elements can fall _and_ move
7794   int element = Tile[x][y];
7795
7796   if (Stop[x][y])
7797     return;
7798
7799   if (MovDelay[x][y] == 0)
7800     GfxAction[x][y] = ACTION_DEFAULT;
7801
7802   if (CAN_FALL(element) && y < lev_fieldy - 1)
7803   {
7804     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7805         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7806       if (JustBeingPushed(x, y))
7807         return;
7808
7809     if (element == EL_QUICKSAND_FULL)
7810     {
7811       if (IS_FREE(x, y + 1))
7812       {
7813         InitMovingField(x, y, MV_DOWN);
7814         started_moving = TRUE;
7815
7816         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7817 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7818         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7819           Store[x][y] = EL_ROCK;
7820 #else
7821         Store[x][y] = EL_ROCK;
7822 #endif
7823
7824         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7825       }
7826       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7827       {
7828         if (!MovDelay[x][y])
7829         {
7830           MovDelay[x][y] = TILEY + 1;
7831
7832           ResetGfxAnimation(x, y);
7833           ResetGfxAnimation(x, y + 1);
7834         }
7835
7836         if (MovDelay[x][y])
7837         {
7838           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7839           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7840
7841           MovDelay[x][y]--;
7842           if (MovDelay[x][y])
7843             return;
7844         }
7845
7846         Tile[x][y] = EL_QUICKSAND_EMPTY;
7847         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7848         Store[x][y + 1] = Store[x][y];
7849         Store[x][y] = 0;
7850
7851         PlayLevelSoundAction(x, y, ACTION_FILLING);
7852       }
7853       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7854       {
7855         if (!MovDelay[x][y])
7856         {
7857           MovDelay[x][y] = TILEY + 1;
7858
7859           ResetGfxAnimation(x, y);
7860           ResetGfxAnimation(x, y + 1);
7861         }
7862
7863         if (MovDelay[x][y])
7864         {
7865           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7866           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7867
7868           MovDelay[x][y]--;
7869           if (MovDelay[x][y])
7870             return;
7871         }
7872
7873         Tile[x][y] = EL_QUICKSAND_EMPTY;
7874         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7875         Store[x][y + 1] = Store[x][y];
7876         Store[x][y] = 0;
7877
7878         PlayLevelSoundAction(x, y, ACTION_FILLING);
7879       }
7880     }
7881     else if (element == EL_QUICKSAND_FAST_FULL)
7882     {
7883       if (IS_FREE(x, y + 1))
7884       {
7885         InitMovingField(x, y, MV_DOWN);
7886         started_moving = TRUE;
7887
7888         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7889 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7890         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7891           Store[x][y] = EL_ROCK;
7892 #else
7893         Store[x][y] = EL_ROCK;
7894 #endif
7895
7896         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7897       }
7898       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7899       {
7900         if (!MovDelay[x][y])
7901         {
7902           MovDelay[x][y] = TILEY + 1;
7903
7904           ResetGfxAnimation(x, y);
7905           ResetGfxAnimation(x, y + 1);
7906         }
7907
7908         if (MovDelay[x][y])
7909         {
7910           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7911           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7912
7913           MovDelay[x][y]--;
7914           if (MovDelay[x][y])
7915             return;
7916         }
7917
7918         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7919         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7920         Store[x][y + 1] = Store[x][y];
7921         Store[x][y] = 0;
7922
7923         PlayLevelSoundAction(x, y, ACTION_FILLING);
7924       }
7925       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7926       {
7927         if (!MovDelay[x][y])
7928         {
7929           MovDelay[x][y] = TILEY + 1;
7930
7931           ResetGfxAnimation(x, y);
7932           ResetGfxAnimation(x, y + 1);
7933         }
7934
7935         if (MovDelay[x][y])
7936         {
7937           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7938           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7939
7940           MovDelay[x][y]--;
7941           if (MovDelay[x][y])
7942             return;
7943         }
7944
7945         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7946         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7947         Store[x][y + 1] = Store[x][y];
7948         Store[x][y] = 0;
7949
7950         PlayLevelSoundAction(x, y, ACTION_FILLING);
7951       }
7952     }
7953     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7954              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7955     {
7956       InitMovingField(x, y, MV_DOWN);
7957       started_moving = TRUE;
7958
7959       Tile[x][y] = EL_QUICKSAND_FILLING;
7960       Store[x][y] = element;
7961
7962       PlayLevelSoundAction(x, y, ACTION_FILLING);
7963     }
7964     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7965              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7966     {
7967       InitMovingField(x, y, MV_DOWN);
7968       started_moving = TRUE;
7969
7970       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7971       Store[x][y] = element;
7972
7973       PlayLevelSoundAction(x, y, ACTION_FILLING);
7974     }
7975     else if (element == EL_MAGIC_WALL_FULL)
7976     {
7977       if (IS_FREE(x, y + 1))
7978       {
7979         InitMovingField(x, y, MV_DOWN);
7980         started_moving = TRUE;
7981
7982         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7983         Store[x][y] = EL_CHANGED(Store[x][y]);
7984       }
7985       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7986       {
7987         if (!MovDelay[x][y])
7988           MovDelay[x][y] = TILEY / 4 + 1;
7989
7990         if (MovDelay[x][y])
7991         {
7992           MovDelay[x][y]--;
7993           if (MovDelay[x][y])
7994             return;
7995         }
7996
7997         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7998         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7999         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8000         Store[x][y] = 0;
8001       }
8002     }
8003     else if (element == EL_BD_MAGIC_WALL_FULL)
8004     {
8005       if (IS_FREE(x, y + 1))
8006       {
8007         InitMovingField(x, y, MV_DOWN);
8008         started_moving = TRUE;
8009
8010         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8011         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8012       }
8013       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8014       {
8015         if (!MovDelay[x][y])
8016           MovDelay[x][y] = TILEY / 4 + 1;
8017
8018         if (MovDelay[x][y])
8019         {
8020           MovDelay[x][y]--;
8021           if (MovDelay[x][y])
8022             return;
8023         }
8024
8025         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8026         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8027         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8028         Store[x][y] = 0;
8029       }
8030     }
8031     else if (element == EL_DC_MAGIC_WALL_FULL)
8032     {
8033       if (IS_FREE(x, y + 1))
8034       {
8035         InitMovingField(x, y, MV_DOWN);
8036         started_moving = TRUE;
8037
8038         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8039         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8040       }
8041       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8042       {
8043         if (!MovDelay[x][y])
8044           MovDelay[x][y] = TILEY / 4 + 1;
8045
8046         if (MovDelay[x][y])
8047         {
8048           MovDelay[x][y]--;
8049           if (MovDelay[x][y])
8050             return;
8051         }
8052
8053         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8054         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8055         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8056         Store[x][y] = 0;
8057       }
8058     }
8059     else if ((CAN_PASS_MAGIC_WALL(element) &&
8060               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8061                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8062              (CAN_PASS_DC_MAGIC_WALL(element) &&
8063               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8064
8065     {
8066       InitMovingField(x, y, MV_DOWN);
8067       started_moving = TRUE;
8068
8069       Tile[x][y] =
8070         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8071          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8072          EL_DC_MAGIC_WALL_FILLING);
8073       Store[x][y] = element;
8074     }
8075     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8076     {
8077       SplashAcid(x, y + 1);
8078
8079       InitMovingField(x, y, MV_DOWN);
8080       started_moving = TRUE;
8081
8082       Store[x][y] = EL_ACID;
8083     }
8084     else if (
8085              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8086               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8087              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8088               CAN_FALL(element) && WasJustFalling[x][y] &&
8089               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8090
8091              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8092               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8093               (Tile[x][y + 1] == EL_BLOCKED)))
8094     {
8095       /* this is needed for a special case not covered by calling "Impact()"
8096          from "ContinueMoving()": if an element moves to a tile directly below
8097          another element which was just falling on that tile (which was empty
8098          in the previous frame), the falling element above would just stop
8099          instead of smashing the element below (in previous version, the above
8100          element was just checked for "moving" instead of "falling", resulting
8101          in incorrect smashes caused by horizontal movement of the above
8102          element; also, the case of the player being the element to smash was
8103          simply not covered here... :-/ ) */
8104
8105       CheckCollision[x][y] = 0;
8106       CheckImpact[x][y] = 0;
8107
8108       Impact(x, y);
8109     }
8110     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8111     {
8112       if (MovDir[x][y] == MV_NONE)
8113       {
8114         InitMovingField(x, y, MV_DOWN);
8115         started_moving = TRUE;
8116       }
8117     }
8118     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8119     {
8120       if (WasJustFalling[x][y]) // prevent animation from being restarted
8121         MovDir[x][y] = MV_DOWN;
8122
8123       InitMovingField(x, y, MV_DOWN);
8124       started_moving = TRUE;
8125     }
8126     else if (element == EL_AMOEBA_DROP)
8127     {
8128       Tile[x][y] = EL_AMOEBA_GROWING;
8129       Store[x][y] = EL_AMOEBA_WET;
8130     }
8131     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8132               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8133              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8134              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8135     {
8136       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8137                                 (IS_FREE(x - 1, y + 1) ||
8138                                  Tile[x - 1][y + 1] == EL_ACID));
8139       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8140                                 (IS_FREE(x + 1, y + 1) ||
8141                                  Tile[x + 1][y + 1] == EL_ACID));
8142       boolean can_fall_any  = (can_fall_left || can_fall_right);
8143       boolean can_fall_both = (can_fall_left && can_fall_right);
8144       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8145
8146       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8147       {
8148         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8149           can_fall_right = FALSE;
8150         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8151           can_fall_left = FALSE;
8152         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8153           can_fall_right = FALSE;
8154         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8155           can_fall_left = FALSE;
8156
8157         can_fall_any  = (can_fall_left || can_fall_right);
8158         can_fall_both = FALSE;
8159       }
8160
8161       if (can_fall_both)
8162       {
8163         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8164           can_fall_right = FALSE;       // slip down on left side
8165         else
8166           can_fall_left = !(can_fall_right = RND(2));
8167
8168         can_fall_both = FALSE;
8169       }
8170
8171       if (can_fall_any)
8172       {
8173         // if not determined otherwise, prefer left side for slipping down
8174         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8175         started_moving = TRUE;
8176       }
8177     }
8178     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8179     {
8180       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8181       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8182       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8183       int belt_dir = game.belt_dir[belt_nr];
8184
8185       if ((belt_dir == MV_LEFT  && left_is_free) ||
8186           (belt_dir == MV_RIGHT && right_is_free))
8187       {
8188         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8189
8190         InitMovingField(x, y, belt_dir);
8191         started_moving = TRUE;
8192
8193         Pushed[x][y] = TRUE;
8194         Pushed[nextx][y] = TRUE;
8195
8196         GfxAction[x][y] = ACTION_DEFAULT;
8197       }
8198       else
8199       {
8200         MovDir[x][y] = 0;       // if element was moving, stop it
8201       }
8202     }
8203   }
8204
8205   // not "else if" because of elements that can fall and move (EL_SPRING)
8206   if (CAN_MOVE(element) && !started_moving)
8207   {
8208     int move_pattern = element_info[element].move_pattern;
8209     int newx, newy;
8210
8211     Moving2Blocked(x, y, &newx, &newy);
8212
8213     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8214       return;
8215
8216     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8217         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8218     {
8219       WasJustMoving[x][y] = 0;
8220       CheckCollision[x][y] = 0;
8221
8222       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8223
8224       if (Tile[x][y] != element)        // element has changed
8225         return;
8226     }
8227
8228     if (!MovDelay[x][y])        // start new movement phase
8229     {
8230       // all objects that can change their move direction after each step
8231       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8232
8233       if (element != EL_YAMYAM &&
8234           element != EL_DARK_YAMYAM &&
8235           element != EL_PACMAN &&
8236           !(move_pattern & MV_ANY_DIRECTION) &&
8237           move_pattern != MV_TURNING_LEFT &&
8238           move_pattern != MV_TURNING_RIGHT &&
8239           move_pattern != MV_TURNING_LEFT_RIGHT &&
8240           move_pattern != MV_TURNING_RIGHT_LEFT &&
8241           move_pattern != MV_TURNING_RANDOM)
8242       {
8243         TurnRound(x, y);
8244
8245         if (MovDelay[x][y] && (element == EL_BUG ||
8246                                element == EL_SPACESHIP ||
8247                                element == EL_SP_SNIKSNAK ||
8248                                element == EL_SP_ELECTRON ||
8249                                element == EL_MOLE))
8250           TEST_DrawLevelField(x, y);
8251       }
8252     }
8253
8254     if (MovDelay[x][y])         // wait some time before next movement
8255     {
8256       MovDelay[x][y]--;
8257
8258       if (element == EL_ROBOT ||
8259           element == EL_YAMYAM ||
8260           element == EL_DARK_YAMYAM)
8261       {
8262         DrawLevelElementAnimationIfNeeded(x, y, element);
8263         PlayLevelSoundAction(x, y, ACTION_WAITING);
8264       }
8265       else if (element == EL_SP_ELECTRON)
8266         DrawLevelElementAnimationIfNeeded(x, y, element);
8267       else if (element == EL_DRAGON)
8268       {
8269         int i;
8270         int dir = MovDir[x][y];
8271         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8272         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8273         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8274                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8275                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8276                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8277         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8278
8279         GfxAction[x][y] = ACTION_ATTACKING;
8280
8281         if (IS_PLAYER(x, y))
8282           DrawPlayerField(x, y);
8283         else
8284           TEST_DrawLevelField(x, y);
8285
8286         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8287
8288         for (i = 1; i <= 3; i++)
8289         {
8290           int xx = x + i * dx;
8291           int yy = y + i * dy;
8292           int sx = SCREENX(xx);
8293           int sy = SCREENY(yy);
8294           int flame_graphic = graphic + (i - 1);
8295
8296           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8297             break;
8298
8299           if (MovDelay[x][y])
8300           {
8301             int flamed = MovingOrBlocked2Element(xx, yy);
8302
8303             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8304               Bang(xx, yy);
8305             else
8306               RemoveMovingField(xx, yy);
8307
8308             ChangeDelay[xx][yy] = 0;
8309
8310             Tile[xx][yy] = EL_FLAMES;
8311
8312             if (IN_SCR_FIELD(sx, sy))
8313             {
8314               TEST_DrawLevelFieldCrumbled(xx, yy);
8315               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8316             }
8317           }
8318           else
8319           {
8320             if (Tile[xx][yy] == EL_FLAMES)
8321               Tile[xx][yy] = EL_EMPTY;
8322             TEST_DrawLevelField(xx, yy);
8323           }
8324         }
8325       }
8326
8327       if (MovDelay[x][y])       // element still has to wait some time
8328       {
8329         PlayLevelSoundAction(x, y, ACTION_WAITING);
8330
8331         return;
8332       }
8333     }
8334
8335     // now make next step
8336
8337     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8338
8339     if (DONT_COLLIDE_WITH(element) &&
8340         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8341         !PLAYER_ENEMY_PROTECTED(newx, newy))
8342     {
8343       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8344
8345       return;
8346     }
8347
8348     else if (CAN_MOVE_INTO_ACID(element) &&
8349              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8350              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8351              (MovDir[x][y] == MV_DOWN ||
8352               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8353     {
8354       SplashAcid(newx, newy);
8355       Store[x][y] = EL_ACID;
8356     }
8357     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8358     {
8359       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8360           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8361           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8362           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8363       {
8364         RemoveField(x, y);
8365         TEST_DrawLevelField(x, y);
8366
8367         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8368         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8369           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8370
8371         game.friends_still_needed--;
8372         if (!game.friends_still_needed &&
8373             !game.GameOver &&
8374             game.all_players_gone)
8375           LevelSolved();
8376
8377         return;
8378       }
8379       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8380       {
8381         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8382           TEST_DrawLevelField(newx, newy);
8383         else
8384           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8385       }
8386       else if (!IS_FREE(newx, newy))
8387       {
8388         GfxAction[x][y] = ACTION_WAITING;
8389
8390         if (IS_PLAYER(x, y))
8391           DrawPlayerField(x, y);
8392         else
8393           TEST_DrawLevelField(x, y);
8394
8395         return;
8396       }
8397     }
8398     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8399     {
8400       if (IS_FOOD_PIG(Tile[newx][newy]))
8401       {
8402         if (IS_MOVING(newx, newy))
8403           RemoveMovingField(newx, newy);
8404         else
8405         {
8406           Tile[newx][newy] = EL_EMPTY;
8407           TEST_DrawLevelField(newx, newy);
8408         }
8409
8410         PlayLevelSound(x, y, SND_PIG_DIGGING);
8411       }
8412       else if (!IS_FREE(newx, newy))
8413       {
8414         if (IS_PLAYER(x, y))
8415           DrawPlayerField(x, y);
8416         else
8417           TEST_DrawLevelField(x, y);
8418
8419         return;
8420       }
8421     }
8422     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8423     {
8424       if (Store[x][y] != EL_EMPTY)
8425       {
8426         boolean can_clone = FALSE;
8427         int xx, yy;
8428
8429         // check if element to clone is still there
8430         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8431         {
8432           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8433           {
8434             can_clone = TRUE;
8435
8436             break;
8437           }
8438         }
8439
8440         // cannot clone or target field not free anymore -- do not clone
8441         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8442           Store[x][y] = EL_EMPTY;
8443       }
8444
8445       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8446       {
8447         if (IS_MV_DIAGONAL(MovDir[x][y]))
8448         {
8449           int diagonal_move_dir = MovDir[x][y];
8450           int stored = Store[x][y];
8451           int change_delay = 8;
8452           int graphic;
8453
8454           // android is moving diagonally
8455
8456           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8457
8458           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8459           GfxElement[x][y] = EL_EMC_ANDROID;
8460           GfxAction[x][y] = ACTION_SHRINKING;
8461           GfxDir[x][y] = diagonal_move_dir;
8462           ChangeDelay[x][y] = change_delay;
8463
8464           if (Store[x][y] == EL_EMPTY)
8465             Store[x][y] = GfxElementEmpty[x][y];
8466
8467           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8468                                    GfxDir[x][y]);
8469
8470           DrawLevelGraphicAnimation(x, y, graphic);
8471           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8472
8473           if (Tile[newx][newy] == EL_ACID)
8474           {
8475             SplashAcid(newx, newy);
8476
8477             return;
8478           }
8479
8480           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8481
8482           Store[newx][newy] = EL_EMC_ANDROID;
8483           GfxElement[newx][newy] = EL_EMC_ANDROID;
8484           GfxAction[newx][newy] = ACTION_GROWING;
8485           GfxDir[newx][newy] = diagonal_move_dir;
8486           ChangeDelay[newx][newy] = change_delay;
8487
8488           graphic = el_act_dir2img(GfxElement[newx][newy],
8489                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8490
8491           DrawLevelGraphicAnimation(newx, newy, graphic);
8492           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8493
8494           return;
8495         }
8496         else
8497         {
8498           Tile[newx][newy] = EL_EMPTY;
8499           TEST_DrawLevelField(newx, newy);
8500
8501           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8502         }
8503       }
8504       else if (!IS_FREE(newx, newy))
8505       {
8506         return;
8507       }
8508     }
8509     else if (IS_CUSTOM_ELEMENT(element) &&
8510              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8511     {
8512       if (!DigFieldByCE(newx, newy, element))
8513         return;
8514
8515       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8516       {
8517         RunnerVisit[x][y] = FrameCounter;
8518         PlayerVisit[x][y] /= 8;         // expire player visit path
8519       }
8520     }
8521     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8522     {
8523       if (!IS_FREE(newx, newy))
8524       {
8525         if (IS_PLAYER(x, y))
8526           DrawPlayerField(x, y);
8527         else
8528           TEST_DrawLevelField(x, y);
8529
8530         return;
8531       }
8532       else
8533       {
8534         boolean wanna_flame = !RND(10);
8535         int dx = newx - x, dy = newy - y;
8536         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8537         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8538         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8539                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8540         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8541                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8542
8543         if ((wanna_flame ||
8544              IS_CLASSIC_ENEMY(element1) ||
8545              IS_CLASSIC_ENEMY(element2)) &&
8546             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8547             element1 != EL_FLAMES && element2 != EL_FLAMES)
8548         {
8549           ResetGfxAnimation(x, y);
8550           GfxAction[x][y] = ACTION_ATTACKING;
8551
8552           if (IS_PLAYER(x, y))
8553             DrawPlayerField(x, y);
8554           else
8555             TEST_DrawLevelField(x, y);
8556
8557           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8558
8559           MovDelay[x][y] = 50;
8560
8561           Tile[newx][newy] = EL_FLAMES;
8562           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8563             Tile[newx1][newy1] = EL_FLAMES;
8564           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8565             Tile[newx2][newy2] = EL_FLAMES;
8566
8567           return;
8568         }
8569       }
8570     }
8571     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8572              Tile[newx][newy] == EL_DIAMOND)
8573     {
8574       if (IS_MOVING(newx, newy))
8575         RemoveMovingField(newx, newy);
8576       else
8577       {
8578         Tile[newx][newy] = EL_EMPTY;
8579         TEST_DrawLevelField(newx, newy);
8580       }
8581
8582       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8583     }
8584     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8585              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8586     {
8587       if (AmoebaNr[newx][newy])
8588       {
8589         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8590         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8591             Tile[newx][newy] == EL_BD_AMOEBA)
8592           AmoebaCnt[AmoebaNr[newx][newy]]--;
8593       }
8594
8595       if (IS_MOVING(newx, newy))
8596       {
8597         RemoveMovingField(newx, newy);
8598       }
8599       else
8600       {
8601         Tile[newx][newy] = EL_EMPTY;
8602         TEST_DrawLevelField(newx, newy);
8603       }
8604
8605       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8606     }
8607     else if ((element == EL_PACMAN || element == EL_MOLE)
8608              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8609     {
8610       if (AmoebaNr[newx][newy])
8611       {
8612         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8613         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8614             Tile[newx][newy] == EL_BD_AMOEBA)
8615           AmoebaCnt[AmoebaNr[newx][newy]]--;
8616       }
8617
8618       if (element == EL_MOLE)
8619       {
8620         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8621         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8622
8623         ResetGfxAnimation(x, y);
8624         GfxAction[x][y] = ACTION_DIGGING;
8625         TEST_DrawLevelField(x, y);
8626
8627         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8628
8629         return;                         // wait for shrinking amoeba
8630       }
8631       else      // element == EL_PACMAN
8632       {
8633         Tile[newx][newy] = EL_EMPTY;
8634         TEST_DrawLevelField(newx, newy);
8635         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8636       }
8637     }
8638     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8639              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8640               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8641     {
8642       // wait for shrinking amoeba to completely disappear
8643       return;
8644     }
8645     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8646     {
8647       // object was running against a wall
8648
8649       TurnRound(x, y);
8650
8651       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8652         DrawLevelElementAnimation(x, y, element);
8653
8654       if (DONT_TOUCH(element))
8655         TestIfBadThingTouchesPlayer(x, y);
8656
8657       return;
8658     }
8659
8660     InitMovingField(x, y, MovDir[x][y]);
8661
8662     PlayLevelSoundAction(x, y, ACTION_MOVING);
8663   }
8664
8665   if (MovDir[x][y])
8666     ContinueMoving(x, y);
8667 }
8668
8669 void ContinueMoving(int x, int y)
8670 {
8671   int element = Tile[x][y];
8672   struct ElementInfo *ei = &element_info[element];
8673   int direction = MovDir[x][y];
8674   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8675   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8676   int newx = x + dx, newy = y + dy;
8677   int stored = Store[x][y];
8678   int stored_new = Store[newx][newy];
8679   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8680   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8681   boolean last_line = (newy == lev_fieldy - 1);
8682   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8683
8684   if (pushed_by_player)         // special case: moving object pushed by player
8685   {
8686     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8687   }
8688   else if (use_step_delay)      // special case: moving object has step delay
8689   {
8690     if (!MovDelay[x][y])
8691       MovPos[x][y] += getElementMoveStepsize(x, y);
8692
8693     if (MovDelay[x][y])
8694       MovDelay[x][y]--;
8695     else
8696       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8697
8698     if (MovDelay[x][y])
8699     {
8700       TEST_DrawLevelField(x, y);
8701
8702       return;   // element is still waiting
8703     }
8704   }
8705   else                          // normal case: generically moving object
8706   {
8707     MovPos[x][y] += getElementMoveStepsize(x, y);
8708   }
8709
8710   if (ABS(MovPos[x][y]) < TILEX)
8711   {
8712     TEST_DrawLevelField(x, y);
8713
8714     return;     // element is still moving
8715   }
8716
8717   // element reached destination field
8718
8719   Tile[x][y] = EL_EMPTY;
8720   Tile[newx][newy] = element;
8721   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8722
8723   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8724   {
8725     element = Tile[newx][newy] = EL_ACID;
8726   }
8727   else if (element == EL_MOLE)
8728   {
8729     Tile[x][y] = EL_SAND;
8730
8731     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8732   }
8733   else if (element == EL_QUICKSAND_FILLING)
8734   {
8735     element = Tile[newx][newy] = get_next_element(element);
8736     Store[newx][newy] = Store[x][y];
8737   }
8738   else if (element == EL_QUICKSAND_EMPTYING)
8739   {
8740     Tile[x][y] = get_next_element(element);
8741     element = Tile[newx][newy] = Store[x][y];
8742   }
8743   else if (element == EL_QUICKSAND_FAST_FILLING)
8744   {
8745     element = Tile[newx][newy] = get_next_element(element);
8746     Store[newx][newy] = Store[x][y];
8747   }
8748   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8749   {
8750     Tile[x][y] = get_next_element(element);
8751     element = Tile[newx][newy] = Store[x][y];
8752   }
8753   else if (element == EL_MAGIC_WALL_FILLING)
8754   {
8755     element = Tile[newx][newy] = get_next_element(element);
8756     if (!game.magic_wall_active)
8757       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8758     Store[newx][newy] = Store[x][y];
8759   }
8760   else if (element == EL_MAGIC_WALL_EMPTYING)
8761   {
8762     Tile[x][y] = get_next_element(element);
8763     if (!game.magic_wall_active)
8764       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8765     element = Tile[newx][newy] = Store[x][y];
8766
8767     InitField(newx, newy, FALSE);
8768   }
8769   else if (element == EL_BD_MAGIC_WALL_FILLING)
8770   {
8771     element = Tile[newx][newy] = get_next_element(element);
8772     if (!game.magic_wall_active)
8773       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8774     Store[newx][newy] = Store[x][y];
8775   }
8776   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8777   {
8778     Tile[x][y] = get_next_element(element);
8779     if (!game.magic_wall_active)
8780       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8781     element = Tile[newx][newy] = Store[x][y];
8782
8783     InitField(newx, newy, FALSE);
8784   }
8785   else if (element == EL_DC_MAGIC_WALL_FILLING)
8786   {
8787     element = Tile[newx][newy] = get_next_element(element);
8788     if (!game.magic_wall_active)
8789       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8790     Store[newx][newy] = Store[x][y];
8791   }
8792   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8793   {
8794     Tile[x][y] = get_next_element(element);
8795     if (!game.magic_wall_active)
8796       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8797     element = Tile[newx][newy] = Store[x][y];
8798
8799     InitField(newx, newy, FALSE);
8800   }
8801   else if (element == EL_AMOEBA_DROPPING)
8802   {
8803     Tile[x][y] = get_next_element(element);
8804     element = Tile[newx][newy] = Store[x][y];
8805   }
8806   else if (element == EL_SOKOBAN_OBJECT)
8807   {
8808     if (Back[x][y])
8809       Tile[x][y] = Back[x][y];
8810
8811     if (Back[newx][newy])
8812       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8813
8814     Back[x][y] = Back[newx][newy] = 0;
8815   }
8816
8817   Store[x][y] = EL_EMPTY;
8818   MovPos[x][y] = 0;
8819   MovDir[x][y] = 0;
8820   MovDelay[x][y] = 0;
8821
8822   MovDelay[newx][newy] = 0;
8823
8824   if (CAN_CHANGE_OR_HAS_ACTION(element))
8825   {
8826     // copy element change control values to new field
8827     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8828     ChangePage[newx][newy]  = ChangePage[x][y];
8829     ChangeCount[newx][newy] = ChangeCount[x][y];
8830     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8831   }
8832
8833   CustomValue[newx][newy] = CustomValue[x][y];
8834
8835   ChangeDelay[x][y] = 0;
8836   ChangePage[x][y] = -1;
8837   ChangeCount[x][y] = 0;
8838   ChangeEvent[x][y] = -1;
8839
8840   CustomValue[x][y] = 0;
8841
8842   // copy animation control values to new field
8843   GfxFrame[newx][newy]  = GfxFrame[x][y];
8844   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8845   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8846   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8847
8848   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8849
8850   // some elements can leave other elements behind after moving
8851   if (ei->move_leave_element != EL_EMPTY &&
8852       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8853       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8854   {
8855     int move_leave_element = ei->move_leave_element;
8856
8857     // this makes it possible to leave the removed element again
8858     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8859       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8860
8861     Tile[x][y] = move_leave_element;
8862
8863     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8864       MovDir[x][y] = direction;
8865
8866     InitField(x, y, FALSE);
8867
8868     if (GFX_CRUMBLED(Tile[x][y]))
8869       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8870
8871     if (IS_PLAYER_ELEMENT(move_leave_element))
8872       RelocatePlayer(x, y, move_leave_element);
8873   }
8874
8875   // do this after checking for left-behind element
8876   ResetGfxAnimation(x, y);      // reset animation values for old field
8877
8878   if (!CAN_MOVE(element) ||
8879       (CAN_FALL(element) && direction == MV_DOWN &&
8880        (element == EL_SPRING ||
8881         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8882         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8883     GfxDir[x][y] = MovDir[newx][newy] = 0;
8884
8885   TEST_DrawLevelField(x, y);
8886   TEST_DrawLevelField(newx, newy);
8887
8888   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8889
8890   // prevent pushed element from moving on in pushed direction
8891   if (pushed_by_player && CAN_MOVE(element) &&
8892       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8893       !(element_info[element].move_pattern & direction))
8894     TurnRound(newx, newy);
8895
8896   // prevent elements on conveyor belt from moving on in last direction
8897   if (pushed_by_conveyor && CAN_FALL(element) &&
8898       direction & MV_HORIZONTAL)
8899     MovDir[newx][newy] = 0;
8900
8901   if (!pushed_by_player)
8902   {
8903     int nextx = newx + dx, nexty = newy + dy;
8904     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8905
8906     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8907
8908     if (CAN_FALL(element) && direction == MV_DOWN)
8909       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8910
8911     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8912       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8913
8914     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8915       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8916   }
8917
8918   if (DONT_TOUCH(element))      // object may be nasty to player or others
8919   {
8920     TestIfBadThingTouchesPlayer(newx, newy);
8921     TestIfBadThingTouchesFriend(newx, newy);
8922
8923     if (!IS_CUSTOM_ELEMENT(element))
8924       TestIfBadThingTouchesOtherBadThing(newx, newy);
8925   }
8926   else if (element == EL_PENGUIN)
8927     TestIfFriendTouchesBadThing(newx, newy);
8928
8929   if (DONT_GET_HIT_BY(element))
8930   {
8931     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8932   }
8933
8934   // give the player one last chance (one more frame) to move away
8935   if (CAN_FALL(element) && direction == MV_DOWN &&
8936       (last_line || (!IS_FREE(x, newy + 1) &&
8937                      (!IS_PLAYER(x, newy + 1) ||
8938                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8939     Impact(x, newy);
8940
8941   if (pushed_by_player && !game.use_change_when_pushing_bug)
8942   {
8943     int push_side = MV_DIR_OPPOSITE(direction);
8944     struct PlayerInfo *player = PLAYERINFO(x, y);
8945
8946     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8947                                player->index_bit, push_side);
8948     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8949                                         player->index_bit, push_side);
8950   }
8951
8952   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8953     MovDelay[newx][newy] = 1;
8954
8955   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8956
8957   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8958   TestIfElementHitsCustomElement(newx, newy, direction);
8959   TestIfPlayerTouchesCustomElement(newx, newy);
8960   TestIfElementTouchesCustomElement(newx, newy);
8961
8962   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8963       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8964     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8965                              MV_DIR_OPPOSITE(direction));
8966 }
8967
8968 int AmoebaNeighbourNr(int ax, int ay)
8969 {
8970   int i;
8971   int element = Tile[ax][ay];
8972   int group_nr = 0;
8973   static int xy[4][2] =
8974   {
8975     { 0, -1 },
8976     { -1, 0 },
8977     { +1, 0 },
8978     { 0, +1 }
8979   };
8980
8981   for (i = 0; i < NUM_DIRECTIONS; i++)
8982   {
8983     int x = ax + xy[i][0];
8984     int y = ay + xy[i][1];
8985
8986     if (!IN_LEV_FIELD(x, y))
8987       continue;
8988
8989     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8990       group_nr = AmoebaNr[x][y];
8991   }
8992
8993   return group_nr;
8994 }
8995
8996 static void AmoebaMerge(int ax, int ay)
8997 {
8998   int i, x, y, xx, yy;
8999   int new_group_nr = AmoebaNr[ax][ay];
9000   static int xy[4][2] =
9001   {
9002     { 0, -1 },
9003     { -1, 0 },
9004     { +1, 0 },
9005     { 0, +1 }
9006   };
9007
9008   if (new_group_nr == 0)
9009     return;
9010
9011   for (i = 0; i < NUM_DIRECTIONS; i++)
9012   {
9013     x = ax + xy[i][0];
9014     y = ay + xy[i][1];
9015
9016     if (!IN_LEV_FIELD(x, y))
9017       continue;
9018
9019     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9020          Tile[x][y] == EL_BD_AMOEBA ||
9021          Tile[x][y] == EL_AMOEBA_DEAD) &&
9022         AmoebaNr[x][y] != new_group_nr)
9023     {
9024       int old_group_nr = AmoebaNr[x][y];
9025
9026       if (old_group_nr == 0)
9027         return;
9028
9029       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9030       AmoebaCnt[old_group_nr] = 0;
9031       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9032       AmoebaCnt2[old_group_nr] = 0;
9033
9034       SCAN_PLAYFIELD(xx, yy)
9035       {
9036         if (AmoebaNr[xx][yy] == old_group_nr)
9037           AmoebaNr[xx][yy] = new_group_nr;
9038       }
9039     }
9040   }
9041 }
9042
9043 void AmoebaToDiamond(int ax, int ay)
9044 {
9045   int i, x, y;
9046
9047   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9048   {
9049     int group_nr = AmoebaNr[ax][ay];
9050
9051 #ifdef DEBUG
9052     if (group_nr == 0)
9053     {
9054       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9055       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9056
9057       return;
9058     }
9059 #endif
9060
9061     SCAN_PLAYFIELD(x, y)
9062     {
9063       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9064       {
9065         AmoebaNr[x][y] = 0;
9066         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9067       }
9068     }
9069
9070     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9071                             SND_AMOEBA_TURNING_TO_GEM :
9072                             SND_AMOEBA_TURNING_TO_ROCK));
9073     Bang(ax, ay);
9074   }
9075   else
9076   {
9077     static int xy[4][2] =
9078     {
9079       { 0, -1 },
9080       { -1, 0 },
9081       { +1, 0 },
9082       { 0, +1 }
9083     };
9084
9085     for (i = 0; i < NUM_DIRECTIONS; i++)
9086     {
9087       x = ax + xy[i][0];
9088       y = ay + xy[i][1];
9089
9090       if (!IN_LEV_FIELD(x, y))
9091         continue;
9092
9093       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9094       {
9095         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9096                               SND_AMOEBA_TURNING_TO_GEM :
9097                               SND_AMOEBA_TURNING_TO_ROCK));
9098         Bang(x, y);
9099       }
9100     }
9101   }
9102 }
9103
9104 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9105 {
9106   int x, y;
9107   int group_nr = AmoebaNr[ax][ay];
9108   boolean done = FALSE;
9109
9110 #ifdef DEBUG
9111   if (group_nr == 0)
9112   {
9113     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9114     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9115
9116     return;
9117   }
9118 #endif
9119
9120   SCAN_PLAYFIELD(x, y)
9121   {
9122     if (AmoebaNr[x][y] == group_nr &&
9123         (Tile[x][y] == EL_AMOEBA_DEAD ||
9124          Tile[x][y] == EL_BD_AMOEBA ||
9125          Tile[x][y] == EL_AMOEBA_GROWING))
9126     {
9127       AmoebaNr[x][y] = 0;
9128       Tile[x][y] = new_element;
9129       InitField(x, y, FALSE);
9130       TEST_DrawLevelField(x, y);
9131       done = TRUE;
9132     }
9133   }
9134
9135   if (done)
9136     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9137                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9138                             SND_BD_AMOEBA_TURNING_TO_GEM));
9139 }
9140
9141 static void AmoebaGrowing(int x, int y)
9142 {
9143   static DelayCounter sound_delay = { 0 };
9144
9145   if (!MovDelay[x][y])          // start new growing cycle
9146   {
9147     MovDelay[x][y] = 7;
9148
9149     if (DelayReached(&sound_delay))
9150     {
9151       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9152       sound_delay.value = 30;
9153     }
9154   }
9155
9156   if (MovDelay[x][y])           // wait some time before growing bigger
9157   {
9158     MovDelay[x][y]--;
9159     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9160     {
9161       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9162                                            6 - MovDelay[x][y]);
9163
9164       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9165     }
9166
9167     if (!MovDelay[x][y])
9168     {
9169       Tile[x][y] = Store[x][y];
9170       Store[x][y] = 0;
9171       TEST_DrawLevelField(x, y);
9172     }
9173   }
9174 }
9175
9176 static void AmoebaShrinking(int x, int y)
9177 {
9178   static DelayCounter sound_delay = { 0 };
9179
9180   if (!MovDelay[x][y])          // start new shrinking cycle
9181   {
9182     MovDelay[x][y] = 7;
9183
9184     if (DelayReached(&sound_delay))
9185       sound_delay.value = 30;
9186   }
9187
9188   if (MovDelay[x][y])           // wait some time before shrinking
9189   {
9190     MovDelay[x][y]--;
9191     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9192     {
9193       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9194                                            6 - MovDelay[x][y]);
9195
9196       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9197     }
9198
9199     if (!MovDelay[x][y])
9200     {
9201       Tile[x][y] = EL_EMPTY;
9202       TEST_DrawLevelField(x, y);
9203
9204       // don't let mole enter this field in this cycle;
9205       // (give priority to objects falling to this field from above)
9206       Stop[x][y] = TRUE;
9207     }
9208   }
9209 }
9210
9211 static void AmoebaReproduce(int ax, int ay)
9212 {
9213   int i;
9214   int element = Tile[ax][ay];
9215   int graphic = el2img(element);
9216   int newax = ax, neway = ay;
9217   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9218   static int xy[4][2] =
9219   {
9220     { 0, -1 },
9221     { -1, 0 },
9222     { +1, 0 },
9223     { 0, +1 }
9224   };
9225
9226   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9227   {
9228     Tile[ax][ay] = EL_AMOEBA_DEAD;
9229     TEST_DrawLevelField(ax, ay);
9230     return;
9231   }
9232
9233   if (IS_ANIMATED(graphic))
9234     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9235
9236   if (!MovDelay[ax][ay])        // start making new amoeba field
9237     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9238
9239   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9240   {
9241     MovDelay[ax][ay]--;
9242     if (MovDelay[ax][ay])
9243       return;
9244   }
9245
9246   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9247   {
9248     int start = RND(4);
9249     int x = ax + xy[start][0];
9250     int y = ay + xy[start][1];
9251
9252     if (!IN_LEV_FIELD(x, y))
9253       return;
9254
9255     if (IS_FREE(x, y) ||
9256         CAN_GROW_INTO(Tile[x][y]) ||
9257         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9258         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9259     {
9260       newax = x;
9261       neway = y;
9262     }
9263
9264     if (newax == ax && neway == ay)
9265       return;
9266   }
9267   else                          // normal or "filled" (BD style) amoeba
9268   {
9269     int start = RND(4);
9270     boolean waiting_for_player = FALSE;
9271
9272     for (i = 0; i < NUM_DIRECTIONS; i++)
9273     {
9274       int j = (start + i) % 4;
9275       int x = ax + xy[j][0];
9276       int y = ay + xy[j][1];
9277
9278       if (!IN_LEV_FIELD(x, y))
9279         continue;
9280
9281       if (IS_FREE(x, y) ||
9282           CAN_GROW_INTO(Tile[x][y]) ||
9283           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9284           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9285       {
9286         newax = x;
9287         neway = y;
9288         break;
9289       }
9290       else if (IS_PLAYER(x, y))
9291         waiting_for_player = TRUE;
9292     }
9293
9294     if (newax == ax && neway == ay)             // amoeba cannot grow
9295     {
9296       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9297       {
9298         Tile[ax][ay] = EL_AMOEBA_DEAD;
9299         TEST_DrawLevelField(ax, ay);
9300         AmoebaCnt[AmoebaNr[ax][ay]]--;
9301
9302         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9303         {
9304           if (element == EL_AMOEBA_FULL)
9305             AmoebaToDiamond(ax, ay);
9306           else if (element == EL_BD_AMOEBA)
9307             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9308         }
9309       }
9310       return;
9311     }
9312     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9313     {
9314       // amoeba gets larger by growing in some direction
9315
9316       int new_group_nr = AmoebaNr[ax][ay];
9317
9318 #ifdef DEBUG
9319   if (new_group_nr == 0)
9320   {
9321     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9322           newax, neway);
9323     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9324
9325     return;
9326   }
9327 #endif
9328
9329       AmoebaNr[newax][neway] = new_group_nr;
9330       AmoebaCnt[new_group_nr]++;
9331       AmoebaCnt2[new_group_nr]++;
9332
9333       // if amoeba touches other amoeba(s) after growing, unify them
9334       AmoebaMerge(newax, neway);
9335
9336       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9337       {
9338         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9339         return;
9340       }
9341     }
9342   }
9343
9344   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9345       (neway == lev_fieldy - 1 && newax != ax))
9346   {
9347     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9348     Store[newax][neway] = element;
9349   }
9350   else if (neway == ay || element == EL_EMC_DRIPPER)
9351   {
9352     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9353
9354     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9355   }
9356   else
9357   {
9358     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9359     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9360     Store[ax][ay] = EL_AMOEBA_DROP;
9361     ContinueMoving(ax, ay);
9362     return;
9363   }
9364
9365   TEST_DrawLevelField(newax, neway);
9366 }
9367
9368 static void Life(int ax, int ay)
9369 {
9370   int x1, y1, x2, y2;
9371   int life_time = 40;
9372   int element = Tile[ax][ay];
9373   int graphic = el2img(element);
9374   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9375                          level.biomaze);
9376   boolean changed = FALSE;
9377
9378   if (IS_ANIMATED(graphic))
9379     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9380
9381   if (Stop[ax][ay])
9382     return;
9383
9384   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9385     MovDelay[ax][ay] = life_time;
9386
9387   if (MovDelay[ax][ay])         // wait some time before next cycle
9388   {
9389     MovDelay[ax][ay]--;
9390     if (MovDelay[ax][ay])
9391       return;
9392   }
9393
9394   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9395   {
9396     int xx = ax+x1, yy = ay+y1;
9397     int old_element = Tile[xx][yy];
9398     int num_neighbours = 0;
9399
9400     if (!IN_LEV_FIELD(xx, yy))
9401       continue;
9402
9403     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9404     {
9405       int x = xx+x2, y = yy+y2;
9406
9407       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9408         continue;
9409
9410       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9411       boolean is_neighbour = FALSE;
9412
9413       if (level.use_life_bugs)
9414         is_neighbour =
9415           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9416            (IS_FREE(x, y)                             &&  Stop[x][y]));
9417       else
9418         is_neighbour =
9419           (Last[x][y] == element || is_player_cell);
9420
9421       if (is_neighbour)
9422         num_neighbours++;
9423     }
9424
9425     boolean is_free = FALSE;
9426
9427     if (level.use_life_bugs)
9428       is_free = (IS_FREE(xx, yy));
9429     else
9430       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9431
9432     if (xx == ax && yy == ay)           // field in the middle
9433     {
9434       if (num_neighbours < life_parameter[0] ||
9435           num_neighbours > life_parameter[1])
9436       {
9437         Tile[xx][yy] = EL_EMPTY;
9438         if (Tile[xx][yy] != old_element)
9439           TEST_DrawLevelField(xx, yy);
9440         Stop[xx][yy] = TRUE;
9441         changed = TRUE;
9442       }
9443     }
9444     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9445     {                                   // free border field
9446       if (num_neighbours >= life_parameter[2] &&
9447           num_neighbours <= life_parameter[3])
9448       {
9449         Tile[xx][yy] = element;
9450         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9451         if (Tile[xx][yy] != old_element)
9452           TEST_DrawLevelField(xx, yy);
9453         Stop[xx][yy] = TRUE;
9454         changed = TRUE;
9455       }
9456     }
9457   }
9458
9459   if (changed)
9460     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9461                    SND_GAME_OF_LIFE_GROWING);
9462 }
9463
9464 static void InitRobotWheel(int x, int y)
9465 {
9466   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9467 }
9468
9469 static void RunRobotWheel(int x, int y)
9470 {
9471   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9472 }
9473
9474 static void StopRobotWheel(int x, int y)
9475 {
9476   if (game.robot_wheel_x == x &&
9477       game.robot_wheel_y == y)
9478   {
9479     game.robot_wheel_x = -1;
9480     game.robot_wheel_y = -1;
9481     game.robot_wheel_active = FALSE;
9482   }
9483 }
9484
9485 static void InitTimegateWheel(int x, int y)
9486 {
9487   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9488 }
9489
9490 static void RunTimegateWheel(int x, int y)
9491 {
9492   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9493 }
9494
9495 static void InitMagicBallDelay(int x, int y)
9496 {
9497   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9498 }
9499
9500 static void ActivateMagicBall(int bx, int by)
9501 {
9502   int x, y;
9503
9504   if (level.ball_random)
9505   {
9506     int pos_border = RND(8);    // select one of the eight border elements
9507     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9508     int xx = pos_content % 3;
9509     int yy = pos_content / 3;
9510
9511     x = bx - 1 + xx;
9512     y = by - 1 + yy;
9513
9514     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9515       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9516   }
9517   else
9518   {
9519     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9520     {
9521       int xx = x - bx + 1;
9522       int yy = y - by + 1;
9523
9524       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9525         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9526     }
9527   }
9528
9529   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9530 }
9531
9532 static void CheckExit(int x, int y)
9533 {
9534   if (game.gems_still_needed > 0 ||
9535       game.sokoban_fields_still_needed > 0 ||
9536       game.sokoban_objects_still_needed > 0 ||
9537       game.lights_still_needed > 0)
9538   {
9539     int element = Tile[x][y];
9540     int graphic = el2img(element);
9541
9542     if (IS_ANIMATED(graphic))
9543       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9544
9545     return;
9546   }
9547
9548   // do not re-open exit door closed after last player
9549   if (game.all_players_gone)
9550     return;
9551
9552   Tile[x][y] = EL_EXIT_OPENING;
9553
9554   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9555 }
9556
9557 static void CheckExitEM(int x, int y)
9558 {
9559   if (game.gems_still_needed > 0 ||
9560       game.sokoban_fields_still_needed > 0 ||
9561       game.sokoban_objects_still_needed > 0 ||
9562       game.lights_still_needed > 0)
9563   {
9564     int element = Tile[x][y];
9565     int graphic = el2img(element);
9566
9567     if (IS_ANIMATED(graphic))
9568       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9569
9570     return;
9571   }
9572
9573   // do not re-open exit door closed after last player
9574   if (game.all_players_gone)
9575     return;
9576
9577   Tile[x][y] = EL_EM_EXIT_OPENING;
9578
9579   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9580 }
9581
9582 static void CheckExitSteel(int x, int y)
9583 {
9584   if (game.gems_still_needed > 0 ||
9585       game.sokoban_fields_still_needed > 0 ||
9586       game.sokoban_objects_still_needed > 0 ||
9587       game.lights_still_needed > 0)
9588   {
9589     int element = Tile[x][y];
9590     int graphic = el2img(element);
9591
9592     if (IS_ANIMATED(graphic))
9593       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9594
9595     return;
9596   }
9597
9598   // do not re-open exit door closed after last player
9599   if (game.all_players_gone)
9600     return;
9601
9602   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9603
9604   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9605 }
9606
9607 static void CheckExitSteelEM(int x, int y)
9608 {
9609   if (game.gems_still_needed > 0 ||
9610       game.sokoban_fields_still_needed > 0 ||
9611       game.sokoban_objects_still_needed > 0 ||
9612       game.lights_still_needed > 0)
9613   {
9614     int element = Tile[x][y];
9615     int graphic = el2img(element);
9616
9617     if (IS_ANIMATED(graphic))
9618       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9619
9620     return;
9621   }
9622
9623   // do not re-open exit door closed after last player
9624   if (game.all_players_gone)
9625     return;
9626
9627   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9628
9629   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9630 }
9631
9632 static void CheckExitSP(int x, int y)
9633 {
9634   if (game.gems_still_needed > 0)
9635   {
9636     int element = Tile[x][y];
9637     int graphic = el2img(element);
9638
9639     if (IS_ANIMATED(graphic))
9640       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9641
9642     return;
9643   }
9644
9645   // do not re-open exit door closed after last player
9646   if (game.all_players_gone)
9647     return;
9648
9649   Tile[x][y] = EL_SP_EXIT_OPENING;
9650
9651   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9652 }
9653
9654 static void CloseAllOpenTimegates(void)
9655 {
9656   int x, y;
9657
9658   SCAN_PLAYFIELD(x, y)
9659   {
9660     int element = Tile[x][y];
9661
9662     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9663     {
9664       Tile[x][y] = EL_TIMEGATE_CLOSING;
9665
9666       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9667     }
9668   }
9669 }
9670
9671 static void DrawTwinkleOnField(int x, int y)
9672 {
9673   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9674     return;
9675
9676   if (Tile[x][y] == EL_BD_DIAMOND)
9677     return;
9678
9679   if (MovDelay[x][y] == 0)      // next animation frame
9680     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9681
9682   if (MovDelay[x][y] != 0)      // wait some time before next frame
9683   {
9684     MovDelay[x][y]--;
9685
9686     DrawLevelElementAnimation(x, y, Tile[x][y]);
9687
9688     if (MovDelay[x][y] != 0)
9689     {
9690       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9691                                            10 - MovDelay[x][y]);
9692
9693       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9694     }
9695   }
9696 }
9697
9698 static void MauerWaechst(int x, int y)
9699 {
9700   int delay = 6;
9701
9702   if (!MovDelay[x][y])          // next animation frame
9703     MovDelay[x][y] = 3 * delay;
9704
9705   if (MovDelay[x][y])           // wait some time before next frame
9706   {
9707     MovDelay[x][y]--;
9708
9709     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9710     {
9711       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9712       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9713
9714       DrawLevelGraphic(x, y, graphic, frame);
9715     }
9716
9717     if (!MovDelay[x][y])
9718     {
9719       if (MovDir[x][y] == MV_LEFT)
9720       {
9721         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9722           TEST_DrawLevelField(x - 1, y);
9723       }
9724       else if (MovDir[x][y] == MV_RIGHT)
9725       {
9726         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9727           TEST_DrawLevelField(x + 1, y);
9728       }
9729       else if (MovDir[x][y] == MV_UP)
9730       {
9731         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9732           TEST_DrawLevelField(x, y - 1);
9733       }
9734       else
9735       {
9736         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9737           TEST_DrawLevelField(x, y + 1);
9738       }
9739
9740       Tile[x][y] = Store[x][y];
9741       Store[x][y] = 0;
9742       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9743       TEST_DrawLevelField(x, y);
9744     }
9745   }
9746 }
9747
9748 static void MauerAbleger(int ax, int ay)
9749 {
9750   int element = Tile[ax][ay];
9751   int graphic = el2img(element);
9752   boolean oben_frei = FALSE, unten_frei = FALSE;
9753   boolean links_frei = FALSE, rechts_frei = FALSE;
9754   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9755   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9756   boolean new_wall = FALSE;
9757
9758   if (IS_ANIMATED(graphic))
9759     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9760
9761   if (!MovDelay[ax][ay])        // start building new wall
9762     MovDelay[ax][ay] = 6;
9763
9764   if (MovDelay[ax][ay])         // wait some time before building new wall
9765   {
9766     MovDelay[ax][ay]--;
9767     if (MovDelay[ax][ay])
9768       return;
9769   }
9770
9771   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9772     oben_frei = TRUE;
9773   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9774     unten_frei = TRUE;
9775   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9776     links_frei = TRUE;
9777   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9778     rechts_frei = TRUE;
9779
9780   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9781       element == EL_EXPANDABLE_WALL_ANY)
9782   {
9783     if (oben_frei)
9784     {
9785       Tile[ax][ay - 1] = EL_EXPANDABLE_WALL_GROWING;
9786       Store[ax][ay - 1] = element;
9787       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9788       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9789         DrawLevelGraphic(ax, ay - 1, IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9790       new_wall = TRUE;
9791     }
9792     if (unten_frei)
9793     {
9794       Tile[ax][ay + 1] = EL_EXPANDABLE_WALL_GROWING;
9795       Store[ax][ay + 1] = element;
9796       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9797       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9798         DrawLevelGraphic(ax, ay + 1, IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9799       new_wall = TRUE;
9800     }
9801   }
9802
9803   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9804       element == EL_EXPANDABLE_WALL_ANY ||
9805       element == EL_EXPANDABLE_WALL ||
9806       element == EL_BD_EXPANDABLE_WALL)
9807   {
9808     if (links_frei)
9809     {
9810       Tile[ax - 1][ay] = EL_EXPANDABLE_WALL_GROWING;
9811       Store[ax - 1][ay] = element;
9812       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9813       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9814         DrawLevelGraphic(ax - 1, ay, IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9815       new_wall = TRUE;
9816     }
9817
9818     if (rechts_frei)
9819     {
9820       Tile[ax + 1][ay] = EL_EXPANDABLE_WALL_GROWING;
9821       Store[ax + 1][ay] = element;
9822       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9823       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9824         DrawLevelGraphic(ax + 1, ay, IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9825       new_wall = TRUE;
9826     }
9827   }
9828
9829   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9830     TEST_DrawLevelField(ax, ay);
9831
9832   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9833     oben_massiv = TRUE;
9834   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9835     unten_massiv = TRUE;
9836   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9837     links_massiv = TRUE;
9838   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9839     rechts_massiv = TRUE;
9840
9841   if (((oben_massiv && unten_massiv) ||
9842        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9843        element == EL_EXPANDABLE_WALL) &&
9844       ((links_massiv && rechts_massiv) ||
9845        element == EL_EXPANDABLE_WALL_VERTICAL))
9846     Tile[ax][ay] = EL_WALL;
9847
9848   if (new_wall)
9849     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9850 }
9851
9852 static void MauerAblegerStahl(int ax, int ay)
9853 {
9854   int element = Tile[ax][ay];
9855   int graphic = el2img(element);
9856   boolean oben_frei = FALSE, unten_frei = FALSE;
9857   boolean links_frei = FALSE, rechts_frei = FALSE;
9858   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9859   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9860   boolean new_wall = FALSE;
9861
9862   if (IS_ANIMATED(graphic))
9863     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9864
9865   if (!MovDelay[ax][ay])        // start building new wall
9866     MovDelay[ax][ay] = 6;
9867
9868   if (MovDelay[ax][ay])         // wait some time before building new wall
9869   {
9870     MovDelay[ax][ay]--;
9871     if (MovDelay[ax][ay])
9872       return;
9873   }
9874
9875   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9876     oben_frei = TRUE;
9877   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9878     unten_frei = TRUE;
9879   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9880     links_frei = TRUE;
9881   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9882     rechts_frei = TRUE;
9883
9884   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9885       element == EL_EXPANDABLE_STEELWALL_ANY)
9886   {
9887     if (oben_frei)
9888     {
9889       Tile[ax][ay - 1] = EL_EXPANDABLE_STEELWALL_GROWING;
9890       Store[ax][ay - 1] = element;
9891       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9892       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9893         DrawLevelGraphic(ax, ay - 1, IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9894       new_wall = TRUE;
9895     }
9896     if (unten_frei)
9897     {
9898       Tile[ax][ay + 1] = EL_EXPANDABLE_STEELWALL_GROWING;
9899       Store[ax][ay + 1] = element;
9900       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9901       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9902         DrawLevelGraphic(ax, ay + 1, IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9903       new_wall = TRUE;
9904     }
9905   }
9906
9907   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9908       element == EL_EXPANDABLE_STEELWALL_ANY)
9909   {
9910     if (links_frei)
9911     {
9912       Tile[ax - 1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9913       Store[ax - 1][ay] = element;
9914       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9915       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9916         DrawLevelGraphic(ax - 1, ay, IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9917       new_wall = TRUE;
9918     }
9919
9920     if (rechts_frei)
9921     {
9922       Tile[ax + 1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9923       Store[ax + 1][ay] = element;
9924       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9925       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9926         DrawLevelGraphic(ax + 1, ay, IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9927       new_wall = TRUE;
9928     }
9929   }
9930
9931   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9932     oben_massiv = TRUE;
9933   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9934     unten_massiv = TRUE;
9935   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9936     links_massiv = TRUE;
9937   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9938     rechts_massiv = TRUE;
9939
9940   if (((oben_massiv && unten_massiv) ||
9941        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9942       ((links_massiv && rechts_massiv) ||
9943        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9944     Tile[ax][ay] = EL_STEELWALL;
9945
9946   if (new_wall)
9947     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9948 }
9949
9950 static void CheckForDragon(int x, int y)
9951 {
9952   int i, j;
9953   boolean dragon_found = FALSE;
9954   static int xy[4][2] =
9955   {
9956     { 0, -1 },
9957     { -1, 0 },
9958     { +1, 0 },
9959     { 0, +1 }
9960   };
9961
9962   for (i = 0; i < NUM_DIRECTIONS; i++)
9963   {
9964     for (j = 0; j < 4; j++)
9965     {
9966       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9967
9968       if (IN_LEV_FIELD(xx, yy) &&
9969           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9970       {
9971         if (Tile[xx][yy] == EL_DRAGON)
9972           dragon_found = TRUE;
9973       }
9974       else
9975         break;
9976     }
9977   }
9978
9979   if (!dragon_found)
9980   {
9981     for (i = 0; i < NUM_DIRECTIONS; i++)
9982     {
9983       for (j = 0; j < 3; j++)
9984       {
9985         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9986   
9987         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9988         {
9989           Tile[xx][yy] = EL_EMPTY;
9990           TEST_DrawLevelField(xx, yy);
9991         }
9992         else
9993           break;
9994       }
9995     }
9996   }
9997 }
9998
9999 static void InitBuggyBase(int x, int y)
10000 {
10001   int element = Tile[x][y];
10002   int activating_delay = FRAMES_PER_SECOND / 4;
10003
10004   ChangeDelay[x][y] =
10005     (element == EL_SP_BUGGY_BASE ?
10006      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10007      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10008      activating_delay :
10009      element == EL_SP_BUGGY_BASE_ACTIVE ?
10010      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10011 }
10012
10013 static void WarnBuggyBase(int x, int y)
10014 {
10015   int i;
10016   static int xy[4][2] =
10017   {
10018     { 0, -1 },
10019     { -1, 0 },
10020     { +1, 0 },
10021     { 0, +1 }
10022   };
10023
10024   for (i = 0; i < NUM_DIRECTIONS; i++)
10025   {
10026     int xx = x + xy[i][0];
10027     int yy = y + xy[i][1];
10028
10029     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10030     {
10031       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10032
10033       break;
10034     }
10035   }
10036 }
10037
10038 static void InitTrap(int x, int y)
10039 {
10040   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10041 }
10042
10043 static void ActivateTrap(int x, int y)
10044 {
10045   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10046 }
10047
10048 static void ChangeActiveTrap(int x, int y)
10049 {
10050   int graphic = IMG_TRAP_ACTIVE;
10051
10052   // if new animation frame was drawn, correct crumbled sand border
10053   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10054     TEST_DrawLevelFieldCrumbled(x, y);
10055 }
10056
10057 static int getSpecialActionElement(int element, int number, int base_element)
10058 {
10059   return (element != EL_EMPTY ? element :
10060           number != -1 ? base_element + number - 1 :
10061           EL_EMPTY);
10062 }
10063
10064 static int getModifiedActionNumber(int value_old, int operator, int operand,
10065                                    int value_min, int value_max)
10066 {
10067   int value_new = (operator == CA_MODE_SET      ? operand :
10068                    operator == CA_MODE_ADD      ? value_old + operand :
10069                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10070                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10071                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10072                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10073                    value_old);
10074
10075   return (value_new < value_min ? value_min :
10076           value_new > value_max ? value_max :
10077           value_new);
10078 }
10079
10080 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10081 {
10082   struct ElementInfo *ei = &element_info[element];
10083   struct ElementChangeInfo *change = &ei->change_page[page];
10084   int target_element = change->target_element;
10085   int action_type = change->action_type;
10086   int action_mode = change->action_mode;
10087   int action_arg = change->action_arg;
10088   int action_element = change->action_element;
10089   int i;
10090
10091   if (!change->has_action)
10092     return;
10093
10094   // ---------- determine action paramater values -----------------------------
10095
10096   int level_time_value =
10097     (level.time > 0 ? TimeLeft :
10098      TimePlayed);
10099
10100   int action_arg_element_raw =
10101     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10102      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10103      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10104      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10105      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10106      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10107      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10108      EL_EMPTY);
10109   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10110
10111   int action_arg_direction =
10112     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10113      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10114      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10115      change->actual_trigger_side :
10116      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10117      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10118      MV_NONE);
10119
10120   int action_arg_number_min =
10121     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10122      CA_ARG_MIN);
10123
10124   int action_arg_number_max =
10125     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10126      action_type == CA_SET_LEVEL_GEMS ? 999 :
10127      action_type == CA_SET_LEVEL_TIME ? 9999 :
10128      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10129      action_type == CA_SET_CE_VALUE ? 9999 :
10130      action_type == CA_SET_CE_SCORE ? 9999 :
10131      CA_ARG_MAX);
10132
10133   int action_arg_number_reset =
10134     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10135      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10136      action_type == CA_SET_LEVEL_TIME ? level.time :
10137      action_type == CA_SET_LEVEL_SCORE ? 0 :
10138      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10139      action_type == CA_SET_CE_SCORE ? 0 :
10140      0);
10141
10142   int action_arg_number =
10143     (action_arg <= CA_ARG_MAX ? action_arg :
10144      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10145      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10146      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10147      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10148      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10149      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10150      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10151      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10152      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10153      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10154      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10155      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10156      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10157      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10158      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10159      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10160      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10161      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10162      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10163      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10164      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10165      -1);
10166
10167   int action_arg_number_old =
10168     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10169      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10170      action_type == CA_SET_LEVEL_SCORE ? game.score :
10171      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10172      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10173      0);
10174
10175   int action_arg_number_new =
10176     getModifiedActionNumber(action_arg_number_old,
10177                             action_mode, action_arg_number,
10178                             action_arg_number_min, action_arg_number_max);
10179
10180   int trigger_player_bits =
10181     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10182      change->actual_trigger_player_bits : change->trigger_player);
10183
10184   int action_arg_player_bits =
10185     (action_arg >= CA_ARG_PLAYER_1 &&
10186      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10187      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10188      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10189      PLAYER_BITS_ANY);
10190
10191   // ---------- execute action  -----------------------------------------------
10192
10193   switch (action_type)
10194   {
10195     case CA_NO_ACTION:
10196     {
10197       return;
10198     }
10199
10200     // ---------- level actions  ----------------------------------------------
10201
10202     case CA_RESTART_LEVEL:
10203     {
10204       game.restart_level = TRUE;
10205
10206       break;
10207     }
10208
10209     case CA_SHOW_ENVELOPE:
10210     {
10211       int element = getSpecialActionElement(action_arg_element,
10212                                             action_arg_number, EL_ENVELOPE_1);
10213
10214       if (IS_ENVELOPE(element))
10215         local_player->show_envelope = element;
10216
10217       break;
10218     }
10219
10220     case CA_SET_LEVEL_TIME:
10221     {
10222       if (level.time > 0)       // only modify limited time value
10223       {
10224         TimeLeft = action_arg_number_new;
10225
10226         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10227
10228         DisplayGameControlValues();
10229
10230         if (!TimeLeft && game.time_limit)
10231           for (i = 0; i < MAX_PLAYERS; i++)
10232             KillPlayer(&stored_player[i]);
10233       }
10234
10235       break;
10236     }
10237
10238     case CA_SET_LEVEL_SCORE:
10239     {
10240       game.score = action_arg_number_new;
10241
10242       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10243
10244       DisplayGameControlValues();
10245
10246       break;
10247     }
10248
10249     case CA_SET_LEVEL_GEMS:
10250     {
10251       game.gems_still_needed = action_arg_number_new;
10252
10253       game.snapshot.collected_item = TRUE;
10254
10255       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10256
10257       DisplayGameControlValues();
10258
10259       break;
10260     }
10261
10262     case CA_SET_LEVEL_WIND:
10263     {
10264       game.wind_direction = action_arg_direction;
10265
10266       break;
10267     }
10268
10269     case CA_SET_LEVEL_RANDOM_SEED:
10270     {
10271       // ensure that setting a new random seed while playing is predictable
10272       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10273
10274       break;
10275     }
10276
10277     // ---------- player actions  ---------------------------------------------
10278
10279     case CA_MOVE_PLAYER:
10280     case CA_MOVE_PLAYER_NEW:
10281     {
10282       // automatically move to the next field in specified direction
10283       for (i = 0; i < MAX_PLAYERS; i++)
10284         if (trigger_player_bits & (1 << i))
10285           if (action_type == CA_MOVE_PLAYER ||
10286               stored_player[i].MovPos == 0)
10287             stored_player[i].programmed_action = action_arg_direction;
10288
10289       break;
10290     }
10291
10292     case CA_EXIT_PLAYER:
10293     {
10294       for (i = 0; i < MAX_PLAYERS; i++)
10295         if (action_arg_player_bits & (1 << i))
10296           ExitPlayer(&stored_player[i]);
10297
10298       if (game.players_still_needed == 0)
10299         LevelSolved();
10300
10301       break;
10302     }
10303
10304     case CA_KILL_PLAYER:
10305     {
10306       for (i = 0; i < MAX_PLAYERS; i++)
10307         if (action_arg_player_bits & (1 << i))
10308           KillPlayer(&stored_player[i]);
10309
10310       break;
10311     }
10312
10313     case CA_SET_PLAYER_KEYS:
10314     {
10315       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10316       int element = getSpecialActionElement(action_arg_element,
10317                                             action_arg_number, EL_KEY_1);
10318
10319       if (IS_KEY(element))
10320       {
10321         for (i = 0; i < MAX_PLAYERS; i++)
10322         {
10323           if (trigger_player_bits & (1 << i))
10324           {
10325             stored_player[i].key[KEY_NR(element)] = key_state;
10326
10327             DrawGameDoorValues();
10328           }
10329         }
10330       }
10331
10332       break;
10333     }
10334
10335     case CA_SET_PLAYER_SPEED:
10336     {
10337       for (i = 0; i < MAX_PLAYERS; i++)
10338       {
10339         if (trigger_player_bits & (1 << i))
10340         {
10341           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10342
10343           if (action_arg == CA_ARG_SPEED_FASTER &&
10344               stored_player[i].cannot_move)
10345           {
10346             action_arg_number = STEPSIZE_VERY_SLOW;
10347           }
10348           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10349                    action_arg == CA_ARG_SPEED_FASTER)
10350           {
10351             action_arg_number = 2;
10352             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10353                            CA_MODE_MULTIPLY);
10354           }
10355           else if (action_arg == CA_ARG_NUMBER_RESET)
10356           {
10357             action_arg_number = level.initial_player_stepsize[i];
10358           }
10359
10360           move_stepsize =
10361             getModifiedActionNumber(move_stepsize,
10362                                     action_mode,
10363                                     action_arg_number,
10364                                     action_arg_number_min,
10365                                     action_arg_number_max);
10366
10367           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10368         }
10369       }
10370
10371       break;
10372     }
10373
10374     case CA_SET_PLAYER_SHIELD:
10375     {
10376       for (i = 0; i < MAX_PLAYERS; i++)
10377       {
10378         if (trigger_player_bits & (1 << i))
10379         {
10380           if (action_arg == CA_ARG_SHIELD_OFF)
10381           {
10382             stored_player[i].shield_normal_time_left = 0;
10383             stored_player[i].shield_deadly_time_left = 0;
10384           }
10385           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10386           {
10387             stored_player[i].shield_normal_time_left = 999999;
10388           }
10389           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10390           {
10391             stored_player[i].shield_normal_time_left = 999999;
10392             stored_player[i].shield_deadly_time_left = 999999;
10393           }
10394         }
10395       }
10396
10397       break;
10398     }
10399
10400     case CA_SET_PLAYER_GRAVITY:
10401     {
10402       for (i = 0; i < MAX_PLAYERS; i++)
10403       {
10404         if (trigger_player_bits & (1 << i))
10405         {
10406           stored_player[i].gravity =
10407             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10408              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10409              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10410              stored_player[i].gravity);
10411         }
10412       }
10413
10414       break;
10415     }
10416
10417     case CA_SET_PLAYER_ARTWORK:
10418     {
10419       for (i = 0; i < MAX_PLAYERS; i++)
10420       {
10421         if (trigger_player_bits & (1 << i))
10422         {
10423           int artwork_element = action_arg_element;
10424
10425           if (action_arg == CA_ARG_ELEMENT_RESET)
10426             artwork_element =
10427               (level.use_artwork_element[i] ? level.artwork_element[i] :
10428                stored_player[i].element_nr);
10429
10430           if (stored_player[i].artwork_element != artwork_element)
10431             stored_player[i].Frame = 0;
10432
10433           stored_player[i].artwork_element = artwork_element;
10434
10435           SetPlayerWaiting(&stored_player[i], FALSE);
10436
10437           // set number of special actions for bored and sleeping animation
10438           stored_player[i].num_special_action_bored =
10439             get_num_special_action(artwork_element,
10440                                    ACTION_BORING_1, ACTION_BORING_LAST);
10441           stored_player[i].num_special_action_sleeping =
10442             get_num_special_action(artwork_element,
10443                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10444         }
10445       }
10446
10447       break;
10448     }
10449
10450     case CA_SET_PLAYER_INVENTORY:
10451     {
10452       for (i = 0; i < MAX_PLAYERS; i++)
10453       {
10454         struct PlayerInfo *player = &stored_player[i];
10455         int j, k;
10456
10457         if (trigger_player_bits & (1 << i))
10458         {
10459           int inventory_element = action_arg_element;
10460
10461           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10462               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10463               action_arg == CA_ARG_ELEMENT_ACTION)
10464           {
10465             int element = inventory_element;
10466             int collect_count = element_info[element].collect_count_initial;
10467
10468             if (!IS_CUSTOM_ELEMENT(element))
10469               collect_count = 1;
10470
10471             if (collect_count == 0)
10472               player->inventory_infinite_element = element;
10473             else
10474               for (k = 0; k < collect_count; k++)
10475                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10476                   player->inventory_element[player->inventory_size++] =
10477                     element;
10478           }
10479           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10480                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10481                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10482           {
10483             if (player->inventory_infinite_element != EL_UNDEFINED &&
10484                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10485                                      action_arg_element_raw))
10486               player->inventory_infinite_element = EL_UNDEFINED;
10487
10488             for (k = 0, j = 0; j < player->inventory_size; j++)
10489             {
10490               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10491                                         action_arg_element_raw))
10492                 player->inventory_element[k++] = player->inventory_element[j];
10493             }
10494
10495             player->inventory_size = k;
10496           }
10497           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10498           {
10499             if (player->inventory_size > 0)
10500             {
10501               for (j = 0; j < player->inventory_size - 1; j++)
10502                 player->inventory_element[j] = player->inventory_element[j + 1];
10503
10504               player->inventory_size--;
10505             }
10506           }
10507           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10508           {
10509             if (player->inventory_size > 0)
10510               player->inventory_size--;
10511           }
10512           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10513           {
10514             player->inventory_infinite_element = EL_UNDEFINED;
10515             player->inventory_size = 0;
10516           }
10517           else if (action_arg == CA_ARG_INVENTORY_RESET)
10518           {
10519             player->inventory_infinite_element = EL_UNDEFINED;
10520             player->inventory_size = 0;
10521
10522             if (level.use_initial_inventory[i])
10523             {
10524               for (j = 0; j < level.initial_inventory_size[i]; j++)
10525               {
10526                 int element = level.initial_inventory_content[i][j];
10527                 int collect_count = element_info[element].collect_count_initial;
10528
10529                 if (!IS_CUSTOM_ELEMENT(element))
10530                   collect_count = 1;
10531
10532                 if (collect_count == 0)
10533                   player->inventory_infinite_element = element;
10534                 else
10535                   for (k = 0; k < collect_count; k++)
10536                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10537                       player->inventory_element[player->inventory_size++] =
10538                         element;
10539               }
10540             }
10541           }
10542         }
10543       }
10544
10545       break;
10546     }
10547
10548     // ---------- CE actions  -------------------------------------------------
10549
10550     case CA_SET_CE_VALUE:
10551     {
10552       int last_ce_value = CustomValue[x][y];
10553
10554       CustomValue[x][y] = action_arg_number_new;
10555
10556       if (CustomValue[x][y] != last_ce_value)
10557       {
10558         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10559         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10560
10561         if (CustomValue[x][y] == 0)
10562         {
10563           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10564           ChangeCount[x][y] = 0;        // allow at least one more change
10565
10566           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10567           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10568         }
10569       }
10570
10571       break;
10572     }
10573
10574     case CA_SET_CE_SCORE:
10575     {
10576       int last_ce_score = ei->collect_score;
10577
10578       ei->collect_score = action_arg_number_new;
10579
10580       if (ei->collect_score != last_ce_score)
10581       {
10582         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10583         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10584
10585         if (ei->collect_score == 0)
10586         {
10587           int xx, yy;
10588
10589           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10590           ChangeCount[x][y] = 0;        // allow at least one more change
10591
10592           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10593           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10594
10595           /*
10596             This is a very special case that seems to be a mixture between
10597             CheckElementChange() and CheckTriggeredElementChange(): while
10598             the first one only affects single elements that are triggered
10599             directly, the second one affects multiple elements in the playfield
10600             that are triggered indirectly by another element. This is a third
10601             case: Changing the CE score always affects multiple identical CEs,
10602             so every affected CE must be checked, not only the single CE for
10603             which the CE score was changed in the first place (as every instance
10604             of that CE shares the same CE score, and therefore also can change)!
10605           */
10606           SCAN_PLAYFIELD(xx, yy)
10607           {
10608             if (Tile[xx][yy] == element)
10609               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10610                                  CE_SCORE_GETS_ZERO);
10611           }
10612         }
10613       }
10614
10615       break;
10616     }
10617
10618     case CA_SET_CE_ARTWORK:
10619     {
10620       int artwork_element = action_arg_element;
10621       boolean reset_frame = FALSE;
10622       int xx, yy;
10623
10624       if (action_arg == CA_ARG_ELEMENT_RESET)
10625         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10626                            element);
10627
10628       if (ei->gfx_element != artwork_element)
10629         reset_frame = TRUE;
10630
10631       ei->gfx_element = artwork_element;
10632
10633       SCAN_PLAYFIELD(xx, yy)
10634       {
10635         if (Tile[xx][yy] == element)
10636         {
10637           if (reset_frame)
10638           {
10639             ResetGfxAnimation(xx, yy);
10640             ResetRandomAnimationValue(xx, yy);
10641           }
10642
10643           TEST_DrawLevelField(xx, yy);
10644         }
10645       }
10646
10647       break;
10648     }
10649
10650     // ---------- engine actions  ---------------------------------------------
10651
10652     case CA_SET_ENGINE_SCAN_MODE:
10653     {
10654       InitPlayfieldScanMode(action_arg);
10655
10656       break;
10657     }
10658
10659     default:
10660       break;
10661   }
10662 }
10663
10664 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10665 {
10666   int old_element = Tile[x][y];
10667   int new_element = GetElementFromGroupElement(element);
10668   int previous_move_direction = MovDir[x][y];
10669   int last_ce_value = CustomValue[x][y];
10670   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10671   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10672   boolean add_player_onto_element = (new_element_is_player &&
10673                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10674                                      IS_WALKABLE(old_element));
10675
10676   if (!add_player_onto_element)
10677   {
10678     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10679       RemoveMovingField(x, y);
10680     else
10681       RemoveField(x, y);
10682
10683     Tile[x][y] = new_element;
10684
10685     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10686       MovDir[x][y] = previous_move_direction;
10687
10688     if (element_info[new_element].use_last_ce_value)
10689       CustomValue[x][y] = last_ce_value;
10690
10691     InitField_WithBug1(x, y, FALSE);
10692
10693     new_element = Tile[x][y];   // element may have changed
10694
10695     ResetGfxAnimation(x, y);
10696     ResetRandomAnimationValue(x, y);
10697
10698     TEST_DrawLevelField(x, y);
10699
10700     if (GFX_CRUMBLED(new_element))
10701       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10702   }
10703
10704   // check if element under the player changes from accessible to unaccessible
10705   // (needed for special case of dropping element which then changes)
10706   // (must be checked after creating new element for walkable group elements)
10707   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10708       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10709   {
10710     Bang(x, y);
10711
10712     return;
10713   }
10714
10715   // "ChangeCount" not set yet to allow "entered by player" change one time
10716   if (new_element_is_player)
10717     RelocatePlayer(x, y, new_element);
10718
10719   if (is_change)
10720     ChangeCount[x][y]++;        // count number of changes in the same frame
10721
10722   TestIfBadThingTouchesPlayer(x, y);
10723   TestIfPlayerTouchesCustomElement(x, y);
10724   TestIfElementTouchesCustomElement(x, y);
10725 }
10726
10727 static void CreateField(int x, int y, int element)
10728 {
10729   CreateFieldExt(x, y, element, FALSE);
10730 }
10731
10732 static void CreateElementFromChange(int x, int y, int element)
10733 {
10734   element = GET_VALID_RUNTIME_ELEMENT(element);
10735
10736   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10737   {
10738     int old_element = Tile[x][y];
10739
10740     // prevent changed element from moving in same engine frame
10741     // unless both old and new element can either fall or move
10742     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10743         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10744       Stop[x][y] = TRUE;
10745   }
10746
10747   CreateFieldExt(x, y, element, TRUE);
10748 }
10749
10750 static boolean ChangeElement(int x, int y, int element, int page)
10751 {
10752   struct ElementInfo *ei = &element_info[element];
10753   struct ElementChangeInfo *change = &ei->change_page[page];
10754   int ce_value = CustomValue[x][y];
10755   int ce_score = ei->collect_score;
10756   int target_element;
10757   int old_element = Tile[x][y];
10758
10759   // always use default change event to prevent running into a loop
10760   if (ChangeEvent[x][y] == -1)
10761     ChangeEvent[x][y] = CE_DELAY;
10762
10763   if (ChangeEvent[x][y] == CE_DELAY)
10764   {
10765     // reset actual trigger element, trigger player and action element
10766     change->actual_trigger_element = EL_EMPTY;
10767     change->actual_trigger_player = EL_EMPTY;
10768     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10769     change->actual_trigger_side = CH_SIDE_NONE;
10770     change->actual_trigger_ce_value = 0;
10771     change->actual_trigger_ce_score = 0;
10772   }
10773
10774   // do not change elements more than a specified maximum number of changes
10775   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10776     return FALSE;
10777
10778   ChangeCount[x][y]++;          // count number of changes in the same frame
10779
10780   if (change->explode)
10781   {
10782     Bang(x, y);
10783
10784     return TRUE;
10785   }
10786
10787   if (change->use_target_content)
10788   {
10789     boolean complete_replace = TRUE;
10790     boolean can_replace[3][3];
10791     int xx, yy;
10792
10793     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10794     {
10795       boolean is_empty;
10796       boolean is_walkable;
10797       boolean is_diggable;
10798       boolean is_collectible;
10799       boolean is_removable;
10800       boolean is_destructible;
10801       int ex = x + xx - 1;
10802       int ey = y + yy - 1;
10803       int content_element = change->target_content.e[xx][yy];
10804       int e;
10805
10806       can_replace[xx][yy] = TRUE;
10807
10808       if (ex == x && ey == y)   // do not check changing element itself
10809         continue;
10810
10811       if (content_element == EL_EMPTY_SPACE)
10812       {
10813         can_replace[xx][yy] = FALSE;    // do not replace border with space
10814
10815         continue;
10816       }
10817
10818       if (!IN_LEV_FIELD(ex, ey))
10819       {
10820         can_replace[xx][yy] = FALSE;
10821         complete_replace = FALSE;
10822
10823         continue;
10824       }
10825
10826       e = Tile[ex][ey];
10827
10828       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10829         e = MovingOrBlocked2Element(ex, ey);
10830
10831       is_empty = (IS_FREE(ex, ey) ||
10832                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10833
10834       is_walkable     = (is_empty || IS_WALKABLE(e));
10835       is_diggable     = (is_empty || IS_DIGGABLE(e));
10836       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10837       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10838       is_removable    = (is_diggable || is_collectible);
10839
10840       can_replace[xx][yy] =
10841         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10842           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10843           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10844           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10845           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10846           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10847          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10848
10849       if (!can_replace[xx][yy])
10850         complete_replace = FALSE;
10851     }
10852
10853     if (!change->only_if_complete || complete_replace)
10854     {
10855       boolean something_has_changed = FALSE;
10856
10857       if (change->only_if_complete && change->use_random_replace &&
10858           RND(100) < change->random_percentage)
10859         return FALSE;
10860
10861       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10862       {
10863         int ex = x + xx - 1;
10864         int ey = y + yy - 1;
10865         int content_element;
10866
10867         if (can_replace[xx][yy] && (!change->use_random_replace ||
10868                                     RND(100) < change->random_percentage))
10869         {
10870           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10871             RemoveMovingField(ex, ey);
10872
10873           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10874
10875           content_element = change->target_content.e[xx][yy];
10876           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10877                                               ce_value, ce_score);
10878
10879           CreateElementFromChange(ex, ey, target_element);
10880
10881           something_has_changed = TRUE;
10882
10883           // for symmetry reasons, freeze newly created border elements
10884           if (ex != x || ey != y)
10885             Stop[ex][ey] = TRUE;        // no more moving in this frame
10886         }
10887       }
10888
10889       if (something_has_changed)
10890       {
10891         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10892         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10893       }
10894     }
10895   }
10896   else
10897   {
10898     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10899                                         ce_value, ce_score);
10900
10901     if (element == EL_DIAGONAL_GROWING ||
10902         element == EL_DIAGONAL_SHRINKING)
10903     {
10904       target_element = Store[x][y];
10905
10906       Store[x][y] = EL_EMPTY;
10907     }
10908
10909     // special case: element changes to player (and may be kept if walkable)
10910     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10911       CreateElementFromChange(x, y, EL_EMPTY);
10912
10913     CreateElementFromChange(x, y, target_element);
10914
10915     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10916     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10917   }
10918
10919   // this uses direct change before indirect change
10920   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10921
10922   return TRUE;
10923 }
10924
10925 static void HandleElementChange(int x, int y, int page)
10926 {
10927   int element = MovingOrBlocked2Element(x, y);
10928   struct ElementInfo *ei = &element_info[element];
10929   struct ElementChangeInfo *change = &ei->change_page[page];
10930   boolean handle_action_before_change = FALSE;
10931
10932 #ifdef DEBUG
10933   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10934       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10935   {
10936     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10937           x, y, element, element_info[element].token_name);
10938     Debug("game:playing:HandleElementChange", "This should never happen!");
10939   }
10940 #endif
10941
10942   // this can happen with classic bombs on walkable, changing elements
10943   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10944   {
10945     return;
10946   }
10947
10948   if (ChangeDelay[x][y] == 0)           // initialize element change
10949   {
10950     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10951
10952     if (change->can_change)
10953     {
10954       // !!! not clear why graphic animation should be reset at all here !!!
10955       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10956       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10957
10958       /*
10959         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10960
10961         When using an animation frame delay of 1 (this only happens with
10962         "sp_zonk.moving.left/right" in the classic graphics), the default
10963         (non-moving) animation shows wrong animation frames (while the
10964         moving animation, like "sp_zonk.moving.left/right", is correct,
10965         so this graphical bug never shows up with the classic graphics).
10966         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10967         be drawn instead of the correct frames 0,1,2,3. This is caused by
10968         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10969         an element change: First when the change delay ("ChangeDelay[][]")
10970         counter has reached zero after decrementing, then a second time in
10971         the next frame (after "GfxFrame[][]" was already incremented) when
10972         "ChangeDelay[][]" is reset to the initial delay value again.
10973
10974         This causes frame 0 to be drawn twice, while the last frame won't
10975         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10976
10977         As some animations may already be cleverly designed around this bug
10978         (at least the "Snake Bite" snake tail animation does this), it cannot
10979         simply be fixed here without breaking such existing animations.
10980         Unfortunately, it cannot easily be detected if a graphics set was
10981         designed "before" or "after" the bug was fixed. As a workaround,
10982         a new graphics set option "game.graphics_engine_version" was added
10983         to be able to specify the game's major release version for which the
10984         graphics set was designed, which can then be used to decide if the
10985         bugfix should be used (version 4 and above) or not (version 3 or
10986         below, or if no version was specified at all, as with old sets).
10987
10988         (The wrong/fixed animation frames can be tested with the test level set
10989         "test_gfxframe" and level "000", which contains a specially prepared
10990         custom element at level position (x/y) == (11/9) which uses the zonk
10991         animation mentioned above. Using "game.graphics_engine_version: 4"
10992         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10993         This can also be seen from the debug output for this test element.)
10994       */
10995
10996       // when a custom element is about to change (for example by change delay),
10997       // do not reset graphic animation when the custom element is moving
10998       if (game.graphics_engine_version < 4 &&
10999           !IS_MOVING(x, y))
11000       {
11001         ResetGfxAnimation(x, y);
11002         ResetRandomAnimationValue(x, y);
11003       }
11004
11005       if (change->pre_change_function)
11006         change->pre_change_function(x, y);
11007     }
11008   }
11009
11010   ChangeDelay[x][y]--;
11011
11012   if (ChangeDelay[x][y] != 0)           // continue element change
11013   {
11014     if (change->can_change)
11015     {
11016       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11017
11018       if (IS_ANIMATED(graphic))
11019         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11020
11021       if (change->change_function)
11022         change->change_function(x, y);
11023     }
11024   }
11025   else                                  // finish element change
11026   {
11027     if (ChangePage[x][y] != -1)         // remember page from delayed change
11028     {
11029       page = ChangePage[x][y];
11030       ChangePage[x][y] = -1;
11031
11032       change = &ei->change_page[page];
11033     }
11034
11035     if (IS_MOVING(x, y))                // never change a running system ;-)
11036     {
11037       ChangeDelay[x][y] = 1;            // try change after next move step
11038       ChangePage[x][y] = page;          // remember page to use for change
11039
11040       return;
11041     }
11042
11043     // special case: set new level random seed before changing element
11044     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11045       handle_action_before_change = TRUE;
11046
11047     if (change->has_action && handle_action_before_change)
11048       ExecuteCustomElementAction(x, y, element, page);
11049
11050     if (change->can_change)
11051     {
11052       if (ChangeElement(x, y, element, page))
11053       {
11054         if (change->post_change_function)
11055           change->post_change_function(x, y);
11056       }
11057     }
11058
11059     if (change->has_action && !handle_action_before_change)
11060       ExecuteCustomElementAction(x, y, element, page);
11061   }
11062 }
11063
11064 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11065                                               int trigger_element,
11066                                               int trigger_event,
11067                                               int trigger_player,
11068                                               int trigger_side,
11069                                               int trigger_page)
11070 {
11071   boolean change_done_any = FALSE;
11072   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11073   int i;
11074
11075   if (!(trigger_events[trigger_element][trigger_event]))
11076     return FALSE;
11077
11078   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11079
11080   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11081   {
11082     int element = EL_CUSTOM_START + i;
11083     boolean change_done = FALSE;
11084     int p;
11085
11086     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11087         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11088       continue;
11089
11090     for (p = 0; p < element_info[element].num_change_pages; p++)
11091     {
11092       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11093
11094       if (change->can_change_or_has_action &&
11095           change->has_event[trigger_event] &&
11096           change->trigger_side & trigger_side &&
11097           change->trigger_player & trigger_player &&
11098           change->trigger_page & trigger_page_bits &&
11099           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11100       {
11101         change->actual_trigger_element = trigger_element;
11102         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11103         change->actual_trigger_player_bits = trigger_player;
11104         change->actual_trigger_side = trigger_side;
11105         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11106         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11107
11108         if ((change->can_change && !change_done) || change->has_action)
11109         {
11110           int x, y;
11111
11112           SCAN_PLAYFIELD(x, y)
11113           {
11114             if (Tile[x][y] == element)
11115             {
11116               if (change->can_change && !change_done)
11117               {
11118                 // if element already changed in this frame, not only prevent
11119                 // another element change (checked in ChangeElement()), but
11120                 // also prevent additional element actions for this element
11121
11122                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11123                     !level.use_action_after_change_bug)
11124                   continue;
11125
11126                 ChangeDelay[x][y] = 1;
11127                 ChangeEvent[x][y] = trigger_event;
11128
11129                 HandleElementChange(x, y, p);
11130               }
11131               else if (change->has_action)
11132               {
11133                 // if element already changed in this frame, not only prevent
11134                 // another element change (checked in ChangeElement()), but
11135                 // also prevent additional element actions for this element
11136
11137                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11138                     !level.use_action_after_change_bug)
11139                   continue;
11140
11141                 ExecuteCustomElementAction(x, y, element, p);
11142                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11143               }
11144             }
11145           }
11146
11147           if (change->can_change)
11148           {
11149             change_done = TRUE;
11150             change_done_any = TRUE;
11151           }
11152         }
11153       }
11154     }
11155   }
11156
11157   RECURSION_LOOP_DETECTION_END();
11158
11159   return change_done_any;
11160 }
11161
11162 static boolean CheckElementChangeExt(int x, int y,
11163                                      int element,
11164                                      int trigger_element,
11165                                      int trigger_event,
11166                                      int trigger_player,
11167                                      int trigger_side)
11168 {
11169   boolean change_done = FALSE;
11170   int p;
11171
11172   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11173       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11174     return FALSE;
11175
11176   if (Tile[x][y] == EL_BLOCKED)
11177   {
11178     Blocked2Moving(x, y, &x, &y);
11179     element = Tile[x][y];
11180   }
11181
11182   // check if element has already changed or is about to change after moving
11183   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11184        Tile[x][y] != element) ||
11185
11186       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11187        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11188         ChangePage[x][y] != -1)))
11189     return FALSE;
11190
11191   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11192
11193   for (p = 0; p < element_info[element].num_change_pages; p++)
11194   {
11195     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11196
11197     /* check trigger element for all events where the element that is checked
11198        for changing interacts with a directly adjacent element -- this is
11199        different to element changes that affect other elements to change on the
11200        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11201     boolean check_trigger_element =
11202       (trigger_event == CE_NEXT_TO_X ||
11203        trigger_event == CE_TOUCHING_X ||
11204        trigger_event == CE_HITTING_X ||
11205        trigger_event == CE_HIT_BY_X ||
11206        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11207
11208     if (change->can_change_or_has_action &&
11209         change->has_event[trigger_event] &&
11210         change->trigger_side & trigger_side &&
11211         change->trigger_player & trigger_player &&
11212         (!check_trigger_element ||
11213          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11214     {
11215       change->actual_trigger_element = trigger_element;
11216       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11217       change->actual_trigger_player_bits = trigger_player;
11218       change->actual_trigger_side = trigger_side;
11219       change->actual_trigger_ce_value = CustomValue[x][y];
11220       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11221
11222       // special case: trigger element not at (x,y) position for some events
11223       if (check_trigger_element)
11224       {
11225         static struct
11226         {
11227           int dx, dy;
11228         } move_xy[] =
11229           {
11230             {  0,  0 },
11231             { -1,  0 },
11232             { +1,  0 },
11233             {  0,  0 },
11234             {  0, -1 },
11235             {  0,  0 }, { 0, 0 }, { 0, 0 },
11236             {  0, +1 }
11237           };
11238
11239         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11240         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11241
11242         change->actual_trigger_ce_value = CustomValue[xx][yy];
11243         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11244       }
11245
11246       if (change->can_change && !change_done)
11247       {
11248         ChangeDelay[x][y] = 1;
11249         ChangeEvent[x][y] = trigger_event;
11250
11251         HandleElementChange(x, y, p);
11252
11253         change_done = TRUE;
11254       }
11255       else if (change->has_action)
11256       {
11257         ExecuteCustomElementAction(x, y, element, p);
11258         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11259       }
11260     }
11261   }
11262
11263   RECURSION_LOOP_DETECTION_END();
11264
11265   return change_done;
11266 }
11267
11268 static void PlayPlayerSound(struct PlayerInfo *player)
11269 {
11270   int jx = player->jx, jy = player->jy;
11271   int sound_element = player->artwork_element;
11272   int last_action = player->last_action_waiting;
11273   int action = player->action_waiting;
11274
11275   if (player->is_waiting)
11276   {
11277     if (action != last_action)
11278       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11279     else
11280       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11281   }
11282   else
11283   {
11284     if (action != last_action)
11285       StopSound(element_info[sound_element].sound[last_action]);
11286
11287     if (last_action == ACTION_SLEEPING)
11288       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11289   }
11290 }
11291
11292 static void PlayAllPlayersSound(void)
11293 {
11294   int i;
11295
11296   for (i = 0; i < MAX_PLAYERS; i++)
11297     if (stored_player[i].active)
11298       PlayPlayerSound(&stored_player[i]);
11299 }
11300
11301 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11302 {
11303   boolean last_waiting = player->is_waiting;
11304   int move_dir = player->MovDir;
11305
11306   player->dir_waiting = move_dir;
11307   player->last_action_waiting = player->action_waiting;
11308
11309   if (is_waiting)
11310   {
11311     if (!last_waiting)          // not waiting -> waiting
11312     {
11313       player->is_waiting = TRUE;
11314
11315       player->frame_counter_bored =
11316         FrameCounter +
11317         game.player_boring_delay_fixed +
11318         GetSimpleRandom(game.player_boring_delay_random);
11319       player->frame_counter_sleeping =
11320         FrameCounter +
11321         game.player_sleeping_delay_fixed +
11322         GetSimpleRandom(game.player_sleeping_delay_random);
11323
11324       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11325     }
11326
11327     if (game.player_sleeping_delay_fixed +
11328         game.player_sleeping_delay_random > 0 &&
11329         player->anim_delay_counter == 0 &&
11330         player->post_delay_counter == 0 &&
11331         FrameCounter >= player->frame_counter_sleeping)
11332       player->is_sleeping = TRUE;
11333     else if (game.player_boring_delay_fixed +
11334              game.player_boring_delay_random > 0 &&
11335              FrameCounter >= player->frame_counter_bored)
11336       player->is_bored = TRUE;
11337
11338     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11339                               player->is_bored ? ACTION_BORING :
11340                               ACTION_WAITING);
11341
11342     if (player->is_sleeping && player->use_murphy)
11343     {
11344       // special case for sleeping Murphy when leaning against non-free tile
11345
11346       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11347           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11348            !IS_MOVING(player->jx - 1, player->jy)))
11349         move_dir = MV_LEFT;
11350       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11351                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11352                 !IS_MOVING(player->jx + 1, player->jy)))
11353         move_dir = MV_RIGHT;
11354       else
11355         player->is_sleeping = FALSE;
11356
11357       player->dir_waiting = move_dir;
11358     }
11359
11360     if (player->is_sleeping)
11361     {
11362       if (player->num_special_action_sleeping > 0)
11363       {
11364         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11365         {
11366           int last_special_action = player->special_action_sleeping;
11367           int num_special_action = player->num_special_action_sleeping;
11368           int special_action =
11369             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11370              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11371              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11372              last_special_action + 1 : ACTION_SLEEPING);
11373           int special_graphic =
11374             el_act_dir2img(player->artwork_element, special_action, move_dir);
11375
11376           player->anim_delay_counter =
11377             graphic_info[special_graphic].anim_delay_fixed +
11378             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11379           player->post_delay_counter =
11380             graphic_info[special_graphic].post_delay_fixed +
11381             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11382
11383           player->special_action_sleeping = special_action;
11384         }
11385
11386         if (player->anim_delay_counter > 0)
11387         {
11388           player->action_waiting = player->special_action_sleeping;
11389           player->anim_delay_counter--;
11390         }
11391         else if (player->post_delay_counter > 0)
11392         {
11393           player->post_delay_counter--;
11394         }
11395       }
11396     }
11397     else if (player->is_bored)
11398     {
11399       if (player->num_special_action_bored > 0)
11400       {
11401         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11402         {
11403           int special_action =
11404             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11405           int special_graphic =
11406             el_act_dir2img(player->artwork_element, special_action, move_dir);
11407
11408           player->anim_delay_counter =
11409             graphic_info[special_graphic].anim_delay_fixed +
11410             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11411           player->post_delay_counter =
11412             graphic_info[special_graphic].post_delay_fixed +
11413             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11414
11415           player->special_action_bored = special_action;
11416         }
11417
11418         if (player->anim_delay_counter > 0)
11419         {
11420           player->action_waiting = player->special_action_bored;
11421           player->anim_delay_counter--;
11422         }
11423         else if (player->post_delay_counter > 0)
11424         {
11425           player->post_delay_counter--;
11426         }
11427       }
11428     }
11429   }
11430   else if (last_waiting)        // waiting -> not waiting
11431   {
11432     player->is_waiting = FALSE;
11433     player->is_bored = FALSE;
11434     player->is_sleeping = FALSE;
11435
11436     player->frame_counter_bored = -1;
11437     player->frame_counter_sleeping = -1;
11438
11439     player->anim_delay_counter = 0;
11440     player->post_delay_counter = 0;
11441
11442     player->dir_waiting = player->MovDir;
11443     player->action_waiting = ACTION_DEFAULT;
11444
11445     player->special_action_bored = ACTION_DEFAULT;
11446     player->special_action_sleeping = ACTION_DEFAULT;
11447   }
11448 }
11449
11450 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11451 {
11452   if ((!player->is_moving  && player->was_moving) ||
11453       (player->MovPos == 0 && player->was_moving) ||
11454       (player->is_snapping && !player->was_snapping) ||
11455       (player->is_dropping && !player->was_dropping))
11456   {
11457     if (!CheckSaveEngineSnapshotToList())
11458       return;
11459
11460     player->was_moving = FALSE;
11461     player->was_snapping = TRUE;
11462     player->was_dropping = TRUE;
11463   }
11464   else
11465   {
11466     if (player->is_moving)
11467       player->was_moving = TRUE;
11468
11469     if (!player->is_snapping)
11470       player->was_snapping = FALSE;
11471
11472     if (!player->is_dropping)
11473       player->was_dropping = FALSE;
11474   }
11475
11476   static struct MouseActionInfo mouse_action_last = { 0 };
11477   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11478   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11479
11480   if (new_released)
11481     CheckSaveEngineSnapshotToList();
11482
11483   mouse_action_last = mouse_action;
11484 }
11485
11486 static void CheckSingleStepMode(struct PlayerInfo *player)
11487 {
11488   if (tape.single_step && tape.recording && !tape.pausing)
11489   {
11490     // as it is called "single step mode", just return to pause mode when the
11491     // player stopped moving after one tile (or never starts moving at all)
11492     // (reverse logic needed here in case single step mode used in team mode)
11493     if (player->is_moving ||
11494         player->is_pushing ||
11495         player->is_dropping_pressed ||
11496         player->effective_mouse_action.button)
11497       game.enter_single_step_mode = FALSE;
11498   }
11499
11500   CheckSaveEngineSnapshot(player);
11501 }
11502
11503 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11504 {
11505   int left      = player_action & JOY_LEFT;
11506   int right     = player_action & JOY_RIGHT;
11507   int up        = player_action & JOY_UP;
11508   int down      = player_action & JOY_DOWN;
11509   int button1   = player_action & JOY_BUTTON_1;
11510   int button2   = player_action & JOY_BUTTON_2;
11511   int dx        = (left ? -1 : right ? 1 : 0);
11512   int dy        = (up   ? -1 : down  ? 1 : 0);
11513
11514   if (!player->active || tape.pausing)
11515     return 0;
11516
11517   if (player_action)
11518   {
11519     if (button1)
11520       SnapField(player, dx, dy);
11521     else
11522     {
11523       if (button2)
11524         DropElement(player);
11525
11526       MovePlayer(player, dx, dy);
11527     }
11528
11529     CheckSingleStepMode(player);
11530
11531     SetPlayerWaiting(player, FALSE);
11532
11533     return player_action;
11534   }
11535   else
11536   {
11537     // no actions for this player (no input at player's configured device)
11538
11539     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11540     SnapField(player, 0, 0);
11541     CheckGravityMovementWhenNotMoving(player);
11542
11543     if (player->MovPos == 0)
11544       SetPlayerWaiting(player, TRUE);
11545
11546     if (player->MovPos == 0)    // needed for tape.playing
11547       player->is_moving = FALSE;
11548
11549     player->is_dropping = FALSE;
11550     player->is_dropping_pressed = FALSE;
11551     player->drop_pressed_delay = 0;
11552
11553     CheckSingleStepMode(player);
11554
11555     return 0;
11556   }
11557 }
11558
11559 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11560                                          byte *tape_action)
11561 {
11562   if (!tape.use_mouse_actions)
11563     return;
11564
11565   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11566   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11567   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11568 }
11569
11570 static void SetTapeActionFromMouseAction(byte *tape_action,
11571                                          struct MouseActionInfo *mouse_action)
11572 {
11573   if (!tape.use_mouse_actions)
11574     return;
11575
11576   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11577   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11578   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11579 }
11580
11581 static void CheckLevelSolved(void)
11582 {
11583   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11584   {
11585     if (game_em.level_solved &&
11586         !game_em.game_over)                             // game won
11587     {
11588       LevelSolved();
11589
11590       game_em.game_over = TRUE;
11591
11592       game.all_players_gone = TRUE;
11593     }
11594
11595     if (game_em.game_over)                              // game lost
11596       game.all_players_gone = TRUE;
11597   }
11598   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11599   {
11600     if (game_sp.level_solved &&
11601         !game_sp.game_over)                             // game won
11602     {
11603       LevelSolved();
11604
11605       game_sp.game_over = TRUE;
11606
11607       game.all_players_gone = TRUE;
11608     }
11609
11610     if (game_sp.game_over)                              // game lost
11611       game.all_players_gone = TRUE;
11612   }
11613   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11614   {
11615     if (game_mm.level_solved &&
11616         !game_mm.game_over)                             // game won
11617     {
11618       LevelSolved();
11619
11620       game_mm.game_over = TRUE;
11621
11622       game.all_players_gone = TRUE;
11623     }
11624
11625     if (game_mm.game_over)                              // game lost
11626       game.all_players_gone = TRUE;
11627   }
11628 }
11629
11630 static void CheckLevelTime_StepCounter(void)
11631 {
11632   int i;
11633
11634   TimePlayed++;
11635
11636   if (TimeLeft > 0)
11637   {
11638     TimeLeft--;
11639
11640     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11641       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11642
11643     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11644
11645     DisplayGameControlValues();
11646
11647     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11648       for (i = 0; i < MAX_PLAYERS; i++)
11649         KillPlayer(&stored_player[i]);
11650   }
11651   else if (game.no_level_time_limit && !game.all_players_gone)
11652   {
11653     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11654
11655     DisplayGameControlValues();
11656   }
11657 }
11658
11659 static void CheckLevelTime(void)
11660 {
11661   int i;
11662
11663   if (TimeFrames >= FRAMES_PER_SECOND)
11664   {
11665     TimeFrames = 0;
11666     TapeTime++;
11667
11668     for (i = 0; i < MAX_PLAYERS; i++)
11669     {
11670       struct PlayerInfo *player = &stored_player[i];
11671
11672       if (SHIELD_ON(player))
11673       {
11674         player->shield_normal_time_left--;
11675
11676         if (player->shield_deadly_time_left > 0)
11677           player->shield_deadly_time_left--;
11678       }
11679     }
11680
11681     if (!game.LevelSolved && !level.use_step_counter)
11682     {
11683       TimePlayed++;
11684
11685       if (TimeLeft > 0)
11686       {
11687         TimeLeft--;
11688
11689         if (TimeLeft <= 10 && game.time_limit)
11690           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11691
11692         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11693            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11694
11695         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11696
11697         if (!TimeLeft && game.time_limit)
11698         {
11699           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11700             game_em.lev->killed_out_of_time = TRUE;
11701           else
11702             for (i = 0; i < MAX_PLAYERS; i++)
11703               KillPlayer(&stored_player[i]);
11704         }
11705       }
11706       else if (game.no_level_time_limit && !game.all_players_gone)
11707       {
11708         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11709       }
11710
11711       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11712     }
11713
11714     if (tape.recording || tape.playing)
11715       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11716   }
11717
11718   if (tape.recording || tape.playing)
11719     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11720
11721   UpdateAndDisplayGameControlValues();
11722 }
11723
11724 void AdvanceFrameAndPlayerCounters(int player_nr)
11725 {
11726   int i;
11727
11728   // advance frame counters (global frame counter and time frame counter)
11729   FrameCounter++;
11730   TimeFrames++;
11731
11732   // advance player counters (counters for move delay, move animation etc.)
11733   for (i = 0; i < MAX_PLAYERS; i++)
11734   {
11735     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11736     int move_delay_value = stored_player[i].move_delay_value;
11737     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11738
11739     if (!advance_player_counters)       // not all players may be affected
11740       continue;
11741
11742     if (move_frames == 0)       // less than one move per game frame
11743     {
11744       int stepsize = TILEX / move_delay_value;
11745       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11746       int count = (stored_player[i].is_moving ?
11747                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11748
11749       if (count % delay == 0)
11750         move_frames = 1;
11751     }
11752
11753     stored_player[i].Frame += move_frames;
11754
11755     if (stored_player[i].MovPos != 0)
11756       stored_player[i].StepFrame += move_frames;
11757
11758     if (stored_player[i].move_delay > 0)
11759       stored_player[i].move_delay--;
11760
11761     // due to bugs in previous versions, counter must count up, not down
11762     if (stored_player[i].push_delay != -1)
11763       stored_player[i].push_delay++;
11764
11765     if (stored_player[i].drop_delay > 0)
11766       stored_player[i].drop_delay--;
11767
11768     if (stored_player[i].is_dropping_pressed)
11769       stored_player[i].drop_pressed_delay++;
11770   }
11771 }
11772
11773 void StartGameActions(boolean init_network_game, boolean record_tape,
11774                       int random_seed)
11775 {
11776   unsigned int new_random_seed = InitRND(random_seed);
11777
11778   if (record_tape)
11779     TapeStartRecording(new_random_seed);
11780
11781   if (setup.auto_pause_on_start && !tape.pausing)
11782     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11783
11784   if (init_network_game)
11785   {
11786     SendToServer_LevelFile();
11787     SendToServer_StartPlaying();
11788
11789     return;
11790   }
11791
11792   InitGame();
11793 }
11794
11795 static void GameActionsExt(void)
11796 {
11797 #if 0
11798   static unsigned int game_frame_delay = 0;
11799 #endif
11800   unsigned int game_frame_delay_value;
11801   byte *recorded_player_action;
11802   byte summarized_player_action = 0;
11803   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11804   int i;
11805
11806   // detect endless loops, caused by custom element programming
11807   if (recursion_loop_detected && recursion_loop_depth == 0)
11808   {
11809     char *message = getStringCat3("Internal Error! Element ",
11810                                   EL_NAME(recursion_loop_element),
11811                                   " caused endless loop! Quit the game?");
11812
11813     Warn("element '%s' caused endless loop in game engine",
11814          EL_NAME(recursion_loop_element));
11815
11816     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11817
11818     recursion_loop_detected = FALSE;    // if game should be continued
11819
11820     free(message);
11821
11822     return;
11823   }
11824
11825   if (game.restart_level)
11826     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11827
11828   CheckLevelSolved();
11829
11830   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11831     GameWon();
11832
11833   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11834     TapeStop();
11835
11836   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11837     return;
11838
11839   game_frame_delay_value =
11840     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11841
11842   if (tape.playing && tape.warp_forward && !tape.pausing)
11843     game_frame_delay_value = 0;
11844
11845   SetVideoFrameDelay(game_frame_delay_value);
11846
11847   // (de)activate virtual buttons depending on current game status
11848   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11849   {
11850     if (game.all_players_gone)  // if no players there to be controlled anymore
11851       SetOverlayActive(FALSE);
11852     else if (!tape.playing)     // if game continues after tape stopped playing
11853       SetOverlayActive(TRUE);
11854   }
11855
11856 #if 0
11857 #if 0
11858   // ---------- main game synchronization point ----------
11859
11860   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11861
11862   Debug("game:playing:skip", "skip == %d", skip);
11863
11864 #else
11865   // ---------- main game synchronization point ----------
11866
11867   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11868 #endif
11869 #endif
11870
11871   if (network_playing && !network_player_action_received)
11872   {
11873     // try to get network player actions in time
11874
11875     // last chance to get network player actions without main loop delay
11876     HandleNetworking();
11877
11878     // game was quit by network peer
11879     if (game_status != GAME_MODE_PLAYING)
11880       return;
11881
11882     // check if network player actions still missing and game still running
11883     if (!network_player_action_received && !checkGameEnded())
11884       return;           // failed to get network player actions in time
11885
11886     // do not yet reset "network_player_action_received" (for tape.pausing)
11887   }
11888
11889   if (tape.pausing)
11890     return;
11891
11892   // at this point we know that we really continue executing the game
11893
11894   network_player_action_received = FALSE;
11895
11896   // when playing tape, read previously recorded player input from tape data
11897   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11898
11899   local_player->effective_mouse_action = local_player->mouse_action;
11900
11901   if (recorded_player_action != NULL)
11902     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11903                                  recorded_player_action);
11904
11905   // TapePlayAction() may return NULL when toggling to "pause before death"
11906   if (tape.pausing)
11907     return;
11908
11909   if (tape.set_centered_player)
11910   {
11911     game.centered_player_nr_next = tape.centered_player_nr_next;
11912     game.set_centered_player = TRUE;
11913   }
11914
11915   for (i = 0; i < MAX_PLAYERS; i++)
11916   {
11917     summarized_player_action |= stored_player[i].action;
11918
11919     if (!network_playing && (game.team_mode || tape.playing))
11920       stored_player[i].effective_action = stored_player[i].action;
11921   }
11922
11923   if (network_playing && !checkGameEnded())
11924     SendToServer_MovePlayer(summarized_player_action);
11925
11926   // summarize all actions at local players mapped input device position
11927   // (this allows using different input devices in single player mode)
11928   if (!network.enabled && !game.team_mode)
11929     stored_player[map_player_action[local_player->index_nr]].effective_action =
11930       summarized_player_action;
11931
11932   // summarize all actions at centered player in local team mode
11933   if (tape.recording &&
11934       setup.team_mode && !network.enabled &&
11935       setup.input_on_focus &&
11936       game.centered_player_nr != -1)
11937   {
11938     for (i = 0; i < MAX_PLAYERS; i++)
11939       stored_player[map_player_action[i]].effective_action =
11940         (i == game.centered_player_nr ? summarized_player_action : 0);
11941   }
11942
11943   if (recorded_player_action != NULL)
11944     for (i = 0; i < MAX_PLAYERS; i++)
11945       stored_player[i].effective_action = recorded_player_action[i];
11946
11947   for (i = 0; i < MAX_PLAYERS; i++)
11948   {
11949     tape_action[i] = stored_player[i].effective_action;
11950
11951     /* (this may happen in the RND game engine if a player was not present on
11952        the playfield on level start, but appeared later from a custom element */
11953     if (setup.team_mode &&
11954         tape.recording &&
11955         tape_action[i] &&
11956         !tape.player_participates[i])
11957       tape.player_participates[i] = TRUE;
11958   }
11959
11960   SetTapeActionFromMouseAction(tape_action,
11961                                &local_player->effective_mouse_action);
11962
11963   // only record actions from input devices, but not programmed actions
11964   if (tape.recording)
11965     TapeRecordAction(tape_action);
11966
11967   // remember if game was played (especially after tape stopped playing)
11968   if (!tape.playing && summarized_player_action)
11969     game.GamePlayed = TRUE;
11970
11971 #if USE_NEW_PLAYER_ASSIGNMENTS
11972   // !!! also map player actions in single player mode !!!
11973   // if (game.team_mode)
11974   if (1)
11975   {
11976     byte mapped_action[MAX_PLAYERS];
11977
11978 #if DEBUG_PLAYER_ACTIONS
11979     for (i = 0; i < MAX_PLAYERS; i++)
11980       DebugContinued("", "%d, ", stored_player[i].effective_action);
11981 #endif
11982
11983     for (i = 0; i < MAX_PLAYERS; i++)
11984       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11985
11986     for (i = 0; i < MAX_PLAYERS; i++)
11987       stored_player[i].effective_action = mapped_action[i];
11988
11989 #if DEBUG_PLAYER_ACTIONS
11990     DebugContinued("", "=> ");
11991     for (i = 0; i < MAX_PLAYERS; i++)
11992       DebugContinued("", "%d, ", stored_player[i].effective_action);
11993     DebugContinued("game:playing:player", "\n");
11994 #endif
11995   }
11996 #if DEBUG_PLAYER_ACTIONS
11997   else
11998   {
11999     for (i = 0; i < MAX_PLAYERS; i++)
12000       DebugContinued("", "%d, ", stored_player[i].effective_action);
12001     DebugContinued("game:playing:player", "\n");
12002   }
12003 #endif
12004 #endif
12005
12006   for (i = 0; i < MAX_PLAYERS; i++)
12007   {
12008     // allow engine snapshot in case of changed movement attempt
12009     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12010         (stored_player[i].effective_action & KEY_MOTION))
12011       game.snapshot.changed_action = TRUE;
12012
12013     // allow engine snapshot in case of snapping/dropping attempt
12014     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12015         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12016       game.snapshot.changed_action = TRUE;
12017
12018     game.snapshot.last_action[i] = stored_player[i].effective_action;
12019   }
12020
12021   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12022   {
12023     GameActions_EM_Main();
12024   }
12025   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12026   {
12027     GameActions_SP_Main();
12028   }
12029   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12030   {
12031     GameActions_MM_Main();
12032   }
12033   else
12034   {
12035     GameActions_RND_Main();
12036   }
12037
12038   BlitScreenToBitmap(backbuffer);
12039
12040   CheckLevelSolved();
12041   CheckLevelTime();
12042
12043   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12044
12045   if (global.show_frames_per_second)
12046   {
12047     static unsigned int fps_counter = 0;
12048     static int fps_frames = 0;
12049     unsigned int fps_delay_ms = Counter() - fps_counter;
12050
12051     fps_frames++;
12052
12053     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12054     {
12055       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12056
12057       fps_frames = 0;
12058       fps_counter = Counter();
12059
12060       // always draw FPS to screen after FPS value was updated
12061       redraw_mask |= REDRAW_FPS;
12062     }
12063
12064     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12065     if (GetDrawDeactivationMask() == REDRAW_NONE)
12066       redraw_mask |= REDRAW_FPS;
12067   }
12068 }
12069
12070 static void GameActions_CheckSaveEngineSnapshot(void)
12071 {
12072   if (!game.snapshot.save_snapshot)
12073     return;
12074
12075   // clear flag for saving snapshot _before_ saving snapshot
12076   game.snapshot.save_snapshot = FALSE;
12077
12078   SaveEngineSnapshotToList();
12079 }
12080
12081 void GameActions(void)
12082 {
12083   GameActionsExt();
12084
12085   GameActions_CheckSaveEngineSnapshot();
12086 }
12087
12088 void GameActions_EM_Main(void)
12089 {
12090   byte effective_action[MAX_PLAYERS];
12091   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12092   int i;
12093
12094   for (i = 0; i < MAX_PLAYERS; i++)
12095     effective_action[i] = stored_player[i].effective_action;
12096
12097   GameActions_EM(effective_action, warp_mode);
12098 }
12099
12100 void GameActions_SP_Main(void)
12101 {
12102   byte effective_action[MAX_PLAYERS];
12103   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12104   int i;
12105
12106   for (i = 0; i < MAX_PLAYERS; i++)
12107     effective_action[i] = stored_player[i].effective_action;
12108
12109   GameActions_SP(effective_action, warp_mode);
12110
12111   for (i = 0; i < MAX_PLAYERS; i++)
12112   {
12113     if (stored_player[i].force_dropping)
12114       stored_player[i].action |= KEY_BUTTON_DROP;
12115
12116     stored_player[i].force_dropping = FALSE;
12117   }
12118 }
12119
12120 void GameActions_MM_Main(void)
12121 {
12122   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12123
12124   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12125 }
12126
12127 void GameActions_RND_Main(void)
12128 {
12129   GameActions_RND();
12130 }
12131
12132 void GameActions_RND(void)
12133 {
12134   static struct MouseActionInfo mouse_action_last = { 0 };
12135   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12136   int magic_wall_x = 0, magic_wall_y = 0;
12137   int i, x, y, element, graphic, last_gfx_frame;
12138
12139   InitPlayfieldScanModeVars();
12140
12141   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12142   {
12143     SCAN_PLAYFIELD(x, y)
12144     {
12145       ChangeCount[x][y] = 0;
12146       ChangeEvent[x][y] = -1;
12147     }
12148   }
12149
12150   if (game.set_centered_player)
12151   {
12152     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12153
12154     // switching to "all players" only possible if all players fit to screen
12155     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12156     {
12157       game.centered_player_nr_next = game.centered_player_nr;
12158       game.set_centered_player = FALSE;
12159     }
12160
12161     // do not switch focus to non-existing (or non-active) player
12162     if (game.centered_player_nr_next >= 0 &&
12163         !stored_player[game.centered_player_nr_next].active)
12164     {
12165       game.centered_player_nr_next = game.centered_player_nr;
12166       game.set_centered_player = FALSE;
12167     }
12168   }
12169
12170   if (game.set_centered_player &&
12171       ScreenMovPos == 0)        // screen currently aligned at tile position
12172   {
12173     int sx, sy;
12174
12175     if (game.centered_player_nr_next == -1)
12176     {
12177       setScreenCenteredToAllPlayers(&sx, &sy);
12178     }
12179     else
12180     {
12181       sx = stored_player[game.centered_player_nr_next].jx;
12182       sy = stored_player[game.centered_player_nr_next].jy;
12183     }
12184
12185     game.centered_player_nr = game.centered_player_nr_next;
12186     game.set_centered_player = FALSE;
12187
12188     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12189     DrawGameDoorValues();
12190   }
12191
12192   // check single step mode (set flag and clear again if any player is active)
12193   game.enter_single_step_mode =
12194     (tape.single_step && tape.recording && !tape.pausing);
12195
12196   for (i = 0; i < MAX_PLAYERS; i++)
12197   {
12198     int actual_player_action = stored_player[i].effective_action;
12199
12200 #if 1
12201     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12202        - rnd_equinox_tetrachloride 048
12203        - rnd_equinox_tetrachloride_ii 096
12204        - rnd_emanuel_schmieg 002
12205        - doctor_sloan_ww 001, 020
12206     */
12207     if (stored_player[i].MovPos == 0)
12208       CheckGravityMovement(&stored_player[i]);
12209 #endif
12210
12211     // overwrite programmed action with tape action
12212     if (stored_player[i].programmed_action)
12213       actual_player_action = stored_player[i].programmed_action;
12214
12215     PlayerActions(&stored_player[i], actual_player_action);
12216
12217     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12218   }
12219
12220   // single step pause mode may already have been toggled by "ScrollPlayer()"
12221   if (game.enter_single_step_mode && !tape.pausing)
12222     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12223
12224   ScrollScreen(NULL, SCROLL_GO_ON);
12225
12226   /* for backwards compatibility, the following code emulates a fixed bug that
12227      occured when pushing elements (causing elements that just made their last
12228      pushing step to already (if possible) make their first falling step in the
12229      same game frame, which is bad); this code is also needed to use the famous
12230      "spring push bug" which is used in older levels and might be wanted to be
12231      used also in newer levels, but in this case the buggy pushing code is only
12232      affecting the "spring" element and no other elements */
12233
12234   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12235   {
12236     for (i = 0; i < MAX_PLAYERS; i++)
12237     {
12238       struct PlayerInfo *player = &stored_player[i];
12239       int x = player->jx;
12240       int y = player->jy;
12241
12242       if (player->active && player->is_pushing && player->is_moving &&
12243           IS_MOVING(x, y) &&
12244           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12245            Tile[x][y] == EL_SPRING))
12246       {
12247         ContinueMoving(x, y);
12248
12249         // continue moving after pushing (this is actually a bug)
12250         if (!IS_MOVING(x, y))
12251           Stop[x][y] = FALSE;
12252       }
12253     }
12254   }
12255
12256   SCAN_PLAYFIELD(x, y)
12257   {
12258     Last[x][y] = Tile[x][y];
12259
12260     ChangeCount[x][y] = 0;
12261     ChangeEvent[x][y] = -1;
12262
12263     // this must be handled before main playfield loop
12264     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12265     {
12266       MovDelay[x][y]--;
12267       if (MovDelay[x][y] <= 0)
12268         RemoveField(x, y);
12269     }
12270
12271     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12272     {
12273       MovDelay[x][y]--;
12274       if (MovDelay[x][y] <= 0)
12275       {
12276         int element = Store[x][y];
12277         int move_direction = MovDir[x][y];
12278         int player_index_bit = Store2[x][y];
12279
12280         Store[x][y] = 0;
12281         Store2[x][y] = 0;
12282
12283         RemoveField(x, y);
12284         TEST_DrawLevelField(x, y);
12285
12286         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12287
12288         if (IS_ENVELOPE(element))
12289           local_player->show_envelope = element;
12290       }
12291     }
12292
12293 #if DEBUG
12294     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12295     {
12296       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12297             x, y);
12298       Debug("game:playing:GameActions_RND", "This should never happen!");
12299
12300       ChangePage[x][y] = -1;
12301     }
12302 #endif
12303
12304     Stop[x][y] = FALSE;
12305     if (WasJustMoving[x][y] > 0)
12306       WasJustMoving[x][y]--;
12307     if (WasJustFalling[x][y] > 0)
12308       WasJustFalling[x][y]--;
12309     if (CheckCollision[x][y] > 0)
12310       CheckCollision[x][y]--;
12311     if (CheckImpact[x][y] > 0)
12312       CheckImpact[x][y]--;
12313
12314     GfxFrame[x][y]++;
12315
12316     /* reset finished pushing action (not done in ContinueMoving() to allow
12317        continuous pushing animation for elements with zero push delay) */
12318     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12319     {
12320       ResetGfxAnimation(x, y);
12321       TEST_DrawLevelField(x, y);
12322     }
12323
12324 #if DEBUG
12325     if (IS_BLOCKED(x, y))
12326     {
12327       int oldx, oldy;
12328
12329       Blocked2Moving(x, y, &oldx, &oldy);
12330       if (!IS_MOVING(oldx, oldy))
12331       {
12332         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12333         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12334         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12335         Debug("game:playing:GameActions_RND", "This should never happen!");
12336       }
12337     }
12338 #endif
12339   }
12340
12341   if (mouse_action.button)
12342   {
12343     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12344     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12345
12346     x = mouse_action.lx;
12347     y = mouse_action.ly;
12348     element = Tile[x][y];
12349
12350     if (new_button)
12351     {
12352       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12353       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12354                                          ch_button);
12355     }
12356
12357     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12358     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12359                                        ch_button);
12360
12361     if (level.use_step_counter)
12362     {
12363       boolean counted_click = FALSE;
12364
12365       // element clicked that can change when clicked/pressed
12366       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12367           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12368            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12369         counted_click = TRUE;
12370
12371       // element clicked that can trigger change when clicked/pressed
12372       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12373           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12374         counted_click = TRUE;
12375
12376       if (new_button && counted_click)
12377         CheckLevelTime_StepCounter();
12378     }
12379   }
12380
12381   SCAN_PLAYFIELD(x, y)
12382   {
12383     element = Tile[x][y];
12384     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12385     last_gfx_frame = GfxFrame[x][y];
12386
12387     if (element == EL_EMPTY)
12388       graphic = el2img(GfxElementEmpty[x][y]);
12389
12390     ResetGfxFrame(x, y);
12391
12392     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12393       DrawLevelGraphicAnimation(x, y, graphic);
12394
12395     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12396         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12397       ResetRandomAnimationValue(x, y);
12398
12399     SetRandomAnimationValue(x, y);
12400
12401     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12402
12403     if (IS_INACTIVE(element))
12404     {
12405       if (IS_ANIMATED(graphic))
12406         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12407
12408       continue;
12409     }
12410
12411     // this may take place after moving, so 'element' may have changed
12412     if (IS_CHANGING(x, y) &&
12413         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12414     {
12415       int page = element_info[element].event_page_nr[CE_DELAY];
12416
12417       HandleElementChange(x, y, page);
12418
12419       element = Tile[x][y];
12420       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12421     }
12422
12423     CheckNextToConditions(x, y);
12424
12425     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12426     {
12427       StartMoving(x, y);
12428
12429       element = Tile[x][y];
12430       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12431
12432       if (IS_ANIMATED(graphic) &&
12433           !IS_MOVING(x, y) &&
12434           !Stop[x][y])
12435         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12436
12437       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12438         TEST_DrawTwinkleOnField(x, y);
12439     }
12440     else if (element == EL_ACID)
12441     {
12442       if (!Stop[x][y])
12443         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12444     }
12445     else if ((element == EL_EXIT_OPEN ||
12446               element == EL_EM_EXIT_OPEN ||
12447               element == EL_SP_EXIT_OPEN ||
12448               element == EL_STEEL_EXIT_OPEN ||
12449               element == EL_EM_STEEL_EXIT_OPEN ||
12450               element == EL_SP_TERMINAL ||
12451               element == EL_SP_TERMINAL_ACTIVE ||
12452               element == EL_EXTRA_TIME ||
12453               element == EL_SHIELD_NORMAL ||
12454               element == EL_SHIELD_DEADLY) &&
12455              IS_ANIMATED(graphic))
12456       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12457     else if (IS_MOVING(x, y))
12458       ContinueMoving(x, y);
12459     else if (IS_ACTIVE_BOMB(element))
12460       CheckDynamite(x, y);
12461     else if (element == EL_AMOEBA_GROWING)
12462       AmoebaGrowing(x, y);
12463     else if (element == EL_AMOEBA_SHRINKING)
12464       AmoebaShrinking(x, y);
12465
12466 #if !USE_NEW_AMOEBA_CODE
12467     else if (IS_AMOEBALIVE(element))
12468       AmoebaReproduce(x, y);
12469 #endif
12470
12471     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12472       Life(x, y);
12473     else if (element == EL_EXIT_CLOSED)
12474       CheckExit(x, y);
12475     else if (element == EL_EM_EXIT_CLOSED)
12476       CheckExitEM(x, y);
12477     else if (element == EL_STEEL_EXIT_CLOSED)
12478       CheckExitSteel(x, y);
12479     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12480       CheckExitSteelEM(x, y);
12481     else if (element == EL_SP_EXIT_CLOSED)
12482       CheckExitSP(x, y);
12483     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12484              element == EL_EXPANDABLE_STEELWALL_GROWING)
12485       MauerWaechst(x, y);
12486     else if (element == EL_EXPANDABLE_WALL ||
12487              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12488              element == EL_EXPANDABLE_WALL_VERTICAL ||
12489              element == EL_EXPANDABLE_WALL_ANY ||
12490              element == EL_BD_EXPANDABLE_WALL)
12491       MauerAbleger(x, y);
12492     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12493              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12494              element == EL_EXPANDABLE_STEELWALL_ANY)
12495       MauerAblegerStahl(x, y);
12496     else if (element == EL_FLAMES)
12497       CheckForDragon(x, y);
12498     else if (element == EL_EXPLOSION)
12499       ; // drawing of correct explosion animation is handled separately
12500     else if (element == EL_ELEMENT_SNAPPING ||
12501              element == EL_DIAGONAL_SHRINKING ||
12502              element == EL_DIAGONAL_GROWING)
12503     {
12504       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12505
12506       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12507     }
12508     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12509       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12510
12511     if (IS_BELT_ACTIVE(element))
12512       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12513
12514     if (game.magic_wall_active)
12515     {
12516       int jx = local_player->jx, jy = local_player->jy;
12517
12518       // play the element sound at the position nearest to the player
12519       if ((element == EL_MAGIC_WALL_FULL ||
12520            element == EL_MAGIC_WALL_ACTIVE ||
12521            element == EL_MAGIC_WALL_EMPTYING ||
12522            element == EL_BD_MAGIC_WALL_FULL ||
12523            element == EL_BD_MAGIC_WALL_ACTIVE ||
12524            element == EL_BD_MAGIC_WALL_EMPTYING ||
12525            element == EL_DC_MAGIC_WALL_FULL ||
12526            element == EL_DC_MAGIC_WALL_ACTIVE ||
12527            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12528           ABS(x - jx) + ABS(y - jy) <
12529           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12530       {
12531         magic_wall_x = x;
12532         magic_wall_y = y;
12533       }
12534     }
12535   }
12536
12537 #if USE_NEW_AMOEBA_CODE
12538   // new experimental amoeba growth stuff
12539   if (!(FrameCounter % 8))
12540   {
12541     static unsigned int random = 1684108901;
12542
12543     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12544     {
12545       x = RND(lev_fieldx);
12546       y = RND(lev_fieldy);
12547       element = Tile[x][y];
12548
12549       if (!IS_PLAYER(x,y) &&
12550           (element == EL_EMPTY ||
12551            CAN_GROW_INTO(element) ||
12552            element == EL_QUICKSAND_EMPTY ||
12553            element == EL_QUICKSAND_FAST_EMPTY ||
12554            element == EL_ACID_SPLASH_LEFT ||
12555            element == EL_ACID_SPLASH_RIGHT))
12556       {
12557         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12558             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12559             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12560             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12561           Tile[x][y] = EL_AMOEBA_DROP;
12562       }
12563
12564       random = random * 129 + 1;
12565     }
12566   }
12567 #endif
12568
12569   game.explosions_delayed = FALSE;
12570
12571   SCAN_PLAYFIELD(x, y)
12572   {
12573     element = Tile[x][y];
12574
12575     if (ExplodeField[x][y])
12576       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12577     else if (element == EL_EXPLOSION)
12578       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12579
12580     ExplodeField[x][y] = EX_TYPE_NONE;
12581   }
12582
12583   game.explosions_delayed = TRUE;
12584
12585   if (game.magic_wall_active)
12586   {
12587     if (!(game.magic_wall_time_left % 4))
12588     {
12589       int element = Tile[magic_wall_x][magic_wall_y];
12590
12591       if (element == EL_BD_MAGIC_WALL_FULL ||
12592           element == EL_BD_MAGIC_WALL_ACTIVE ||
12593           element == EL_BD_MAGIC_WALL_EMPTYING)
12594         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12595       else if (element == EL_DC_MAGIC_WALL_FULL ||
12596                element == EL_DC_MAGIC_WALL_ACTIVE ||
12597                element == EL_DC_MAGIC_WALL_EMPTYING)
12598         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12599       else
12600         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12601     }
12602
12603     if (game.magic_wall_time_left > 0)
12604     {
12605       game.magic_wall_time_left--;
12606
12607       if (!game.magic_wall_time_left)
12608       {
12609         SCAN_PLAYFIELD(x, y)
12610         {
12611           element = Tile[x][y];
12612
12613           if (element == EL_MAGIC_WALL_ACTIVE ||
12614               element == EL_MAGIC_WALL_FULL)
12615           {
12616             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12617             TEST_DrawLevelField(x, y);
12618           }
12619           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12620                    element == EL_BD_MAGIC_WALL_FULL)
12621           {
12622             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12623             TEST_DrawLevelField(x, y);
12624           }
12625           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12626                    element == EL_DC_MAGIC_WALL_FULL)
12627           {
12628             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12629             TEST_DrawLevelField(x, y);
12630           }
12631         }
12632
12633         game.magic_wall_active = FALSE;
12634       }
12635     }
12636   }
12637
12638   if (game.light_time_left > 0)
12639   {
12640     game.light_time_left--;
12641
12642     if (game.light_time_left == 0)
12643       RedrawAllLightSwitchesAndInvisibleElements();
12644   }
12645
12646   if (game.timegate_time_left > 0)
12647   {
12648     game.timegate_time_left--;
12649
12650     if (game.timegate_time_left == 0)
12651       CloseAllOpenTimegates();
12652   }
12653
12654   if (game.lenses_time_left > 0)
12655   {
12656     game.lenses_time_left--;
12657
12658     if (game.lenses_time_left == 0)
12659       RedrawAllInvisibleElementsForLenses();
12660   }
12661
12662   if (game.magnify_time_left > 0)
12663   {
12664     game.magnify_time_left--;
12665
12666     if (game.magnify_time_left == 0)
12667       RedrawAllInvisibleElementsForMagnifier();
12668   }
12669
12670   for (i = 0; i < MAX_PLAYERS; i++)
12671   {
12672     struct PlayerInfo *player = &stored_player[i];
12673
12674     if (SHIELD_ON(player))
12675     {
12676       if (player->shield_deadly_time_left)
12677         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12678       else if (player->shield_normal_time_left)
12679         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12680     }
12681   }
12682
12683 #if USE_DELAYED_GFX_REDRAW
12684   SCAN_PLAYFIELD(x, y)
12685   {
12686     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12687     {
12688       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12689          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12690
12691       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12692         DrawLevelField(x, y);
12693
12694       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12695         DrawLevelFieldCrumbled(x, y);
12696
12697       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12698         DrawLevelFieldCrumbledNeighbours(x, y);
12699
12700       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12701         DrawTwinkleOnField(x, y);
12702     }
12703
12704     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12705   }
12706 #endif
12707
12708   DrawAllPlayers();
12709   PlayAllPlayersSound();
12710
12711   for (i = 0; i < MAX_PLAYERS; i++)
12712   {
12713     struct PlayerInfo *player = &stored_player[i];
12714
12715     if (player->show_envelope != 0 && (!player->active ||
12716                                        player->MovPos == 0))
12717     {
12718       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12719
12720       player->show_envelope = 0;
12721     }
12722   }
12723
12724   // use random number generator in every frame to make it less predictable
12725   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12726     RND(1);
12727
12728   mouse_action_last = mouse_action;
12729 }
12730
12731 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12732 {
12733   int min_x = x, min_y = y, max_x = x, max_y = y;
12734   int scr_fieldx = getScreenFieldSizeX();
12735   int scr_fieldy = getScreenFieldSizeY();
12736   int i;
12737
12738   for (i = 0; i < MAX_PLAYERS; i++)
12739   {
12740     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12741
12742     if (!stored_player[i].active || &stored_player[i] == player)
12743       continue;
12744
12745     min_x = MIN(min_x, jx);
12746     min_y = MIN(min_y, jy);
12747     max_x = MAX(max_x, jx);
12748     max_y = MAX(max_y, jy);
12749   }
12750
12751   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12752 }
12753
12754 static boolean AllPlayersInVisibleScreen(void)
12755 {
12756   int i;
12757
12758   for (i = 0; i < MAX_PLAYERS; i++)
12759   {
12760     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12761
12762     if (!stored_player[i].active)
12763       continue;
12764
12765     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12766       return FALSE;
12767   }
12768
12769   return TRUE;
12770 }
12771
12772 void ScrollLevel(int dx, int dy)
12773 {
12774   int scroll_offset = 2 * TILEX_VAR;
12775   int x, y;
12776
12777   BlitBitmap(drawto_field, drawto_field,
12778              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12779              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12780              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12781              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12782              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12783              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12784
12785   if (dx != 0)
12786   {
12787     x = (dx == 1 ? BX1 : BX2);
12788     for (y = BY1; y <= BY2; y++)
12789       DrawScreenField(x, y);
12790   }
12791
12792   if (dy != 0)
12793   {
12794     y = (dy == 1 ? BY1 : BY2);
12795     for (x = BX1; x <= BX2; x++)
12796       DrawScreenField(x, y);
12797   }
12798
12799   redraw_mask |= REDRAW_FIELD;
12800 }
12801
12802 static boolean canFallDown(struct PlayerInfo *player)
12803 {
12804   int jx = player->jx, jy = player->jy;
12805
12806   return (IN_LEV_FIELD(jx, jy + 1) &&
12807           (IS_FREE(jx, jy + 1) ||
12808            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12809           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12810           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12811 }
12812
12813 static boolean canPassField(int x, int y, int move_dir)
12814 {
12815   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12816   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12817   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12818   int nextx = x + dx;
12819   int nexty = y + dy;
12820   int element = Tile[x][y];
12821
12822   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12823           !CAN_MOVE(element) &&
12824           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12825           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12826           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12827 }
12828
12829 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12830 {
12831   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12832   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12833   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12834   int newx = x + dx;
12835   int newy = y + dy;
12836
12837   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12838           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12839           (IS_DIGGABLE(Tile[newx][newy]) ||
12840            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12841            canPassField(newx, newy, move_dir)));
12842 }
12843
12844 static void CheckGravityMovement(struct PlayerInfo *player)
12845 {
12846   if (player->gravity && !player->programmed_action)
12847   {
12848     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12849     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12850     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12851     int jx = player->jx, jy = player->jy;
12852     boolean player_is_moving_to_valid_field =
12853       (!player_is_snapping &&
12854        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12855         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12856     boolean player_can_fall_down = canFallDown(player);
12857
12858     if (player_can_fall_down &&
12859         !player_is_moving_to_valid_field)
12860       player->programmed_action = MV_DOWN;
12861   }
12862 }
12863
12864 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12865 {
12866   return CheckGravityMovement(player);
12867
12868   if (player->gravity && !player->programmed_action)
12869   {
12870     int jx = player->jx, jy = player->jy;
12871     boolean field_under_player_is_free =
12872       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12873     boolean player_is_standing_on_valid_field =
12874       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12875        (IS_WALKABLE(Tile[jx][jy]) &&
12876         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12877
12878     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12879       player->programmed_action = MV_DOWN;
12880   }
12881 }
12882
12883 /*
12884   MovePlayerOneStep()
12885   -----------------------------------------------------------------------------
12886   dx, dy:               direction (non-diagonal) to try to move the player to
12887   real_dx, real_dy:     direction as read from input device (can be diagonal)
12888 */
12889
12890 boolean MovePlayerOneStep(struct PlayerInfo *player,
12891                           int dx, int dy, int real_dx, int real_dy)
12892 {
12893   int jx = player->jx, jy = player->jy;
12894   int new_jx = jx + dx, new_jy = jy + dy;
12895   int can_move;
12896   boolean player_can_move = !player->cannot_move;
12897
12898   if (!player->active || (!dx && !dy))
12899     return MP_NO_ACTION;
12900
12901   player->MovDir = (dx < 0 ? MV_LEFT :
12902                     dx > 0 ? MV_RIGHT :
12903                     dy < 0 ? MV_UP :
12904                     dy > 0 ? MV_DOWN :  MV_NONE);
12905
12906   if (!IN_LEV_FIELD(new_jx, new_jy))
12907     return MP_NO_ACTION;
12908
12909   if (!player_can_move)
12910   {
12911     if (player->MovPos == 0)
12912     {
12913       player->is_moving = FALSE;
12914       player->is_digging = FALSE;
12915       player->is_collecting = FALSE;
12916       player->is_snapping = FALSE;
12917       player->is_pushing = FALSE;
12918     }
12919   }
12920
12921   if (!network.enabled && game.centered_player_nr == -1 &&
12922       !AllPlayersInSight(player, new_jx, new_jy))
12923     return MP_NO_ACTION;
12924
12925   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12926   if (can_move != MP_MOVING)
12927     return can_move;
12928
12929   // check if DigField() has caused relocation of the player
12930   if (player->jx != jx || player->jy != jy)
12931     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12932
12933   StorePlayer[jx][jy] = 0;
12934   player->last_jx = jx;
12935   player->last_jy = jy;
12936   player->jx = new_jx;
12937   player->jy = new_jy;
12938   StorePlayer[new_jx][new_jy] = player->element_nr;
12939
12940   if (player->move_delay_value_next != -1)
12941   {
12942     player->move_delay_value = player->move_delay_value_next;
12943     player->move_delay_value_next = -1;
12944   }
12945
12946   player->MovPos =
12947     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12948
12949   player->step_counter++;
12950
12951   PlayerVisit[jx][jy] = FrameCounter;
12952
12953   player->is_moving = TRUE;
12954
12955 #if 1
12956   // should better be called in MovePlayer(), but this breaks some tapes
12957   ScrollPlayer(player, SCROLL_INIT);
12958 #endif
12959
12960   return MP_MOVING;
12961 }
12962
12963 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12964 {
12965   int jx = player->jx, jy = player->jy;
12966   int old_jx = jx, old_jy = jy;
12967   int moved = MP_NO_ACTION;
12968
12969   if (!player->active)
12970     return FALSE;
12971
12972   if (!dx && !dy)
12973   {
12974     if (player->MovPos == 0)
12975     {
12976       player->is_moving = FALSE;
12977       player->is_digging = FALSE;
12978       player->is_collecting = FALSE;
12979       player->is_snapping = FALSE;
12980       player->is_pushing = FALSE;
12981     }
12982
12983     return FALSE;
12984   }
12985
12986   if (player->move_delay > 0)
12987     return FALSE;
12988
12989   player->move_delay = -1;              // set to "uninitialized" value
12990
12991   // store if player is automatically moved to next field
12992   player->is_auto_moving = (player->programmed_action != MV_NONE);
12993
12994   // remove the last programmed player action
12995   player->programmed_action = 0;
12996
12997   if (player->MovPos)
12998   {
12999     // should only happen if pre-1.2 tape recordings are played
13000     // this is only for backward compatibility
13001
13002     int original_move_delay_value = player->move_delay_value;
13003
13004 #if DEBUG
13005     Debug("game:playing:MovePlayer",
13006           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13007           tape.counter);
13008 #endif
13009
13010     // scroll remaining steps with finest movement resolution
13011     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13012
13013     while (player->MovPos)
13014     {
13015       ScrollPlayer(player, SCROLL_GO_ON);
13016       ScrollScreen(NULL, SCROLL_GO_ON);
13017
13018       AdvanceFrameAndPlayerCounters(player->index_nr);
13019
13020       DrawAllPlayers();
13021       BackToFront_WithFrameDelay(0);
13022     }
13023
13024     player->move_delay_value = original_move_delay_value;
13025   }
13026
13027   player->is_active = FALSE;
13028
13029   if (player->last_move_dir & MV_HORIZONTAL)
13030   {
13031     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13032       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13033   }
13034   else
13035   {
13036     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13037       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13038   }
13039
13040   if (!moved && !player->is_active)
13041   {
13042     player->is_moving = FALSE;
13043     player->is_digging = FALSE;
13044     player->is_collecting = FALSE;
13045     player->is_snapping = FALSE;
13046     player->is_pushing = FALSE;
13047   }
13048
13049   jx = player->jx;
13050   jy = player->jy;
13051
13052   if (moved & MP_MOVING && !ScreenMovPos &&
13053       (player->index_nr == game.centered_player_nr ||
13054        game.centered_player_nr == -1))
13055   {
13056     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13057
13058     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13059     {
13060       // actual player has left the screen -- scroll in that direction
13061       if (jx != old_jx)         // player has moved horizontally
13062         scroll_x += (jx - old_jx);
13063       else                      // player has moved vertically
13064         scroll_y += (jy - old_jy);
13065     }
13066     else
13067     {
13068       int offset_raw = game.scroll_delay_value;
13069
13070       if (jx != old_jx)         // player has moved horizontally
13071       {
13072         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13073         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13074         int new_scroll_x = jx - MIDPOSX + offset_x;
13075
13076         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13077             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13078           scroll_x = new_scroll_x;
13079
13080         // don't scroll over playfield boundaries
13081         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13082
13083         // don't scroll more than one field at a time
13084         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13085
13086         // don't scroll against the player's moving direction
13087         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13088             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13089           scroll_x = old_scroll_x;
13090       }
13091       else                      // player has moved vertically
13092       {
13093         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13094         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13095         int new_scroll_y = jy - MIDPOSY + offset_y;
13096
13097         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13098             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13099           scroll_y = new_scroll_y;
13100
13101         // don't scroll over playfield boundaries
13102         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13103
13104         // don't scroll more than one field at a time
13105         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13106
13107         // don't scroll against the player's moving direction
13108         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13109             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13110           scroll_y = old_scroll_y;
13111       }
13112     }
13113
13114     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13115     {
13116       if (!network.enabled && game.centered_player_nr == -1 &&
13117           !AllPlayersInVisibleScreen())
13118       {
13119         scroll_x = old_scroll_x;
13120         scroll_y = old_scroll_y;
13121       }
13122       else
13123       {
13124         ScrollScreen(player, SCROLL_INIT);
13125         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13126       }
13127     }
13128   }
13129
13130   player->StepFrame = 0;
13131
13132   if (moved & MP_MOVING)
13133   {
13134     if (old_jx != jx && old_jy == jy)
13135       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13136     else if (old_jx == jx && old_jy != jy)
13137       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13138
13139     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13140
13141     player->last_move_dir = player->MovDir;
13142     player->is_moving = TRUE;
13143     player->is_snapping = FALSE;
13144     player->is_switching = FALSE;
13145     player->is_dropping = FALSE;
13146     player->is_dropping_pressed = FALSE;
13147     player->drop_pressed_delay = 0;
13148
13149 #if 0
13150     // should better be called here than above, but this breaks some tapes
13151     ScrollPlayer(player, SCROLL_INIT);
13152 #endif
13153   }
13154   else
13155   {
13156     CheckGravityMovementWhenNotMoving(player);
13157
13158     player->is_moving = FALSE;
13159
13160     /* at this point, the player is allowed to move, but cannot move right now
13161        (e.g. because of something blocking the way) -- ensure that the player
13162        is also allowed to move in the next frame (in old versions before 3.1.1,
13163        the player was forced to wait again for eight frames before next try) */
13164
13165     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13166       player->move_delay = 0;   // allow direct movement in the next frame
13167   }
13168
13169   if (player->move_delay == -1)         // not yet initialized by DigField()
13170     player->move_delay = player->move_delay_value;
13171
13172   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13173   {
13174     TestIfPlayerTouchesBadThing(jx, jy);
13175     TestIfPlayerTouchesCustomElement(jx, jy);
13176   }
13177
13178   if (!player->active)
13179     RemovePlayer(player);
13180
13181   return moved;
13182 }
13183
13184 void ScrollPlayer(struct PlayerInfo *player, int mode)
13185 {
13186   int jx = player->jx, jy = player->jy;
13187   int last_jx = player->last_jx, last_jy = player->last_jy;
13188   int move_stepsize = TILEX / player->move_delay_value;
13189
13190   if (!player->active)
13191     return;
13192
13193   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13194     return;
13195
13196   if (mode == SCROLL_INIT)
13197   {
13198     player->actual_frame_counter.count = FrameCounter;
13199     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13200
13201     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13202         Tile[last_jx][last_jy] == EL_EMPTY)
13203     {
13204       int last_field_block_delay = 0;   // start with no blocking at all
13205       int block_delay_adjustment = player->block_delay_adjustment;
13206
13207       // if player blocks last field, add delay for exactly one move
13208       if (player->block_last_field)
13209       {
13210         last_field_block_delay += player->move_delay_value;
13211
13212         // when blocking enabled, prevent moving up despite gravity
13213         if (player->gravity && player->MovDir == MV_UP)
13214           block_delay_adjustment = -1;
13215       }
13216
13217       // add block delay adjustment (also possible when not blocking)
13218       last_field_block_delay += block_delay_adjustment;
13219
13220       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13221       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13222     }
13223
13224     if (player->MovPos != 0)    // player has not yet reached destination
13225       return;
13226   }
13227   else if (!FrameReached(&player->actual_frame_counter))
13228     return;
13229
13230   if (player->MovPos != 0)
13231   {
13232     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13233     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13234
13235     // before DrawPlayer() to draw correct player graphic for this case
13236     if (player->MovPos == 0)
13237       CheckGravityMovement(player);
13238   }
13239
13240   if (player->MovPos == 0)      // player reached destination field
13241   {
13242     if (player->move_delay_reset_counter > 0)
13243     {
13244       player->move_delay_reset_counter--;
13245
13246       if (player->move_delay_reset_counter == 0)
13247       {
13248         // continue with normal speed after quickly moving through gate
13249         HALVE_PLAYER_SPEED(player);
13250
13251         // be able to make the next move without delay
13252         player->move_delay = 0;
13253       }
13254     }
13255
13256     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13257         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13258         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13259         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13260         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13261         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13262         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13263         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13264     {
13265       ExitPlayer(player);
13266
13267       if (game.players_still_needed == 0 &&
13268           (game.friends_still_needed == 0 ||
13269            IS_SP_ELEMENT(Tile[jx][jy])))
13270         LevelSolved();
13271     }
13272
13273     player->last_jx = jx;
13274     player->last_jy = jy;
13275
13276     // this breaks one level: "machine", level 000
13277     {
13278       int move_direction = player->MovDir;
13279       int enter_side = MV_DIR_OPPOSITE(move_direction);
13280       int leave_side = move_direction;
13281       int old_jx = last_jx;
13282       int old_jy = last_jy;
13283       int old_element = Tile[old_jx][old_jy];
13284       int new_element = Tile[jx][jy];
13285
13286       if (IS_CUSTOM_ELEMENT(old_element))
13287         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13288                                    CE_LEFT_BY_PLAYER,
13289                                    player->index_bit, leave_side);
13290
13291       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13292                                           CE_PLAYER_LEAVES_X,
13293                                           player->index_bit, leave_side);
13294
13295       if (IS_CUSTOM_ELEMENT(new_element))
13296         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13297                                    player->index_bit, enter_side);
13298
13299       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13300                                           CE_PLAYER_ENTERS_X,
13301                                           player->index_bit, enter_side);
13302
13303       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13304                                         CE_MOVE_OF_X, move_direction);
13305     }
13306
13307     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13308     {
13309       TestIfPlayerTouchesBadThing(jx, jy);
13310       TestIfPlayerTouchesCustomElement(jx, jy);
13311
13312       /* needed because pushed element has not yet reached its destination,
13313          so it would trigger a change event at its previous field location */
13314       if (!player->is_pushing)
13315         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13316
13317       if (level.finish_dig_collect &&
13318           (player->is_digging || player->is_collecting))
13319       {
13320         int last_element = player->last_removed_element;
13321         int move_direction = player->MovDir;
13322         int enter_side = MV_DIR_OPPOSITE(move_direction);
13323         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13324                             CE_PLAYER_COLLECTS_X);
13325
13326         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13327                                             player->index_bit, enter_side);
13328
13329         player->last_removed_element = EL_UNDEFINED;
13330       }
13331
13332       if (!player->active)
13333         RemovePlayer(player);
13334     }
13335
13336     if (level.use_step_counter)
13337       CheckLevelTime_StepCounter();
13338
13339     if (tape.single_step && tape.recording && !tape.pausing &&
13340         !player->programmed_action)
13341       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13342
13343     if (!player->programmed_action)
13344       CheckSaveEngineSnapshot(player);
13345   }
13346 }
13347
13348 void ScrollScreen(struct PlayerInfo *player, int mode)
13349 {
13350   static DelayCounter screen_frame_counter = { 0 };
13351
13352   if (mode == SCROLL_INIT)
13353   {
13354     // set scrolling step size according to actual player's moving speed
13355     ScrollStepSize = TILEX / player->move_delay_value;
13356
13357     screen_frame_counter.count = FrameCounter;
13358     screen_frame_counter.value = 1;
13359
13360     ScreenMovDir = player->MovDir;
13361     ScreenMovPos = player->MovPos;
13362     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13363     return;
13364   }
13365   else if (!FrameReached(&screen_frame_counter))
13366     return;
13367
13368   if (ScreenMovPos)
13369   {
13370     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13371     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13372     redraw_mask |= REDRAW_FIELD;
13373   }
13374   else
13375     ScreenMovDir = MV_NONE;
13376 }
13377
13378 void CheckNextToConditions(int x, int y)
13379 {
13380   int element = Tile[x][y];
13381
13382   if (IS_PLAYER(x, y))
13383     TestIfPlayerNextToCustomElement(x, y);
13384
13385   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13386       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13387     TestIfElementNextToCustomElement(x, y);
13388 }
13389
13390 void TestIfPlayerNextToCustomElement(int x, int y)
13391 {
13392   static int xy[4][2] =
13393   {
13394     { 0, -1 },
13395     { -1, 0 },
13396     { +1, 0 },
13397     { 0, +1 }
13398   };
13399   static int trigger_sides[4][2] =
13400   {
13401     // center side       border side
13402     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13403     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13404     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13405     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13406   };
13407   int i;
13408
13409   if (!IS_PLAYER(x, y))
13410     return;
13411
13412   struct PlayerInfo *player = PLAYERINFO(x, y);
13413
13414   if (player->is_moving)
13415     return;
13416
13417   for (i = 0; i < NUM_DIRECTIONS; i++)
13418   {
13419     int xx = x + xy[i][0];
13420     int yy = y + xy[i][1];
13421     int border_side = trigger_sides[i][1];
13422     int border_element;
13423
13424     if (!IN_LEV_FIELD(xx, yy))
13425       continue;
13426
13427     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13428       continue;         // center and border element not connected
13429
13430     border_element = Tile[xx][yy];
13431
13432     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13433                                player->index_bit, border_side);
13434     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13435                                         CE_PLAYER_NEXT_TO_X,
13436                                         player->index_bit, border_side);
13437
13438     /* use player element that is initially defined in the level playfield,
13439        not the player element that corresponds to the runtime player number
13440        (example: a level that contains EL_PLAYER_3 as the only player would
13441        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13442
13443     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13444                              CE_NEXT_TO_X, border_side);
13445   }
13446 }
13447
13448 void TestIfPlayerTouchesCustomElement(int x, int y)
13449 {
13450   static int xy[4][2] =
13451   {
13452     { 0, -1 },
13453     { -1, 0 },
13454     { +1, 0 },
13455     { 0, +1 }
13456   };
13457   static int trigger_sides[4][2] =
13458   {
13459     // center side       border side
13460     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13461     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13462     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13463     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13464   };
13465   static int touch_dir[4] =
13466   {
13467     MV_LEFT | MV_RIGHT,
13468     MV_UP   | MV_DOWN,
13469     MV_UP   | MV_DOWN,
13470     MV_LEFT | MV_RIGHT
13471   };
13472   int center_element = Tile[x][y];      // should always be non-moving!
13473   int i;
13474
13475   for (i = 0; i < NUM_DIRECTIONS; i++)
13476   {
13477     int xx = x + xy[i][0];
13478     int yy = y + xy[i][1];
13479     int center_side = trigger_sides[i][0];
13480     int border_side = trigger_sides[i][1];
13481     int border_element;
13482
13483     if (!IN_LEV_FIELD(xx, yy))
13484       continue;
13485
13486     if (IS_PLAYER(x, y))                // player found at center element
13487     {
13488       struct PlayerInfo *player = PLAYERINFO(x, y);
13489
13490       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13491         border_element = Tile[xx][yy];          // may be moving!
13492       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13493         border_element = Tile[xx][yy];
13494       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13495         border_element = MovingOrBlocked2Element(xx, yy);
13496       else
13497         continue;               // center and border element do not touch
13498
13499       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13500                                  player->index_bit, border_side);
13501       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13502                                           CE_PLAYER_TOUCHES_X,
13503                                           player->index_bit, border_side);
13504
13505       {
13506         /* use player element that is initially defined in the level playfield,
13507            not the player element that corresponds to the runtime player number
13508            (example: a level that contains EL_PLAYER_3 as the only player would
13509            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13510         int player_element = PLAYERINFO(x, y)->initial_element;
13511
13512         CheckElementChangeBySide(xx, yy, border_element, player_element,
13513                                  CE_TOUCHING_X, border_side);
13514       }
13515     }
13516     else if (IS_PLAYER(xx, yy))         // player found at border element
13517     {
13518       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13519
13520       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13521       {
13522         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13523           continue;             // center and border element do not touch
13524       }
13525
13526       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13527                                  player->index_bit, center_side);
13528       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13529                                           CE_PLAYER_TOUCHES_X,
13530                                           player->index_bit, center_side);
13531
13532       {
13533         /* use player element that is initially defined in the level playfield,
13534            not the player element that corresponds to the runtime player number
13535            (example: a level that contains EL_PLAYER_3 as the only player would
13536            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13537         int player_element = PLAYERINFO(xx, yy)->initial_element;
13538
13539         CheckElementChangeBySide(x, y, center_element, player_element,
13540                                  CE_TOUCHING_X, center_side);
13541       }
13542
13543       break;
13544     }
13545   }
13546 }
13547
13548 void TestIfElementNextToCustomElement(int x, int y)
13549 {
13550   static int xy[4][2] =
13551   {
13552     { 0, -1 },
13553     { -1, 0 },
13554     { +1, 0 },
13555     { 0, +1 }
13556   };
13557   static int trigger_sides[4][2] =
13558   {
13559     // center side      border side
13560     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13561     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13562     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13563     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13564   };
13565   int center_element = Tile[x][y];      // should always be non-moving!
13566   int i;
13567
13568   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13569     return;
13570
13571   for (i = 0; i < NUM_DIRECTIONS; i++)
13572   {
13573     int xx = x + xy[i][0];
13574     int yy = y + xy[i][1];
13575     int border_side = trigger_sides[i][1];
13576     int border_element;
13577
13578     if (!IN_LEV_FIELD(xx, yy))
13579       continue;
13580
13581     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13582       continue;                 // center and border element not connected
13583
13584     border_element = Tile[xx][yy];
13585
13586     // check for change of center element (but change it only once)
13587     if (CheckElementChangeBySide(x, y, center_element, border_element,
13588                                  CE_NEXT_TO_X, border_side))
13589       break;
13590   }
13591 }
13592
13593 void TestIfElementTouchesCustomElement(int x, int y)
13594 {
13595   static int xy[4][2] =
13596   {
13597     { 0, -1 },
13598     { -1, 0 },
13599     { +1, 0 },
13600     { 0, +1 }
13601   };
13602   static int trigger_sides[4][2] =
13603   {
13604     // center side      border side
13605     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13606     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13607     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13608     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13609   };
13610   static int touch_dir[4] =
13611   {
13612     MV_LEFT | MV_RIGHT,
13613     MV_UP   | MV_DOWN,
13614     MV_UP   | MV_DOWN,
13615     MV_LEFT | MV_RIGHT
13616   };
13617   boolean change_center_element = FALSE;
13618   int center_element = Tile[x][y];      // should always be non-moving!
13619   int border_element_old[NUM_DIRECTIONS];
13620   int i;
13621
13622   for (i = 0; i < NUM_DIRECTIONS; i++)
13623   {
13624     int xx = x + xy[i][0];
13625     int yy = y + xy[i][1];
13626     int border_element;
13627
13628     border_element_old[i] = -1;
13629
13630     if (!IN_LEV_FIELD(xx, yy))
13631       continue;
13632
13633     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13634       border_element = Tile[xx][yy];    // may be moving!
13635     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13636       border_element = Tile[xx][yy];
13637     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13638       border_element = MovingOrBlocked2Element(xx, yy);
13639     else
13640       continue;                 // center and border element do not touch
13641
13642     border_element_old[i] = border_element;
13643   }
13644
13645   for (i = 0; i < NUM_DIRECTIONS; i++)
13646   {
13647     int xx = x + xy[i][0];
13648     int yy = y + xy[i][1];
13649     int center_side = trigger_sides[i][0];
13650     int border_element = border_element_old[i];
13651
13652     if (border_element == -1)
13653       continue;
13654
13655     // check for change of border element
13656     CheckElementChangeBySide(xx, yy, border_element, center_element,
13657                              CE_TOUCHING_X, center_side);
13658
13659     // (center element cannot be player, so we dont have to check this here)
13660   }
13661
13662   for (i = 0; i < NUM_DIRECTIONS; i++)
13663   {
13664     int xx = x + xy[i][0];
13665     int yy = y + xy[i][1];
13666     int border_side = trigger_sides[i][1];
13667     int border_element = border_element_old[i];
13668
13669     if (border_element == -1)
13670       continue;
13671
13672     // check for change of center element (but change it only once)
13673     if (!change_center_element)
13674       change_center_element =
13675         CheckElementChangeBySide(x, y, center_element, border_element,
13676                                  CE_TOUCHING_X, border_side);
13677
13678     if (IS_PLAYER(xx, yy))
13679     {
13680       /* use player element that is initially defined in the level playfield,
13681          not the player element that corresponds to the runtime player number
13682          (example: a level that contains EL_PLAYER_3 as the only player would
13683          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13684       int player_element = PLAYERINFO(xx, yy)->initial_element;
13685
13686       CheckElementChangeBySide(x, y, center_element, player_element,
13687                                CE_TOUCHING_X, border_side);
13688     }
13689   }
13690 }
13691
13692 void TestIfElementHitsCustomElement(int x, int y, int direction)
13693 {
13694   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13695   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13696   int hitx = x + dx, hity = y + dy;
13697   int hitting_element = Tile[x][y];
13698   int touched_element;
13699
13700   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13701     return;
13702
13703   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13704                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13705
13706   if (IN_LEV_FIELD(hitx, hity))
13707   {
13708     int opposite_direction = MV_DIR_OPPOSITE(direction);
13709     int hitting_side = direction;
13710     int touched_side = opposite_direction;
13711     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13712                           MovDir[hitx][hity] != direction ||
13713                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13714
13715     object_hit = TRUE;
13716
13717     if (object_hit)
13718     {
13719       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13720                                CE_HITTING_X, touched_side);
13721
13722       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13723                                CE_HIT_BY_X, hitting_side);
13724
13725       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13726                                CE_HIT_BY_SOMETHING, opposite_direction);
13727
13728       if (IS_PLAYER(hitx, hity))
13729       {
13730         /* use player element that is initially defined in the level playfield,
13731            not the player element that corresponds to the runtime player number
13732            (example: a level that contains EL_PLAYER_3 as the only player would
13733            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13734         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13735
13736         CheckElementChangeBySide(x, y, hitting_element, player_element,
13737                                  CE_HITTING_X, touched_side);
13738       }
13739     }
13740   }
13741
13742   // "hitting something" is also true when hitting the playfield border
13743   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13744                            CE_HITTING_SOMETHING, direction);
13745 }
13746
13747 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13748 {
13749   int i, kill_x = -1, kill_y = -1;
13750
13751   int bad_element = -1;
13752   static int test_xy[4][2] =
13753   {
13754     { 0, -1 },
13755     { -1, 0 },
13756     { +1, 0 },
13757     { 0, +1 }
13758   };
13759   static int test_dir[4] =
13760   {
13761     MV_UP,
13762     MV_LEFT,
13763     MV_RIGHT,
13764     MV_DOWN
13765   };
13766
13767   for (i = 0; i < NUM_DIRECTIONS; i++)
13768   {
13769     int test_x, test_y, test_move_dir, test_element;
13770
13771     test_x = good_x + test_xy[i][0];
13772     test_y = good_y + test_xy[i][1];
13773
13774     if (!IN_LEV_FIELD(test_x, test_y))
13775       continue;
13776
13777     test_move_dir =
13778       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13779
13780     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13781
13782     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13783        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13784     */
13785     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13786         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13787     {
13788       kill_x = test_x;
13789       kill_y = test_y;
13790       bad_element = test_element;
13791
13792       break;
13793     }
13794   }
13795
13796   if (kill_x != -1 || kill_y != -1)
13797   {
13798     if (IS_PLAYER(good_x, good_y))
13799     {
13800       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13801
13802       if (player->shield_deadly_time_left > 0 &&
13803           !IS_INDESTRUCTIBLE(bad_element))
13804         Bang(kill_x, kill_y);
13805       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13806         KillPlayer(player);
13807     }
13808     else
13809       Bang(good_x, good_y);
13810   }
13811 }
13812
13813 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13814 {
13815   int i, kill_x = -1, kill_y = -1;
13816   int bad_element = Tile[bad_x][bad_y];
13817   static int test_xy[4][2] =
13818   {
13819     { 0, -1 },
13820     { -1, 0 },
13821     { +1, 0 },
13822     { 0, +1 }
13823   };
13824   static int touch_dir[4] =
13825   {
13826     MV_LEFT | MV_RIGHT,
13827     MV_UP   | MV_DOWN,
13828     MV_UP   | MV_DOWN,
13829     MV_LEFT | MV_RIGHT
13830   };
13831   static int test_dir[4] =
13832   {
13833     MV_UP,
13834     MV_LEFT,
13835     MV_RIGHT,
13836     MV_DOWN
13837   };
13838
13839   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13840     return;
13841
13842   for (i = 0; i < NUM_DIRECTIONS; i++)
13843   {
13844     int test_x, test_y, test_move_dir, test_element;
13845
13846     test_x = bad_x + test_xy[i][0];
13847     test_y = bad_y + test_xy[i][1];
13848
13849     if (!IN_LEV_FIELD(test_x, test_y))
13850       continue;
13851
13852     test_move_dir =
13853       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13854
13855     test_element = Tile[test_x][test_y];
13856
13857     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13858        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13859     */
13860     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13861         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13862     {
13863       // good thing is player or penguin that does not move away
13864       if (IS_PLAYER(test_x, test_y))
13865       {
13866         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13867
13868         if (bad_element == EL_ROBOT && player->is_moving)
13869           continue;     // robot does not kill player if he is moving
13870
13871         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13872         {
13873           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13874             continue;           // center and border element do not touch
13875         }
13876
13877         kill_x = test_x;
13878         kill_y = test_y;
13879
13880         break;
13881       }
13882       else if (test_element == EL_PENGUIN)
13883       {
13884         kill_x = test_x;
13885         kill_y = test_y;
13886
13887         break;
13888       }
13889     }
13890   }
13891
13892   if (kill_x != -1 || kill_y != -1)
13893   {
13894     if (IS_PLAYER(kill_x, kill_y))
13895     {
13896       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13897
13898       if (player->shield_deadly_time_left > 0 &&
13899           !IS_INDESTRUCTIBLE(bad_element))
13900         Bang(bad_x, bad_y);
13901       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13902         KillPlayer(player);
13903     }
13904     else
13905       Bang(kill_x, kill_y);
13906   }
13907 }
13908
13909 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13910 {
13911   int bad_element = Tile[bad_x][bad_y];
13912   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13913   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13914   int test_x = bad_x + dx, test_y = bad_y + dy;
13915   int test_move_dir, test_element;
13916   int kill_x = -1, kill_y = -1;
13917
13918   if (!IN_LEV_FIELD(test_x, test_y))
13919     return;
13920
13921   test_move_dir =
13922     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13923
13924   test_element = Tile[test_x][test_y];
13925
13926   if (test_move_dir != bad_move_dir)
13927   {
13928     // good thing can be player or penguin that does not move away
13929     if (IS_PLAYER(test_x, test_y))
13930     {
13931       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13932
13933       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13934          player as being hit when he is moving towards the bad thing, because
13935          the "get hit by" condition would be lost after the player stops) */
13936       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13937         return;         // player moves away from bad thing
13938
13939       kill_x = test_x;
13940       kill_y = test_y;
13941     }
13942     else if (test_element == EL_PENGUIN)
13943     {
13944       kill_x = test_x;
13945       kill_y = test_y;
13946     }
13947   }
13948
13949   if (kill_x != -1 || kill_y != -1)
13950   {
13951     if (IS_PLAYER(kill_x, kill_y))
13952     {
13953       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13954
13955       if (player->shield_deadly_time_left > 0 &&
13956           !IS_INDESTRUCTIBLE(bad_element))
13957         Bang(bad_x, bad_y);
13958       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13959         KillPlayer(player);
13960     }
13961     else
13962       Bang(kill_x, kill_y);
13963   }
13964 }
13965
13966 void TestIfPlayerTouchesBadThing(int x, int y)
13967 {
13968   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13969 }
13970
13971 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13972 {
13973   TestIfGoodThingHitsBadThing(x, y, move_dir);
13974 }
13975
13976 void TestIfBadThingTouchesPlayer(int x, int y)
13977 {
13978   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13979 }
13980
13981 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13982 {
13983   TestIfBadThingHitsGoodThing(x, y, move_dir);
13984 }
13985
13986 void TestIfFriendTouchesBadThing(int x, int y)
13987 {
13988   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13989 }
13990
13991 void TestIfBadThingTouchesFriend(int x, int y)
13992 {
13993   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13994 }
13995
13996 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13997 {
13998   int i, kill_x = bad_x, kill_y = bad_y;
13999   static int xy[4][2] =
14000   {
14001     { 0, -1 },
14002     { -1, 0 },
14003     { +1, 0 },
14004     { 0, +1 }
14005   };
14006
14007   for (i = 0; i < NUM_DIRECTIONS; i++)
14008   {
14009     int x, y, element;
14010
14011     x = bad_x + xy[i][0];
14012     y = bad_y + xy[i][1];
14013     if (!IN_LEV_FIELD(x, y))
14014       continue;
14015
14016     element = Tile[x][y];
14017     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14018         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14019     {
14020       kill_x = x;
14021       kill_y = y;
14022       break;
14023     }
14024   }
14025
14026   if (kill_x != bad_x || kill_y != bad_y)
14027     Bang(bad_x, bad_y);
14028 }
14029
14030 void KillPlayer(struct PlayerInfo *player)
14031 {
14032   int jx = player->jx, jy = player->jy;
14033
14034   if (!player->active)
14035     return;
14036
14037 #if 0
14038   Debug("game:playing:KillPlayer",
14039         "0: killed == %d, active == %d, reanimated == %d",
14040         player->killed, player->active, player->reanimated);
14041 #endif
14042
14043   /* the following code was introduced to prevent an infinite loop when calling
14044      -> Bang()
14045      -> CheckTriggeredElementChangeExt()
14046      -> ExecuteCustomElementAction()
14047      -> KillPlayer()
14048      -> (infinitely repeating the above sequence of function calls)
14049      which occurs when killing the player while having a CE with the setting
14050      "kill player X when explosion of <player X>"; the solution using a new
14051      field "player->killed" was chosen for backwards compatibility, although
14052      clever use of the fields "player->active" etc. would probably also work */
14053 #if 1
14054   if (player->killed)
14055     return;
14056 #endif
14057
14058   player->killed = TRUE;
14059
14060   // remove accessible field at the player's position
14061   Tile[jx][jy] = EL_EMPTY;
14062
14063   // deactivate shield (else Bang()/Explode() would not work right)
14064   player->shield_normal_time_left = 0;
14065   player->shield_deadly_time_left = 0;
14066
14067 #if 0
14068   Debug("game:playing:KillPlayer",
14069         "1: killed == %d, active == %d, reanimated == %d",
14070         player->killed, player->active, player->reanimated);
14071 #endif
14072
14073   Bang(jx, jy);
14074
14075 #if 0
14076   Debug("game:playing:KillPlayer",
14077         "2: killed == %d, active == %d, reanimated == %d",
14078         player->killed, player->active, player->reanimated);
14079 #endif
14080
14081   if (player->reanimated)       // killed player may have been reanimated
14082     player->killed = player->reanimated = FALSE;
14083   else
14084     BuryPlayer(player);
14085 }
14086
14087 static void KillPlayerUnlessEnemyProtected(int x, int y)
14088 {
14089   if (!PLAYER_ENEMY_PROTECTED(x, y))
14090     KillPlayer(PLAYERINFO(x, y));
14091 }
14092
14093 static void KillPlayerUnlessExplosionProtected(int x, int y)
14094 {
14095   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14096     KillPlayer(PLAYERINFO(x, y));
14097 }
14098
14099 void BuryPlayer(struct PlayerInfo *player)
14100 {
14101   int jx = player->jx, jy = player->jy;
14102
14103   if (!player->active)
14104     return;
14105
14106   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14107   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14108
14109   RemovePlayer(player);
14110
14111   player->buried = TRUE;
14112
14113   if (game.all_players_gone)
14114     game.GameOver = TRUE;
14115 }
14116
14117 void RemovePlayer(struct PlayerInfo *player)
14118 {
14119   int jx = player->jx, jy = player->jy;
14120   int i, found = FALSE;
14121
14122   player->present = FALSE;
14123   player->active = FALSE;
14124
14125   // required for some CE actions (even if the player is not active anymore)
14126   player->MovPos = 0;
14127
14128   if (!ExplodeField[jx][jy])
14129     StorePlayer[jx][jy] = 0;
14130
14131   if (player->is_moving)
14132     TEST_DrawLevelField(player->last_jx, player->last_jy);
14133
14134   for (i = 0; i < MAX_PLAYERS; i++)
14135     if (stored_player[i].active)
14136       found = TRUE;
14137
14138   if (!found)
14139   {
14140     game.all_players_gone = TRUE;
14141     game.GameOver = TRUE;
14142   }
14143
14144   game.exit_x = game.robot_wheel_x = jx;
14145   game.exit_y = game.robot_wheel_y = jy;
14146 }
14147
14148 void ExitPlayer(struct PlayerInfo *player)
14149 {
14150   DrawPlayer(player);   // needed here only to cleanup last field
14151   RemovePlayer(player);
14152
14153   if (game.players_still_needed > 0)
14154     game.players_still_needed--;
14155 }
14156
14157 static void SetFieldForSnapping(int x, int y, int element, int direction,
14158                                 int player_index_bit)
14159 {
14160   struct ElementInfo *ei = &element_info[element];
14161   int direction_bit = MV_DIR_TO_BIT(direction);
14162   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14163   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14164                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14165
14166   Tile[x][y] = EL_ELEMENT_SNAPPING;
14167   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14168   MovDir[x][y] = direction;
14169   Store[x][y] = element;
14170   Store2[x][y] = player_index_bit;
14171
14172   ResetGfxAnimation(x, y);
14173
14174   GfxElement[x][y] = element;
14175   GfxAction[x][y] = action;
14176   GfxDir[x][y] = direction;
14177   GfxFrame[x][y] = -1;
14178 }
14179
14180 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14181                                    int player_index_bit)
14182 {
14183   TestIfElementTouchesCustomElement(x, y);      // for empty space
14184
14185   if (level.finish_dig_collect)
14186   {
14187     int dig_side = MV_DIR_OPPOSITE(direction);
14188     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14189                         CE_PLAYER_COLLECTS_X);
14190
14191     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14192                                         player_index_bit, dig_side);
14193     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14194                                         player_index_bit, dig_side);
14195   }
14196 }
14197
14198 /*
14199   =============================================================================
14200   checkDiagonalPushing()
14201   -----------------------------------------------------------------------------
14202   check if diagonal input device direction results in pushing of object
14203   (by checking if the alternative direction is walkable, diggable, ...)
14204   =============================================================================
14205 */
14206
14207 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14208                                     int x, int y, int real_dx, int real_dy)
14209 {
14210   int jx, jy, dx, dy, xx, yy;
14211
14212   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14213     return TRUE;
14214
14215   // diagonal direction: check alternative direction
14216   jx = player->jx;
14217   jy = player->jy;
14218   dx = x - jx;
14219   dy = y - jy;
14220   xx = jx + (dx == 0 ? real_dx : 0);
14221   yy = jy + (dy == 0 ? real_dy : 0);
14222
14223   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14224 }
14225
14226 /*
14227   =============================================================================
14228   DigField()
14229   -----------------------------------------------------------------------------
14230   x, y:                 field next to player (non-diagonal) to try to dig to
14231   real_dx, real_dy:     direction as read from input device (can be diagonal)
14232   =============================================================================
14233 */
14234
14235 static int DigField(struct PlayerInfo *player,
14236                     int oldx, int oldy, int x, int y,
14237                     int real_dx, int real_dy, int mode)
14238 {
14239   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14240   boolean player_was_pushing = player->is_pushing;
14241   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14242   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14243   int jx = oldx, jy = oldy;
14244   int dx = x - jx, dy = y - jy;
14245   int nextx = x + dx, nexty = y + dy;
14246   int move_direction = (dx == -1 ? MV_LEFT  :
14247                         dx == +1 ? MV_RIGHT :
14248                         dy == -1 ? MV_UP    :
14249                         dy == +1 ? MV_DOWN  : MV_NONE);
14250   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14251   int dig_side = MV_DIR_OPPOSITE(move_direction);
14252   int old_element = Tile[jx][jy];
14253   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14254   int collect_count;
14255
14256   if (is_player)                // function can also be called by EL_PENGUIN
14257   {
14258     if (player->MovPos == 0)
14259     {
14260       player->is_digging = FALSE;
14261       player->is_collecting = FALSE;
14262     }
14263
14264     if (player->MovPos == 0)    // last pushing move finished
14265       player->is_pushing = FALSE;
14266
14267     if (mode == DF_NO_PUSH)     // player just stopped pushing
14268     {
14269       player->is_switching = FALSE;
14270       player->push_delay = -1;
14271
14272       return MP_NO_ACTION;
14273     }
14274   }
14275   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14276     old_element = Back[jx][jy];
14277
14278   // in case of element dropped at player position, check background
14279   else if (Back[jx][jy] != EL_EMPTY &&
14280            game.engine_version >= VERSION_IDENT(2,2,0,0))
14281     old_element = Back[jx][jy];
14282
14283   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14284     return MP_NO_ACTION;        // field has no opening in this direction
14285
14286   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14287     return MP_NO_ACTION;        // field has no opening in this direction
14288
14289   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14290   {
14291     SplashAcid(x, y);
14292
14293     Tile[jx][jy] = player->artwork_element;
14294     InitMovingField(jx, jy, MV_DOWN);
14295     Store[jx][jy] = EL_ACID;
14296     ContinueMoving(jx, jy);
14297     BuryPlayer(player);
14298
14299     return MP_DONT_RUN_INTO;
14300   }
14301
14302   if (player_can_move && DONT_RUN_INTO(element))
14303   {
14304     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14305
14306     return MP_DONT_RUN_INTO;
14307   }
14308
14309   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14310     return MP_NO_ACTION;
14311
14312   collect_count = element_info[element].collect_count_initial;
14313
14314   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14315     return MP_NO_ACTION;
14316
14317   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14318     player_can_move = player_can_move_or_snap;
14319
14320   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14321       game.engine_version >= VERSION_IDENT(2,2,0,0))
14322   {
14323     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14324                                player->index_bit, dig_side);
14325     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14326                                         player->index_bit, dig_side);
14327
14328     if (element == EL_DC_LANDMINE)
14329       Bang(x, y);
14330
14331     if (Tile[x][y] != element)          // field changed by snapping
14332       return MP_ACTION;
14333
14334     return MP_NO_ACTION;
14335   }
14336
14337   if (player->gravity && is_player && !player->is_auto_moving &&
14338       canFallDown(player) && move_direction != MV_DOWN &&
14339       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14340     return MP_NO_ACTION;        // player cannot walk here due to gravity
14341
14342   if (player_can_move &&
14343       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14344   {
14345     int sound_element = SND_ELEMENT(element);
14346     int sound_action = ACTION_WALKING;
14347
14348     if (IS_RND_GATE(element))
14349     {
14350       if (!player->key[RND_GATE_NR(element)])
14351         return MP_NO_ACTION;
14352     }
14353     else if (IS_RND_GATE_GRAY(element))
14354     {
14355       if (!player->key[RND_GATE_GRAY_NR(element)])
14356         return MP_NO_ACTION;
14357     }
14358     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14359     {
14360       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14361         return MP_NO_ACTION;
14362     }
14363     else if (element == EL_EXIT_OPEN ||
14364              element == EL_EM_EXIT_OPEN ||
14365              element == EL_EM_EXIT_OPENING ||
14366              element == EL_STEEL_EXIT_OPEN ||
14367              element == EL_EM_STEEL_EXIT_OPEN ||
14368              element == EL_EM_STEEL_EXIT_OPENING ||
14369              element == EL_SP_EXIT_OPEN ||
14370              element == EL_SP_EXIT_OPENING)
14371     {
14372       sound_action = ACTION_PASSING;    // player is passing exit
14373     }
14374     else if (element == EL_EMPTY)
14375     {
14376       sound_action = ACTION_MOVING;             // nothing to walk on
14377     }
14378
14379     // play sound from background or player, whatever is available
14380     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14381       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14382     else
14383       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14384   }
14385   else if (player_can_move &&
14386            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14387   {
14388     if (!ACCESS_FROM(element, opposite_direction))
14389       return MP_NO_ACTION;      // field not accessible from this direction
14390
14391     if (CAN_MOVE(element))      // only fixed elements can be passed!
14392       return MP_NO_ACTION;
14393
14394     if (IS_EM_GATE(element))
14395     {
14396       if (!player->key[EM_GATE_NR(element)])
14397         return MP_NO_ACTION;
14398     }
14399     else if (IS_EM_GATE_GRAY(element))
14400     {
14401       if (!player->key[EM_GATE_GRAY_NR(element)])
14402         return MP_NO_ACTION;
14403     }
14404     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14405     {
14406       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14407         return MP_NO_ACTION;
14408     }
14409     else if (IS_EMC_GATE(element))
14410     {
14411       if (!player->key[EMC_GATE_NR(element)])
14412         return MP_NO_ACTION;
14413     }
14414     else if (IS_EMC_GATE_GRAY(element))
14415     {
14416       if (!player->key[EMC_GATE_GRAY_NR(element)])
14417         return MP_NO_ACTION;
14418     }
14419     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14420     {
14421       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14422         return MP_NO_ACTION;
14423     }
14424     else if (element == EL_DC_GATE_WHITE ||
14425              element == EL_DC_GATE_WHITE_GRAY ||
14426              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14427     {
14428       if (player->num_white_keys == 0)
14429         return MP_NO_ACTION;
14430
14431       player->num_white_keys--;
14432     }
14433     else if (IS_SP_PORT(element))
14434     {
14435       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14436           element == EL_SP_GRAVITY_PORT_RIGHT ||
14437           element == EL_SP_GRAVITY_PORT_UP ||
14438           element == EL_SP_GRAVITY_PORT_DOWN)
14439         player->gravity = !player->gravity;
14440       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14441                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14442                element == EL_SP_GRAVITY_ON_PORT_UP ||
14443                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14444         player->gravity = TRUE;
14445       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14446                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14447                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14448                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14449         player->gravity = FALSE;
14450     }
14451
14452     // automatically move to the next field with double speed
14453     player->programmed_action = move_direction;
14454
14455     if (player->move_delay_reset_counter == 0)
14456     {
14457       player->move_delay_reset_counter = 2;     // two double speed steps
14458
14459       DOUBLE_PLAYER_SPEED(player);
14460     }
14461
14462     PlayLevelSoundAction(x, y, ACTION_PASSING);
14463   }
14464   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14465   {
14466     RemoveField(x, y);
14467
14468     if (mode != DF_SNAP)
14469     {
14470       GfxElement[x][y] = GFX_ELEMENT(element);
14471       player->is_digging = TRUE;
14472     }
14473
14474     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14475
14476     // use old behaviour for old levels (digging)
14477     if (!level.finish_dig_collect)
14478     {
14479       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14480                                           player->index_bit, dig_side);
14481
14482       // if digging triggered player relocation, finish digging tile
14483       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14484         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14485     }
14486
14487     if (mode == DF_SNAP)
14488     {
14489       if (level.block_snap_field)
14490         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14491       else
14492         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14493
14494       // use old behaviour for old levels (snapping)
14495       if (!level.finish_dig_collect)
14496         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14497                                             player->index_bit, dig_side);
14498     }
14499   }
14500   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14501   {
14502     RemoveField(x, y);
14503
14504     if (is_player && mode != DF_SNAP)
14505     {
14506       GfxElement[x][y] = element;
14507       player->is_collecting = TRUE;
14508     }
14509
14510     if (element == EL_SPEED_PILL)
14511     {
14512       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14513     }
14514     else if (element == EL_EXTRA_TIME && level.time > 0)
14515     {
14516       TimeLeft += level.extra_time;
14517
14518       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14519
14520       DisplayGameControlValues();
14521     }
14522     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14523     {
14524       player->shield_normal_time_left += level.shield_normal_time;
14525       if (element == EL_SHIELD_DEADLY)
14526         player->shield_deadly_time_left += level.shield_deadly_time;
14527     }
14528     else if (element == EL_DYNAMITE ||
14529              element == EL_EM_DYNAMITE ||
14530              element == EL_SP_DISK_RED)
14531     {
14532       if (player->inventory_size < MAX_INVENTORY_SIZE)
14533         player->inventory_element[player->inventory_size++] = element;
14534
14535       DrawGameDoorValues();
14536     }
14537     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14538     {
14539       player->dynabomb_count++;
14540       player->dynabombs_left++;
14541     }
14542     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14543     {
14544       player->dynabomb_size++;
14545     }
14546     else if (element == EL_DYNABOMB_INCREASE_POWER)
14547     {
14548       player->dynabomb_xl = TRUE;
14549     }
14550     else if (IS_KEY(element))
14551     {
14552       player->key[KEY_NR(element)] = TRUE;
14553
14554       DrawGameDoorValues();
14555     }
14556     else if (element == EL_DC_KEY_WHITE)
14557     {
14558       player->num_white_keys++;
14559
14560       // display white keys?
14561       // DrawGameDoorValues();
14562     }
14563     else if (IS_ENVELOPE(element))
14564     {
14565       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14566
14567       if (!wait_for_snapping)
14568         player->show_envelope = element;
14569     }
14570     else if (element == EL_EMC_LENSES)
14571     {
14572       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14573
14574       RedrawAllInvisibleElementsForLenses();
14575     }
14576     else if (element == EL_EMC_MAGNIFIER)
14577     {
14578       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14579
14580       RedrawAllInvisibleElementsForMagnifier();
14581     }
14582     else if (IS_DROPPABLE(element) ||
14583              IS_THROWABLE(element))     // can be collected and dropped
14584     {
14585       int i;
14586
14587       if (collect_count == 0)
14588         player->inventory_infinite_element = element;
14589       else
14590         for (i = 0; i < collect_count; i++)
14591           if (player->inventory_size < MAX_INVENTORY_SIZE)
14592             player->inventory_element[player->inventory_size++] = element;
14593
14594       DrawGameDoorValues();
14595     }
14596     else if (collect_count > 0)
14597     {
14598       game.gems_still_needed -= collect_count;
14599       if (game.gems_still_needed < 0)
14600         game.gems_still_needed = 0;
14601
14602       game.snapshot.collected_item = TRUE;
14603
14604       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14605
14606       DisplayGameControlValues();
14607     }
14608
14609     RaiseScoreElement(element);
14610     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14611
14612     // use old behaviour for old levels (collecting)
14613     if (!level.finish_dig_collect && is_player)
14614     {
14615       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14616                                           player->index_bit, dig_side);
14617
14618       // if collecting triggered player relocation, finish collecting tile
14619       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14620         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14621     }
14622
14623     if (mode == DF_SNAP)
14624     {
14625       if (level.block_snap_field)
14626         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14627       else
14628         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14629
14630       // use old behaviour for old levels (snapping)
14631       if (!level.finish_dig_collect)
14632         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14633                                             player->index_bit, dig_side);
14634     }
14635   }
14636   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14637   {
14638     if (mode == DF_SNAP && element != EL_BD_ROCK)
14639       return MP_NO_ACTION;
14640
14641     if (CAN_FALL(element) && dy)
14642       return MP_NO_ACTION;
14643
14644     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14645         !(element == EL_SPRING && level.use_spring_bug))
14646       return MP_NO_ACTION;
14647
14648     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14649         ((move_direction & MV_VERTICAL &&
14650           ((element_info[element].move_pattern & MV_LEFT &&
14651             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14652            (element_info[element].move_pattern & MV_RIGHT &&
14653             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14654          (move_direction & MV_HORIZONTAL &&
14655           ((element_info[element].move_pattern & MV_UP &&
14656             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14657            (element_info[element].move_pattern & MV_DOWN &&
14658             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14659       return MP_NO_ACTION;
14660
14661     // do not push elements already moving away faster than player
14662     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14663         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14664       return MP_NO_ACTION;
14665
14666     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14667     {
14668       if (player->push_delay_value == -1 || !player_was_pushing)
14669         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14670     }
14671     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14672     {
14673       if (player->push_delay_value == -1)
14674         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14675     }
14676     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14677     {
14678       if (!player->is_pushing)
14679         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14680     }
14681
14682     player->is_pushing = TRUE;
14683     player->is_active = TRUE;
14684
14685     if (!(IN_LEV_FIELD(nextx, nexty) &&
14686           (IS_FREE(nextx, nexty) ||
14687            (IS_SB_ELEMENT(element) &&
14688             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14689            (IS_CUSTOM_ELEMENT(element) &&
14690             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14691       return MP_NO_ACTION;
14692
14693     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14694       return MP_NO_ACTION;
14695
14696     if (player->push_delay == -1)       // new pushing; restart delay
14697       player->push_delay = 0;
14698
14699     if (player->push_delay < player->push_delay_value &&
14700         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14701         element != EL_SPRING && element != EL_BALLOON)
14702     {
14703       // make sure that there is no move delay before next try to push
14704       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14705         player->move_delay = 0;
14706
14707       return MP_NO_ACTION;
14708     }
14709
14710     if (IS_CUSTOM_ELEMENT(element) &&
14711         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14712     {
14713       if (!DigFieldByCE(nextx, nexty, element))
14714         return MP_NO_ACTION;
14715     }
14716
14717     if (IS_SB_ELEMENT(element))
14718     {
14719       boolean sokoban_task_solved = FALSE;
14720
14721       if (element == EL_SOKOBAN_FIELD_FULL)
14722       {
14723         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14724
14725         IncrementSokobanFieldsNeeded();
14726         IncrementSokobanObjectsNeeded();
14727       }
14728
14729       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14730       {
14731         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14732
14733         DecrementSokobanFieldsNeeded();
14734         DecrementSokobanObjectsNeeded();
14735
14736         // sokoban object was pushed from empty field to sokoban field
14737         if (Back[x][y] == EL_EMPTY)
14738           sokoban_task_solved = TRUE;
14739       }
14740
14741       Tile[x][y] = EL_SOKOBAN_OBJECT;
14742
14743       if (Back[x][y] == Back[nextx][nexty])
14744         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14745       else if (Back[x][y] != 0)
14746         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14747                                     ACTION_EMPTYING);
14748       else
14749         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14750                                     ACTION_FILLING);
14751
14752       if (sokoban_task_solved &&
14753           game.sokoban_fields_still_needed == 0 &&
14754           game.sokoban_objects_still_needed == 0 &&
14755           level.auto_exit_sokoban)
14756       {
14757         game.players_still_needed = 0;
14758
14759         LevelSolved();
14760
14761         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14762       }
14763     }
14764     else
14765       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14766
14767     InitMovingField(x, y, move_direction);
14768     GfxAction[x][y] = ACTION_PUSHING;
14769
14770     if (mode == DF_SNAP)
14771       ContinueMoving(x, y);
14772     else
14773       MovPos[x][y] = (dx != 0 ? dx : dy);
14774
14775     Pushed[x][y] = TRUE;
14776     Pushed[nextx][nexty] = TRUE;
14777
14778     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14779       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14780     else
14781       player->push_delay_value = -1;    // get new value later
14782
14783     // check for element change _after_ element has been pushed
14784     if (game.use_change_when_pushing_bug)
14785     {
14786       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14787                                  player->index_bit, dig_side);
14788       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14789                                           player->index_bit, dig_side);
14790     }
14791   }
14792   else if (IS_SWITCHABLE(element))
14793   {
14794     if (PLAYER_SWITCHING(player, x, y))
14795     {
14796       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14797                                           player->index_bit, dig_side);
14798
14799       return MP_ACTION;
14800     }
14801
14802     player->is_switching = TRUE;
14803     player->switch_x = x;
14804     player->switch_y = y;
14805
14806     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14807
14808     if (element == EL_ROBOT_WHEEL)
14809     {
14810       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14811
14812       game.robot_wheel_x = x;
14813       game.robot_wheel_y = y;
14814       game.robot_wheel_active = TRUE;
14815
14816       TEST_DrawLevelField(x, y);
14817     }
14818     else if (element == EL_SP_TERMINAL)
14819     {
14820       int xx, yy;
14821
14822       SCAN_PLAYFIELD(xx, yy)
14823       {
14824         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14825         {
14826           Bang(xx, yy);
14827         }
14828         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14829         {
14830           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14831
14832           ResetGfxAnimation(xx, yy);
14833           TEST_DrawLevelField(xx, yy);
14834         }
14835       }
14836     }
14837     else if (IS_BELT_SWITCH(element))
14838     {
14839       ToggleBeltSwitch(x, y);
14840     }
14841     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14842              element == EL_SWITCHGATE_SWITCH_DOWN ||
14843              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14844              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14845     {
14846       ToggleSwitchgateSwitch(x, y);
14847     }
14848     else if (element == EL_LIGHT_SWITCH ||
14849              element == EL_LIGHT_SWITCH_ACTIVE)
14850     {
14851       ToggleLightSwitch(x, y);
14852     }
14853     else if (element == EL_TIMEGATE_SWITCH ||
14854              element == EL_DC_TIMEGATE_SWITCH)
14855     {
14856       ActivateTimegateSwitch(x, y);
14857     }
14858     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14859              element == EL_BALLOON_SWITCH_RIGHT ||
14860              element == EL_BALLOON_SWITCH_UP    ||
14861              element == EL_BALLOON_SWITCH_DOWN  ||
14862              element == EL_BALLOON_SWITCH_NONE  ||
14863              element == EL_BALLOON_SWITCH_ANY)
14864     {
14865       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14866                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14867                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14868                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14869                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14870                              move_direction);
14871     }
14872     else if (element == EL_LAMP)
14873     {
14874       Tile[x][y] = EL_LAMP_ACTIVE;
14875       game.lights_still_needed--;
14876
14877       ResetGfxAnimation(x, y);
14878       TEST_DrawLevelField(x, y);
14879     }
14880     else if (element == EL_TIME_ORB_FULL)
14881     {
14882       Tile[x][y] = EL_TIME_ORB_EMPTY;
14883
14884       if (level.time > 0 || level.use_time_orb_bug)
14885       {
14886         TimeLeft += level.time_orb_time;
14887         game.no_level_time_limit = FALSE;
14888
14889         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14890
14891         DisplayGameControlValues();
14892       }
14893
14894       ResetGfxAnimation(x, y);
14895       TEST_DrawLevelField(x, y);
14896     }
14897     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14898              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14899     {
14900       int xx, yy;
14901
14902       game.ball_active = !game.ball_active;
14903
14904       SCAN_PLAYFIELD(xx, yy)
14905       {
14906         int e = Tile[xx][yy];
14907
14908         if (game.ball_active)
14909         {
14910           if (e == EL_EMC_MAGIC_BALL)
14911             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14912           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14913             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14914         }
14915         else
14916         {
14917           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14918             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14919           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14920             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14921         }
14922       }
14923     }
14924
14925     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14926                                         player->index_bit, dig_side);
14927
14928     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14929                                         player->index_bit, dig_side);
14930
14931     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14932                                         player->index_bit, dig_side);
14933
14934     return MP_ACTION;
14935   }
14936   else
14937   {
14938     if (!PLAYER_SWITCHING(player, x, y))
14939     {
14940       player->is_switching = TRUE;
14941       player->switch_x = x;
14942       player->switch_y = y;
14943
14944       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14945                                  player->index_bit, dig_side);
14946       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14947                                           player->index_bit, dig_side);
14948
14949       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14950                                  player->index_bit, dig_side);
14951       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14952                                           player->index_bit, dig_side);
14953     }
14954
14955     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14956                                player->index_bit, dig_side);
14957     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14958                                         player->index_bit, dig_side);
14959
14960     return MP_NO_ACTION;
14961   }
14962
14963   player->push_delay = -1;
14964
14965   if (is_player)                // function can also be called by EL_PENGUIN
14966   {
14967     if (Tile[x][y] != element)          // really digged/collected something
14968     {
14969       player->is_collecting = !player->is_digging;
14970       player->is_active = TRUE;
14971
14972       player->last_removed_element = element;
14973     }
14974   }
14975
14976   return MP_MOVING;
14977 }
14978
14979 static boolean DigFieldByCE(int x, int y, int digging_element)
14980 {
14981   int element = Tile[x][y];
14982
14983   if (!IS_FREE(x, y))
14984   {
14985     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14986                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14987                   ACTION_BREAKING);
14988
14989     // no element can dig solid indestructible elements
14990     if (IS_INDESTRUCTIBLE(element) &&
14991         !IS_DIGGABLE(element) &&
14992         !IS_COLLECTIBLE(element))
14993       return FALSE;
14994
14995     if (AmoebaNr[x][y] &&
14996         (element == EL_AMOEBA_FULL ||
14997          element == EL_BD_AMOEBA ||
14998          element == EL_AMOEBA_GROWING))
14999     {
15000       AmoebaCnt[AmoebaNr[x][y]]--;
15001       AmoebaCnt2[AmoebaNr[x][y]]--;
15002     }
15003
15004     if (IS_MOVING(x, y))
15005       RemoveMovingField(x, y);
15006     else
15007     {
15008       RemoveField(x, y);
15009       TEST_DrawLevelField(x, y);
15010     }
15011
15012     // if digged element was about to explode, prevent the explosion
15013     ExplodeField[x][y] = EX_TYPE_NONE;
15014
15015     PlayLevelSoundAction(x, y, action);
15016   }
15017
15018   Store[x][y] = EL_EMPTY;
15019
15020   // this makes it possible to leave the removed element again
15021   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15022     Store[x][y] = element;
15023
15024   return TRUE;
15025 }
15026
15027 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15028 {
15029   int jx = player->jx, jy = player->jy;
15030   int x = jx + dx, y = jy + dy;
15031   int snap_direction = (dx == -1 ? MV_LEFT  :
15032                         dx == +1 ? MV_RIGHT :
15033                         dy == -1 ? MV_UP    :
15034                         dy == +1 ? MV_DOWN  : MV_NONE);
15035   boolean can_continue_snapping = (level.continuous_snapping &&
15036                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15037
15038   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15039     return FALSE;
15040
15041   if (!player->active || !IN_LEV_FIELD(x, y))
15042     return FALSE;
15043
15044   if (dx && dy)
15045     return FALSE;
15046
15047   if (!dx && !dy)
15048   {
15049     if (player->MovPos == 0)
15050       player->is_pushing = FALSE;
15051
15052     player->is_snapping = FALSE;
15053
15054     if (player->MovPos == 0)
15055     {
15056       player->is_moving = FALSE;
15057       player->is_digging = FALSE;
15058       player->is_collecting = FALSE;
15059     }
15060
15061     return FALSE;
15062   }
15063
15064   // prevent snapping with already pressed snap key when not allowed
15065   if (player->is_snapping && !can_continue_snapping)
15066     return FALSE;
15067
15068   player->MovDir = snap_direction;
15069
15070   if (player->MovPos == 0)
15071   {
15072     player->is_moving = FALSE;
15073     player->is_digging = FALSE;
15074     player->is_collecting = FALSE;
15075   }
15076
15077   player->is_dropping = FALSE;
15078   player->is_dropping_pressed = FALSE;
15079   player->drop_pressed_delay = 0;
15080
15081   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15082     return FALSE;
15083
15084   player->is_snapping = TRUE;
15085   player->is_active = TRUE;
15086
15087   if (player->MovPos == 0)
15088   {
15089     player->is_moving = FALSE;
15090     player->is_digging = FALSE;
15091     player->is_collecting = FALSE;
15092   }
15093
15094   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15095     TEST_DrawLevelField(player->last_jx, player->last_jy);
15096
15097   TEST_DrawLevelField(x, y);
15098
15099   return TRUE;
15100 }
15101
15102 static boolean DropElement(struct PlayerInfo *player)
15103 {
15104   int old_element, new_element;
15105   int dropx = player->jx, dropy = player->jy;
15106   int drop_direction = player->MovDir;
15107   int drop_side = drop_direction;
15108   int drop_element = get_next_dropped_element(player);
15109
15110   /* do not drop an element on top of another element; when holding drop key
15111      pressed without moving, dropped element must move away before the next
15112      element can be dropped (this is especially important if the next element
15113      is dynamite, which can be placed on background for historical reasons) */
15114   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15115     return MP_ACTION;
15116
15117   if (IS_THROWABLE(drop_element))
15118   {
15119     dropx += GET_DX_FROM_DIR(drop_direction);
15120     dropy += GET_DY_FROM_DIR(drop_direction);
15121
15122     if (!IN_LEV_FIELD(dropx, dropy))
15123       return FALSE;
15124   }
15125
15126   old_element = Tile[dropx][dropy];     // old element at dropping position
15127   new_element = drop_element;           // default: no change when dropping
15128
15129   // check if player is active, not moving and ready to drop
15130   if (!player->active || player->MovPos || player->drop_delay > 0)
15131     return FALSE;
15132
15133   // check if player has anything that can be dropped
15134   if (new_element == EL_UNDEFINED)
15135     return FALSE;
15136
15137   // only set if player has anything that can be dropped
15138   player->is_dropping_pressed = TRUE;
15139
15140   // check if drop key was pressed long enough for EM style dynamite
15141   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15142     return FALSE;
15143
15144   // check if anything can be dropped at the current position
15145   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15146     return FALSE;
15147
15148   // collected custom elements can only be dropped on empty fields
15149   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15150     return FALSE;
15151
15152   if (old_element != EL_EMPTY)
15153     Back[dropx][dropy] = old_element;   // store old element on this field
15154
15155   ResetGfxAnimation(dropx, dropy);
15156   ResetRandomAnimationValue(dropx, dropy);
15157
15158   if (player->inventory_size > 0 ||
15159       player->inventory_infinite_element != EL_UNDEFINED)
15160   {
15161     if (player->inventory_size > 0)
15162     {
15163       player->inventory_size--;
15164
15165       DrawGameDoorValues();
15166
15167       if (new_element == EL_DYNAMITE)
15168         new_element = EL_DYNAMITE_ACTIVE;
15169       else if (new_element == EL_EM_DYNAMITE)
15170         new_element = EL_EM_DYNAMITE_ACTIVE;
15171       else if (new_element == EL_SP_DISK_RED)
15172         new_element = EL_SP_DISK_RED_ACTIVE;
15173     }
15174
15175     Tile[dropx][dropy] = new_element;
15176
15177     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15178       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15179                           el2img(Tile[dropx][dropy]), 0);
15180
15181     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15182
15183     // needed if previous element just changed to "empty" in the last frame
15184     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15185
15186     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15187                                player->index_bit, drop_side);
15188     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15189                                         CE_PLAYER_DROPS_X,
15190                                         player->index_bit, drop_side);
15191
15192     TestIfElementTouchesCustomElement(dropx, dropy);
15193   }
15194   else          // player is dropping a dyna bomb
15195   {
15196     player->dynabombs_left--;
15197
15198     Tile[dropx][dropy] = new_element;
15199
15200     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15201       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15202                           el2img(Tile[dropx][dropy]), 0);
15203
15204     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15205   }
15206
15207   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15208     InitField_WithBug1(dropx, dropy, FALSE);
15209
15210   new_element = Tile[dropx][dropy];     // element might have changed
15211
15212   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15213       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15214   {
15215     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15216       MovDir[dropx][dropy] = drop_direction;
15217
15218     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15219
15220     // do not cause impact style collision by dropping elements that can fall
15221     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15222   }
15223
15224   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15225   player->is_dropping = TRUE;
15226
15227   player->drop_pressed_delay = 0;
15228   player->is_dropping_pressed = FALSE;
15229
15230   player->drop_x = dropx;
15231   player->drop_y = dropy;
15232
15233   return TRUE;
15234 }
15235
15236 // ----------------------------------------------------------------------------
15237 // game sound playing functions
15238 // ----------------------------------------------------------------------------
15239
15240 static int *loop_sound_frame = NULL;
15241 static int *loop_sound_volume = NULL;
15242
15243 void InitPlayLevelSound(void)
15244 {
15245   int num_sounds = getSoundListSize();
15246
15247   checked_free(loop_sound_frame);
15248   checked_free(loop_sound_volume);
15249
15250   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15251   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15252 }
15253
15254 static void PlayLevelSound(int x, int y, int nr)
15255 {
15256   int sx = SCREENX(x), sy = SCREENY(y);
15257   int volume, stereo_position;
15258   int max_distance = 8;
15259   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15260
15261   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15262       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15263     return;
15264
15265   if (!IN_LEV_FIELD(x, y) ||
15266       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15267       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15268     return;
15269
15270   volume = SOUND_MAX_VOLUME;
15271
15272   if (!IN_SCR_FIELD(sx, sy))
15273   {
15274     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15275     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15276
15277     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15278   }
15279
15280   stereo_position = (SOUND_MAX_LEFT +
15281                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15282                      (SCR_FIELDX + 2 * max_distance));
15283
15284   if (IS_LOOP_SOUND(nr))
15285   {
15286     /* This assures that quieter loop sounds do not overwrite louder ones,
15287        while restarting sound volume comparison with each new game frame. */
15288
15289     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15290       return;
15291
15292     loop_sound_volume[nr] = volume;
15293     loop_sound_frame[nr] = FrameCounter;
15294   }
15295
15296   PlaySoundExt(nr, volume, stereo_position, type);
15297 }
15298
15299 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15300 {
15301   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15302                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15303                  y < LEVELY(BY1) ? LEVELY(BY1) :
15304                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15305                  sound_action);
15306 }
15307
15308 static void PlayLevelSoundAction(int x, int y, int action)
15309 {
15310   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15311 }
15312
15313 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15314 {
15315   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15316
15317   if (sound_effect != SND_UNDEFINED)
15318     PlayLevelSound(x, y, sound_effect);
15319 }
15320
15321 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15322                                               int action)
15323 {
15324   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15325
15326   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15327     PlayLevelSound(x, y, sound_effect);
15328 }
15329
15330 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15331 {
15332   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15333
15334   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15335     PlayLevelSound(x, y, sound_effect);
15336 }
15337
15338 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15339 {
15340   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15341
15342   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15343     StopSound(sound_effect);
15344 }
15345
15346 static int getLevelMusicNr(void)
15347 {
15348   if (levelset.music[level_nr] != MUS_UNDEFINED)
15349     return levelset.music[level_nr];            // from config file
15350   else
15351     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15352 }
15353
15354 static void FadeLevelSounds(void)
15355 {
15356   FadeSounds();
15357 }
15358
15359 static void FadeLevelMusic(void)
15360 {
15361   int music_nr = getLevelMusicNr();
15362   char *curr_music = getCurrentlyPlayingMusicFilename();
15363   char *next_music = getMusicInfoEntryFilename(music_nr);
15364
15365   if (!strEqual(curr_music, next_music))
15366     FadeMusic();
15367 }
15368
15369 void FadeLevelSoundsAndMusic(void)
15370 {
15371   FadeLevelSounds();
15372   FadeLevelMusic();
15373 }
15374
15375 static void PlayLevelMusic(void)
15376 {
15377   int music_nr = getLevelMusicNr();
15378   char *curr_music = getCurrentlyPlayingMusicFilename();
15379   char *next_music = getMusicInfoEntryFilename(music_nr);
15380
15381   if (!strEqual(curr_music, next_music))
15382     PlayMusicLoop(music_nr);
15383 }
15384
15385 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15386 {
15387   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15388   int offset = 0;
15389   int x = xx - offset;
15390   int y = yy - offset;
15391
15392   switch (sample)
15393   {
15394     case SOUND_blank:
15395       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15396       break;
15397
15398     case SOUND_roll:
15399       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15400       break;
15401
15402     case SOUND_stone:
15403       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15404       break;
15405
15406     case SOUND_nut:
15407       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15408       break;
15409
15410     case SOUND_crack:
15411       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15412       break;
15413
15414     case SOUND_bug:
15415       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15416       break;
15417
15418     case SOUND_tank:
15419       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15420       break;
15421
15422     case SOUND_android_clone:
15423       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15424       break;
15425
15426     case SOUND_android_move:
15427       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15428       break;
15429
15430     case SOUND_spring:
15431       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15432       break;
15433
15434     case SOUND_slurp:
15435       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15436       break;
15437
15438     case SOUND_eater:
15439       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15440       break;
15441
15442     case SOUND_eater_eat:
15443       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15444       break;
15445
15446     case SOUND_alien:
15447       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15448       break;
15449
15450     case SOUND_collect:
15451       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15452       break;
15453
15454     case SOUND_diamond:
15455       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15456       break;
15457
15458     case SOUND_squash:
15459       // !!! CHECK THIS !!!
15460 #if 1
15461       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15462 #else
15463       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15464 #endif
15465       break;
15466
15467     case SOUND_wonderfall:
15468       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15469       break;
15470
15471     case SOUND_drip:
15472       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15473       break;
15474
15475     case SOUND_push:
15476       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15477       break;
15478
15479     case SOUND_dirt:
15480       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15481       break;
15482
15483     case SOUND_acid:
15484       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15485       break;
15486
15487     case SOUND_ball:
15488       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15489       break;
15490
15491     case SOUND_slide:
15492       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15493       break;
15494
15495     case SOUND_wonder:
15496       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15497       break;
15498
15499     case SOUND_door:
15500       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15501       break;
15502
15503     case SOUND_exit_open:
15504       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15505       break;
15506
15507     case SOUND_exit_leave:
15508       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15509       break;
15510
15511     case SOUND_dynamite:
15512       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15513       break;
15514
15515     case SOUND_tick:
15516       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15517       break;
15518
15519     case SOUND_press:
15520       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15521       break;
15522
15523     case SOUND_wheel:
15524       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15525       break;
15526
15527     case SOUND_boom:
15528       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15529       break;
15530
15531     case SOUND_die:
15532       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15533       break;
15534
15535     case SOUND_time:
15536       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15537       break;
15538
15539     default:
15540       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15541       break;
15542   }
15543 }
15544
15545 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15546 {
15547   int element = map_element_SP_to_RND(element_sp);
15548   int action = map_action_SP_to_RND(action_sp);
15549   int offset = (setup.sp_show_border_elements ? 0 : 1);
15550   int x = xx - offset;
15551   int y = yy - offset;
15552
15553   PlayLevelSoundElementAction(x, y, element, action);
15554 }
15555
15556 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15557 {
15558   int element = map_element_MM_to_RND(element_mm);
15559   int action = map_action_MM_to_RND(action_mm);
15560   int offset = 0;
15561   int x = xx - offset;
15562   int y = yy - offset;
15563
15564   if (!IS_MM_ELEMENT(element))
15565     element = EL_MM_DEFAULT;
15566
15567   PlayLevelSoundElementAction(x, y, element, action);
15568 }
15569
15570 void PlaySound_MM(int sound_mm)
15571 {
15572   int sound = map_sound_MM_to_RND(sound_mm);
15573
15574   if (sound == SND_UNDEFINED)
15575     return;
15576
15577   PlaySound(sound);
15578 }
15579
15580 void PlaySoundLoop_MM(int sound_mm)
15581 {
15582   int sound = map_sound_MM_to_RND(sound_mm);
15583
15584   if (sound == SND_UNDEFINED)
15585     return;
15586
15587   PlaySoundLoop(sound);
15588 }
15589
15590 void StopSound_MM(int sound_mm)
15591 {
15592   int sound = map_sound_MM_to_RND(sound_mm);
15593
15594   if (sound == SND_UNDEFINED)
15595     return;
15596
15597   StopSound(sound);
15598 }
15599
15600 void RaiseScore(int value)
15601 {
15602   game.score += value;
15603
15604   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15605
15606   DisplayGameControlValues();
15607 }
15608
15609 void RaiseScoreElement(int element)
15610 {
15611   switch (element)
15612   {
15613     case EL_EMERALD:
15614     case EL_BD_DIAMOND:
15615     case EL_EMERALD_YELLOW:
15616     case EL_EMERALD_RED:
15617     case EL_EMERALD_PURPLE:
15618     case EL_SP_INFOTRON:
15619       RaiseScore(level.score[SC_EMERALD]);
15620       break;
15621     case EL_DIAMOND:
15622       RaiseScore(level.score[SC_DIAMOND]);
15623       break;
15624     case EL_CRYSTAL:
15625       RaiseScore(level.score[SC_CRYSTAL]);
15626       break;
15627     case EL_PEARL:
15628       RaiseScore(level.score[SC_PEARL]);
15629       break;
15630     case EL_BUG:
15631     case EL_BD_BUTTERFLY:
15632     case EL_SP_ELECTRON:
15633       RaiseScore(level.score[SC_BUG]);
15634       break;
15635     case EL_SPACESHIP:
15636     case EL_BD_FIREFLY:
15637     case EL_SP_SNIKSNAK:
15638       RaiseScore(level.score[SC_SPACESHIP]);
15639       break;
15640     case EL_YAMYAM:
15641     case EL_DARK_YAMYAM:
15642       RaiseScore(level.score[SC_YAMYAM]);
15643       break;
15644     case EL_ROBOT:
15645       RaiseScore(level.score[SC_ROBOT]);
15646       break;
15647     case EL_PACMAN:
15648       RaiseScore(level.score[SC_PACMAN]);
15649       break;
15650     case EL_NUT:
15651       RaiseScore(level.score[SC_NUT]);
15652       break;
15653     case EL_DYNAMITE:
15654     case EL_EM_DYNAMITE:
15655     case EL_SP_DISK_RED:
15656     case EL_DYNABOMB_INCREASE_NUMBER:
15657     case EL_DYNABOMB_INCREASE_SIZE:
15658     case EL_DYNABOMB_INCREASE_POWER:
15659       RaiseScore(level.score[SC_DYNAMITE]);
15660       break;
15661     case EL_SHIELD_NORMAL:
15662     case EL_SHIELD_DEADLY:
15663       RaiseScore(level.score[SC_SHIELD]);
15664       break;
15665     case EL_EXTRA_TIME:
15666       RaiseScore(level.extra_time_score);
15667       break;
15668     case EL_KEY_1:
15669     case EL_KEY_2:
15670     case EL_KEY_3:
15671     case EL_KEY_4:
15672     case EL_EM_KEY_1:
15673     case EL_EM_KEY_2:
15674     case EL_EM_KEY_3:
15675     case EL_EM_KEY_4:
15676     case EL_EMC_KEY_5:
15677     case EL_EMC_KEY_6:
15678     case EL_EMC_KEY_7:
15679     case EL_EMC_KEY_8:
15680     case EL_DC_KEY_WHITE:
15681       RaiseScore(level.score[SC_KEY]);
15682       break;
15683     default:
15684       RaiseScore(element_info[element].collect_score);
15685       break;
15686   }
15687 }
15688
15689 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15690 {
15691   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15692   {
15693     if (!quick_quit)
15694     {
15695       // prevent short reactivation of overlay buttons while closing door
15696       SetOverlayActive(FALSE);
15697       UnmapGameButtons();
15698
15699       // door may still be open due to skipped or envelope style request
15700       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15701     }
15702
15703     if (network.enabled)
15704       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15705     else
15706     {
15707       if (quick_quit)
15708         FadeSkipNextFadeIn();
15709
15710       SetGameStatus(GAME_MODE_MAIN);
15711
15712       DrawMainMenu();
15713     }
15714   }
15715   else          // continue playing the game
15716   {
15717     if (tape.playing && tape.deactivate_display)
15718       TapeDeactivateDisplayOff(TRUE);
15719
15720     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15721
15722     if (tape.playing && tape.deactivate_display)
15723       TapeDeactivateDisplayOn();
15724   }
15725 }
15726
15727 void RequestQuitGame(boolean escape_key_pressed)
15728 {
15729   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15730   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15731                         level_editor_test_game);
15732   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15733                           quick_quit || score_info_tape_play);
15734
15735   RequestQuitGameExt(skip_request, quick_quit,
15736                      "Do you really want to quit the game?");
15737 }
15738
15739 void RequestRestartGame(char *message)
15740 {
15741   game.restart_game_message = NULL;
15742
15743   boolean has_started_game = hasStartedNetworkGame();
15744   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15745
15746   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15747   {
15748     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15749   }
15750   else
15751   {
15752     // needed in case of envelope request to close game panel
15753     CloseDoor(DOOR_CLOSE_1);
15754
15755     SetGameStatus(GAME_MODE_MAIN);
15756
15757     DrawMainMenu();
15758   }
15759 }
15760
15761 void CheckGameOver(void)
15762 {
15763   static boolean last_game_over = FALSE;
15764   static int game_over_delay = 0;
15765   int game_over_delay_value = 50;
15766   boolean game_over = checkGameFailed();
15767
15768   // do not handle game over if request dialog is already active
15769   if (game.request_active)
15770     return;
15771
15772   // do not ask to play again if game was never actually played
15773   if (!game.GamePlayed)
15774     return;
15775
15776   if (!game_over)
15777   {
15778     last_game_over = FALSE;
15779     game_over_delay = game_over_delay_value;
15780
15781     return;
15782   }
15783
15784   if (game_over_delay > 0)
15785   {
15786     game_over_delay--;
15787
15788     return;
15789   }
15790
15791   if (last_game_over != game_over)
15792     game.restart_game_message = (hasStartedNetworkGame() ?
15793                                  "Game over! Play it again?" :
15794                                  "Game over!");
15795
15796   last_game_over = game_over;
15797 }
15798
15799 boolean checkGameSolved(void)
15800 {
15801   // set for all game engines if level was solved
15802   return game.LevelSolved_GameEnd;
15803 }
15804
15805 boolean checkGameFailed(void)
15806 {
15807   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15808     return (game_em.game_over && !game_em.level_solved);
15809   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15810     return (game_sp.game_over && !game_sp.level_solved);
15811   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15812     return (game_mm.game_over && !game_mm.level_solved);
15813   else                          // GAME_ENGINE_TYPE_RND
15814     return (game.GameOver && !game.LevelSolved);
15815 }
15816
15817 boolean checkGameEnded(void)
15818 {
15819   return (checkGameSolved() || checkGameFailed());
15820 }
15821
15822
15823 // ----------------------------------------------------------------------------
15824 // random generator functions
15825 // ----------------------------------------------------------------------------
15826
15827 unsigned int InitEngineRandom_RND(int seed)
15828 {
15829   game.num_random_calls = 0;
15830
15831   return InitEngineRandom(seed);
15832 }
15833
15834 unsigned int RND(int max)
15835 {
15836   if (max > 0)
15837   {
15838     game.num_random_calls++;
15839
15840     return GetEngineRandom(max);
15841   }
15842
15843   return 0;
15844 }
15845
15846
15847 // ----------------------------------------------------------------------------
15848 // game engine snapshot handling functions
15849 // ----------------------------------------------------------------------------
15850
15851 struct EngineSnapshotInfo
15852 {
15853   // runtime values for custom element collect score
15854   int collect_score[NUM_CUSTOM_ELEMENTS];
15855
15856   // runtime values for group element choice position
15857   int choice_pos[NUM_GROUP_ELEMENTS];
15858
15859   // runtime values for belt position animations
15860   int belt_graphic[4][NUM_BELT_PARTS];
15861   int belt_anim_mode[4][NUM_BELT_PARTS];
15862 };
15863
15864 static struct EngineSnapshotInfo engine_snapshot_rnd;
15865 static char *snapshot_level_identifier = NULL;
15866 static int snapshot_level_nr = -1;
15867
15868 static void SaveEngineSnapshotValues_RND(void)
15869 {
15870   static int belt_base_active_element[4] =
15871   {
15872     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15873     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15874     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15875     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15876   };
15877   int i, j;
15878
15879   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15880   {
15881     int element = EL_CUSTOM_START + i;
15882
15883     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15884   }
15885
15886   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15887   {
15888     int element = EL_GROUP_START + i;
15889
15890     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15891   }
15892
15893   for (i = 0; i < 4; i++)
15894   {
15895     for (j = 0; j < NUM_BELT_PARTS; j++)
15896     {
15897       int element = belt_base_active_element[i] + j;
15898       int graphic = el2img(element);
15899       int anim_mode = graphic_info[graphic].anim_mode;
15900
15901       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15902       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15903     }
15904   }
15905 }
15906
15907 static void LoadEngineSnapshotValues_RND(void)
15908 {
15909   unsigned int num_random_calls = game.num_random_calls;
15910   int i, j;
15911
15912   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15913   {
15914     int element = EL_CUSTOM_START + i;
15915
15916     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15917   }
15918
15919   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15920   {
15921     int element = EL_GROUP_START + i;
15922
15923     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15924   }
15925
15926   for (i = 0; i < 4; i++)
15927   {
15928     for (j = 0; j < NUM_BELT_PARTS; j++)
15929     {
15930       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15931       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15932
15933       graphic_info[graphic].anim_mode = anim_mode;
15934     }
15935   }
15936
15937   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15938   {
15939     InitRND(tape.random_seed);
15940     for (i = 0; i < num_random_calls; i++)
15941       RND(1);
15942   }
15943
15944   if (game.num_random_calls != num_random_calls)
15945   {
15946     Error("number of random calls out of sync");
15947     Error("number of random calls should be %d", num_random_calls);
15948     Error("number of random calls is %d", game.num_random_calls);
15949
15950     Fail("this should not happen -- please debug");
15951   }
15952 }
15953
15954 void FreeEngineSnapshotSingle(void)
15955 {
15956   FreeSnapshotSingle();
15957
15958   setString(&snapshot_level_identifier, NULL);
15959   snapshot_level_nr = -1;
15960 }
15961
15962 void FreeEngineSnapshotList(void)
15963 {
15964   FreeSnapshotList();
15965 }
15966
15967 static ListNode *SaveEngineSnapshotBuffers(void)
15968 {
15969   ListNode *buffers = NULL;
15970
15971   // copy some special values to a structure better suited for the snapshot
15972
15973   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15974     SaveEngineSnapshotValues_RND();
15975   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15976     SaveEngineSnapshotValues_EM();
15977   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15978     SaveEngineSnapshotValues_SP(&buffers);
15979   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15980     SaveEngineSnapshotValues_MM(&buffers);
15981
15982   // save values stored in special snapshot structure
15983
15984   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15985     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15986   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15987     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15988   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15989     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15990   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15991     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15992
15993   // save further RND engine values
15994
15995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15996   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15997   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15998
15999   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16000   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16001   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16002   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16003   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16004
16005   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16006   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16007   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16008
16009   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16010
16011   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16012   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16013
16014   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16015   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16016   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16017   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16018   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16019   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16020   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16021   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16022   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16023   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16024   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16025   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16026   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16027   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16028   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16029   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16030   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16031   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16032
16033   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16034   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16035
16036   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16037   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16038   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16039
16040   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16041   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16042
16043   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16044   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16045   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16046   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16047   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16048   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16049
16050   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16051   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16052
16053 #if 0
16054   ListNode *node = engine_snapshot_list_rnd;
16055   int num_bytes = 0;
16056
16057   while (node != NULL)
16058   {
16059     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16060
16061     node = node->next;
16062   }
16063
16064   Debug("game:playing:SaveEngineSnapshotBuffers",
16065         "size of engine snapshot: %d bytes", num_bytes);
16066 #endif
16067
16068   return buffers;
16069 }
16070
16071 void SaveEngineSnapshotSingle(void)
16072 {
16073   ListNode *buffers = SaveEngineSnapshotBuffers();
16074
16075   // finally save all snapshot buffers to single snapshot
16076   SaveSnapshotSingle(buffers);
16077
16078   // save level identification information
16079   setString(&snapshot_level_identifier, leveldir_current->identifier);
16080   snapshot_level_nr = level_nr;
16081 }
16082
16083 boolean CheckSaveEngineSnapshotToList(void)
16084 {
16085   boolean save_snapshot =
16086     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16087      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16088       game.snapshot.changed_action) ||
16089      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16090       game.snapshot.collected_item));
16091
16092   game.snapshot.changed_action = FALSE;
16093   game.snapshot.collected_item = FALSE;
16094   game.snapshot.save_snapshot = save_snapshot;
16095
16096   return save_snapshot;
16097 }
16098
16099 void SaveEngineSnapshotToList(void)
16100 {
16101   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16102       tape.quick_resume)
16103     return;
16104
16105   ListNode *buffers = SaveEngineSnapshotBuffers();
16106
16107   // finally save all snapshot buffers to snapshot list
16108   SaveSnapshotToList(buffers);
16109 }
16110
16111 void SaveEngineSnapshotToListInitial(void)
16112 {
16113   FreeEngineSnapshotList();
16114
16115   SaveEngineSnapshotToList();
16116 }
16117
16118 static void LoadEngineSnapshotValues(void)
16119 {
16120   // restore special values from snapshot structure
16121
16122   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16123     LoadEngineSnapshotValues_RND();
16124   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16125     LoadEngineSnapshotValues_EM();
16126   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16127     LoadEngineSnapshotValues_SP();
16128   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16129     LoadEngineSnapshotValues_MM();
16130 }
16131
16132 void LoadEngineSnapshotSingle(void)
16133 {
16134   LoadSnapshotSingle();
16135
16136   LoadEngineSnapshotValues();
16137 }
16138
16139 static void LoadEngineSnapshot_Undo(int steps)
16140 {
16141   LoadSnapshotFromList_Older(steps);
16142
16143   LoadEngineSnapshotValues();
16144 }
16145
16146 static void LoadEngineSnapshot_Redo(int steps)
16147 {
16148   LoadSnapshotFromList_Newer(steps);
16149
16150   LoadEngineSnapshotValues();
16151 }
16152
16153 boolean CheckEngineSnapshotSingle(void)
16154 {
16155   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16156           snapshot_level_nr == level_nr);
16157 }
16158
16159 boolean CheckEngineSnapshotList(void)
16160 {
16161   return CheckSnapshotList();
16162 }
16163
16164
16165 // ---------- new game button stuff -------------------------------------------
16166
16167 static struct
16168 {
16169   int graphic;
16170   struct XY *pos;
16171   int gadget_id;
16172   boolean *setup_value;
16173   boolean allowed_on_tape;
16174   boolean is_touch_button;
16175   char *infotext;
16176 } gamebutton_info[NUM_GAME_BUTTONS] =
16177 {
16178   {
16179     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16180     GAME_CTRL_ID_STOP,                          NULL,
16181     TRUE, FALSE,                                "stop game"
16182   },
16183   {
16184     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16185     GAME_CTRL_ID_PAUSE,                         NULL,
16186     TRUE, FALSE,                                "pause game"
16187   },
16188   {
16189     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16190     GAME_CTRL_ID_PLAY,                          NULL,
16191     TRUE, FALSE,                                "play game"
16192   },
16193   {
16194     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16195     GAME_CTRL_ID_UNDO,                          NULL,
16196     TRUE, FALSE,                                "undo step"
16197   },
16198   {
16199     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16200     GAME_CTRL_ID_REDO,                          NULL,
16201     TRUE, FALSE,                                "redo step"
16202   },
16203   {
16204     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16205     GAME_CTRL_ID_SAVE,                          NULL,
16206     TRUE, FALSE,                                "save game"
16207   },
16208   {
16209     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16210     GAME_CTRL_ID_PAUSE2,                        NULL,
16211     TRUE, FALSE,                                "pause game"
16212   },
16213   {
16214     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16215     GAME_CTRL_ID_LOAD,                          NULL,
16216     TRUE, FALSE,                                "load game"
16217   },
16218   {
16219     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16220     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16221     FALSE, FALSE,                               "stop game"
16222   },
16223   {
16224     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16225     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16226     FALSE, FALSE,                               "pause game"
16227   },
16228   {
16229     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16230     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16231     FALSE, FALSE,                               "play game"
16232   },
16233   {
16234     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16235     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16236     FALSE, TRUE,                                "stop game"
16237   },
16238   {
16239     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16240     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16241     FALSE, TRUE,                                "pause game"
16242   },
16243   {
16244     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16245     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16246     TRUE, FALSE,                                "background music on/off"
16247   },
16248   {
16249     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16250     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16251     TRUE, FALSE,                                "sound loops on/off"
16252   },
16253   {
16254     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16255     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16256     TRUE, FALSE,                                "normal sounds on/off"
16257   },
16258   {
16259     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16260     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16261     FALSE, FALSE,                               "background music on/off"
16262   },
16263   {
16264     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16265     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16266     FALSE, FALSE,                               "sound loops on/off"
16267   },
16268   {
16269     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16270     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16271     FALSE, FALSE,                               "normal sounds on/off"
16272   }
16273 };
16274
16275 void CreateGameButtons(void)
16276 {
16277   int i;
16278
16279   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16280   {
16281     int graphic = gamebutton_info[i].graphic;
16282     struct GraphicInfo *gfx = &graphic_info[graphic];
16283     struct XY *pos = gamebutton_info[i].pos;
16284     struct GadgetInfo *gi;
16285     int button_type;
16286     boolean checked;
16287     unsigned int event_mask;
16288     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16289     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16290     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16291     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16292     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16293     int gd_x   = gfx->src_x;
16294     int gd_y   = gfx->src_y;
16295     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16296     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16297     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16298     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16299     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16300     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16301     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16302     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16303     int id = i;
16304
16305     // do not use touch buttons if overlay touch buttons are disabled
16306     if (is_touch_button && !setup.touch.overlay_buttons)
16307       continue;
16308
16309     if (gfx->bitmap == NULL)
16310     {
16311       game_gadget[id] = NULL;
16312
16313       continue;
16314     }
16315
16316     if (id == GAME_CTRL_ID_STOP ||
16317         id == GAME_CTRL_ID_PANEL_STOP ||
16318         id == GAME_CTRL_ID_TOUCH_STOP ||
16319         id == GAME_CTRL_ID_PLAY ||
16320         id == GAME_CTRL_ID_PANEL_PLAY ||
16321         id == GAME_CTRL_ID_SAVE ||
16322         id == GAME_CTRL_ID_LOAD)
16323     {
16324       button_type = GD_TYPE_NORMAL_BUTTON;
16325       checked = FALSE;
16326       event_mask = GD_EVENT_RELEASED;
16327     }
16328     else if (id == GAME_CTRL_ID_UNDO ||
16329              id == GAME_CTRL_ID_REDO)
16330     {
16331       button_type = GD_TYPE_NORMAL_BUTTON;
16332       checked = FALSE;
16333       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16334     }
16335     else
16336     {
16337       button_type = GD_TYPE_CHECK_BUTTON;
16338       checked = (gamebutton_info[i].setup_value != NULL ?
16339                  *gamebutton_info[i].setup_value : FALSE);
16340       event_mask = GD_EVENT_PRESSED;
16341     }
16342
16343     gi = CreateGadget(GDI_CUSTOM_ID, id,
16344                       GDI_IMAGE_ID, graphic,
16345                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16346                       GDI_X, base_x + x,
16347                       GDI_Y, base_y + y,
16348                       GDI_WIDTH, gfx->width,
16349                       GDI_HEIGHT, gfx->height,
16350                       GDI_TYPE, button_type,
16351                       GDI_STATE, GD_BUTTON_UNPRESSED,
16352                       GDI_CHECKED, checked,
16353                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16354                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16355                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16356                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16357                       GDI_DIRECT_DRAW, FALSE,
16358                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16359                       GDI_EVENT_MASK, event_mask,
16360                       GDI_CALLBACK_ACTION, HandleGameButtons,
16361                       GDI_END);
16362
16363     if (gi == NULL)
16364       Fail("cannot create gadget");
16365
16366     game_gadget[id] = gi;
16367   }
16368 }
16369
16370 void FreeGameButtons(void)
16371 {
16372   int i;
16373
16374   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16375     FreeGadget(game_gadget[i]);
16376 }
16377
16378 static void UnmapGameButtonsAtSamePosition(int id)
16379 {
16380   int i;
16381
16382   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16383     if (i != id &&
16384         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16385         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16386       UnmapGadget(game_gadget[i]);
16387 }
16388
16389 static void UnmapGameButtonsAtSamePosition_All(void)
16390 {
16391   if (setup.show_load_save_buttons)
16392   {
16393     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16394     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16395     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16396   }
16397   else if (setup.show_undo_redo_buttons)
16398   {
16399     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16400     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16401     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16402   }
16403   else
16404   {
16405     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16406     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16407     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16408
16409     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16410     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16411     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16412   }
16413 }
16414
16415 void MapLoadSaveButtons(void)
16416 {
16417   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16418   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16419
16420   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16421   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16422 }
16423
16424 void MapUndoRedoButtons(void)
16425 {
16426   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16427   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16428
16429   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16430   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16431 }
16432
16433 void ModifyPauseButtons(void)
16434 {
16435   static int ids[] =
16436   {
16437     GAME_CTRL_ID_PAUSE,
16438     GAME_CTRL_ID_PAUSE2,
16439     GAME_CTRL_ID_PANEL_PAUSE,
16440     GAME_CTRL_ID_TOUCH_PAUSE,
16441     -1
16442   };
16443   int i;
16444
16445   for (i = 0; ids[i] > -1; i++)
16446     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16447 }
16448
16449 static void MapGameButtonsExt(boolean on_tape)
16450 {
16451   int i;
16452
16453   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16454   {
16455     if ((i == GAME_CTRL_ID_UNDO ||
16456          i == GAME_CTRL_ID_REDO) &&
16457         game_status != GAME_MODE_PLAYING)
16458       continue;
16459
16460     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16461       MapGadget(game_gadget[i]);
16462   }
16463
16464   UnmapGameButtonsAtSamePosition_All();
16465
16466   RedrawGameButtons();
16467 }
16468
16469 static void UnmapGameButtonsExt(boolean on_tape)
16470 {
16471   int i;
16472
16473   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16474     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16475       UnmapGadget(game_gadget[i]);
16476 }
16477
16478 static void RedrawGameButtonsExt(boolean on_tape)
16479 {
16480   int i;
16481
16482   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16483     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16484       RedrawGadget(game_gadget[i]);
16485 }
16486
16487 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16488 {
16489   if (gi == NULL)
16490     return;
16491
16492   gi->checked = state;
16493 }
16494
16495 static void RedrawSoundButtonGadget(int id)
16496 {
16497   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16498              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16499              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16500              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16501              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16502              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16503              id);
16504
16505   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16506   RedrawGadget(game_gadget[id2]);
16507 }
16508
16509 void MapGameButtons(void)
16510 {
16511   MapGameButtonsExt(FALSE);
16512 }
16513
16514 void UnmapGameButtons(void)
16515 {
16516   UnmapGameButtonsExt(FALSE);
16517 }
16518
16519 void RedrawGameButtons(void)
16520 {
16521   RedrawGameButtonsExt(FALSE);
16522 }
16523
16524 void MapGameButtonsOnTape(void)
16525 {
16526   MapGameButtonsExt(TRUE);
16527 }
16528
16529 void UnmapGameButtonsOnTape(void)
16530 {
16531   UnmapGameButtonsExt(TRUE);
16532 }
16533
16534 void RedrawGameButtonsOnTape(void)
16535 {
16536   RedrawGameButtonsExt(TRUE);
16537 }
16538
16539 static void GameUndoRedoExt(void)
16540 {
16541   ClearPlayerAction();
16542
16543   tape.pausing = TRUE;
16544
16545   RedrawPlayfield();
16546   UpdateAndDisplayGameControlValues();
16547
16548   DrawCompleteVideoDisplay();
16549   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16550   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16551   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16552
16553   ModifyPauseButtons();
16554
16555   BackToFront();
16556 }
16557
16558 static void GameUndo(int steps)
16559 {
16560   if (!CheckEngineSnapshotList())
16561     return;
16562
16563   int tape_property_bits = tape.property_bits;
16564
16565   LoadEngineSnapshot_Undo(steps);
16566
16567   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16568
16569   GameUndoRedoExt();
16570 }
16571
16572 static void GameRedo(int steps)
16573 {
16574   if (!CheckEngineSnapshotList())
16575     return;
16576
16577   int tape_property_bits = tape.property_bits;
16578
16579   LoadEngineSnapshot_Redo(steps);
16580
16581   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16582
16583   GameUndoRedoExt();
16584 }
16585
16586 static void HandleGameButtonsExt(int id, int button)
16587 {
16588   static boolean game_undo_executed = FALSE;
16589   int steps = BUTTON_STEPSIZE(button);
16590   boolean handle_game_buttons =
16591     (game_status == GAME_MODE_PLAYING ||
16592      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16593
16594   if (!handle_game_buttons)
16595     return;
16596
16597   switch (id)
16598   {
16599     case GAME_CTRL_ID_STOP:
16600     case GAME_CTRL_ID_PANEL_STOP:
16601     case GAME_CTRL_ID_TOUCH_STOP:
16602       TapeStopGame();
16603
16604       break;
16605
16606     case GAME_CTRL_ID_PAUSE:
16607     case GAME_CTRL_ID_PAUSE2:
16608     case GAME_CTRL_ID_PANEL_PAUSE:
16609     case GAME_CTRL_ID_TOUCH_PAUSE:
16610       if (network.enabled && game_status == GAME_MODE_PLAYING)
16611       {
16612         if (tape.pausing)
16613           SendToServer_ContinuePlaying();
16614         else
16615           SendToServer_PausePlaying();
16616       }
16617       else
16618         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16619
16620       game_undo_executed = FALSE;
16621
16622       break;
16623
16624     case GAME_CTRL_ID_PLAY:
16625     case GAME_CTRL_ID_PANEL_PLAY:
16626       if (game_status == GAME_MODE_MAIN)
16627       {
16628         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16629       }
16630       else if (tape.pausing)
16631       {
16632         if (network.enabled)
16633           SendToServer_ContinuePlaying();
16634         else
16635           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16636       }
16637       break;
16638
16639     case GAME_CTRL_ID_UNDO:
16640       // Important: When using "save snapshot when collecting an item" mode,
16641       // load last (current) snapshot for first "undo" after pressing "pause"
16642       // (else the last-but-one snapshot would be loaded, because the snapshot
16643       // pointer already points to the last snapshot when pressing "pause",
16644       // which is fine for "every step/move" mode, but not for "every collect")
16645       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16646           !game_undo_executed)
16647         steps--;
16648
16649       game_undo_executed = TRUE;
16650
16651       GameUndo(steps);
16652       break;
16653
16654     case GAME_CTRL_ID_REDO:
16655       GameRedo(steps);
16656       break;
16657
16658     case GAME_CTRL_ID_SAVE:
16659       TapeQuickSave();
16660       break;
16661
16662     case GAME_CTRL_ID_LOAD:
16663       TapeQuickLoad();
16664       break;
16665
16666     case SOUND_CTRL_ID_MUSIC:
16667     case SOUND_CTRL_ID_PANEL_MUSIC:
16668       if (setup.sound_music)
16669       { 
16670         setup.sound_music = FALSE;
16671
16672         FadeMusic();
16673       }
16674       else if (audio.music_available)
16675       { 
16676         setup.sound = setup.sound_music = TRUE;
16677
16678         SetAudioMode(setup.sound);
16679
16680         if (game_status == GAME_MODE_PLAYING)
16681           PlayLevelMusic();
16682       }
16683
16684       RedrawSoundButtonGadget(id);
16685
16686       break;
16687
16688     case SOUND_CTRL_ID_LOOPS:
16689     case SOUND_CTRL_ID_PANEL_LOOPS:
16690       if (setup.sound_loops)
16691         setup.sound_loops = FALSE;
16692       else if (audio.loops_available)
16693       {
16694         setup.sound = setup.sound_loops = TRUE;
16695
16696         SetAudioMode(setup.sound);
16697       }
16698
16699       RedrawSoundButtonGadget(id);
16700
16701       break;
16702
16703     case SOUND_CTRL_ID_SIMPLE:
16704     case SOUND_CTRL_ID_PANEL_SIMPLE:
16705       if (setup.sound_simple)
16706         setup.sound_simple = FALSE;
16707       else if (audio.sound_available)
16708       {
16709         setup.sound = setup.sound_simple = TRUE;
16710
16711         SetAudioMode(setup.sound);
16712       }
16713
16714       RedrawSoundButtonGadget(id);
16715
16716       break;
16717
16718     default:
16719       break;
16720   }
16721 }
16722
16723 static void HandleGameButtons(struct GadgetInfo *gi)
16724 {
16725   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16726 }
16727
16728 void HandleSoundButtonKeys(Key key)
16729 {
16730   if (key == setup.shortcut.sound_simple)
16731     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16732   else if (key == setup.shortcut.sound_loops)
16733     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16734   else if (key == setup.shortcut.sound_music)
16735     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16736 }