added support for graphic animations of empty space elements
[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_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_TIME)
2685       {
2686         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2687
2688         if (use_dynamic_size)           // use dynamic number of digits
2689         {
2690           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2691           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2692           int size2 = size1 + 1;
2693           int font1 = pos->font;
2694           int font2 = pos->font_alt;
2695
2696           size = (value < value_change ? size1 : size2);
2697           font = (value < value_change ? font1 : font2);
2698         }
2699       }
2700
2701       // correct text size if "digits" is zero or less
2702       if (size <= 0)
2703         size = strlen(int2str(value, size));
2704
2705       // dynamically correct text alignment
2706       pos->width = size * getFontWidth(font);
2707
2708       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2709                   int2str(value, size), font, mask_mode);
2710     }
2711     else if (type == TYPE_ELEMENT)
2712     {
2713       int element, graphic;
2714       Bitmap *src_bitmap;
2715       int src_x, src_y;
2716       int width, height;
2717       int dst_x = PANEL_XPOS(pos);
2718       int dst_y = PANEL_YPOS(pos);
2719
2720       if (value != EL_UNDEFINED && value != EL_EMPTY)
2721       {
2722         element = value;
2723         graphic = el2panelimg(value);
2724
2725 #if 0
2726         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2727               element, EL_NAME(element), size);
2728 #endif
2729
2730         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2731           size = TILESIZE;
2732
2733         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2734                               &src_x, &src_y);
2735
2736         width  = graphic_info[graphic].width  * size / TILESIZE;
2737         height = graphic_info[graphic].height * size / TILESIZE;
2738
2739         if (draw_masked)
2740           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2741                            dst_x, dst_y);
2742         else
2743           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2744                      dst_x, dst_y);
2745       }
2746     }
2747     else if (type == TYPE_GRAPHIC)
2748     {
2749       int graphic        = gpc->graphic;
2750       int graphic_active = gpc->graphic_active;
2751       Bitmap *src_bitmap;
2752       int src_x, src_y;
2753       int width, height;
2754       int dst_x = PANEL_XPOS(pos);
2755       int dst_y = PANEL_YPOS(pos);
2756       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2757                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2758
2759       if (graphic != IMG_UNDEFINED && !skip)
2760       {
2761         if (pos->style == STYLE_REVERSE)
2762           value = 100 - value;
2763
2764         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2765
2766         if (pos->direction & MV_HORIZONTAL)
2767         {
2768           width  = graphic_info[graphic_active].width * value / 100;
2769           height = graphic_info[graphic_active].height;
2770
2771           if (pos->direction == MV_LEFT)
2772           {
2773             src_x += graphic_info[graphic_active].width - width;
2774             dst_x += graphic_info[graphic_active].width - width;
2775           }
2776         }
2777         else
2778         {
2779           width  = graphic_info[graphic_active].width;
2780           height = graphic_info[graphic_active].height * value / 100;
2781
2782           if (pos->direction == MV_UP)
2783           {
2784             src_y += graphic_info[graphic_active].height - height;
2785             dst_y += graphic_info[graphic_active].height - height;
2786           }
2787         }
2788
2789         if (draw_masked)
2790           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2791                            dst_x, dst_y);
2792         else
2793           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2794                      dst_x, dst_y);
2795
2796         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2797
2798         if (pos->direction & MV_HORIZONTAL)
2799         {
2800           if (pos->direction == MV_RIGHT)
2801           {
2802             src_x += width;
2803             dst_x += width;
2804           }
2805           else
2806           {
2807             dst_x = PANEL_XPOS(pos);
2808           }
2809
2810           width = graphic_info[graphic].width - width;
2811         }
2812         else
2813         {
2814           if (pos->direction == MV_DOWN)
2815           {
2816             src_y += height;
2817             dst_y += height;
2818           }
2819           else
2820           {
2821             dst_y = PANEL_YPOS(pos);
2822           }
2823
2824           height = graphic_info[graphic].height - height;
2825         }
2826
2827         if (draw_masked)
2828           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2829                            dst_x, dst_y);
2830         else
2831           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2832                      dst_x, dst_y);
2833       }
2834     }
2835     else if (type == TYPE_STRING)
2836     {
2837       boolean active = (value != 0);
2838       char *state_normal = "off";
2839       char *state_active = "on";
2840       char *state = (active ? state_active : state_normal);
2841       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2842                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2843                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2844                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2845
2846       if (nr == GAME_PANEL_GRAVITY_STATE)
2847       {
2848         int font1 = pos->font;          // (used for normal state)
2849         int font2 = pos->font_alt;      // (used for active state)
2850
2851         font = (active ? font2 : font1);
2852       }
2853
2854       if (s != NULL)
2855       {
2856         char *s_cut;
2857
2858         if (size <= 0)
2859         {
2860           // don't truncate output if "chars" is zero or less
2861           size = strlen(s);
2862
2863           // dynamically correct text alignment
2864           pos->width = size * getFontWidth(font);
2865         }
2866
2867         s_cut = getStringCopyN(s, size);
2868
2869         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2870                     s_cut, font, mask_mode);
2871
2872         free(s_cut);
2873       }
2874     }
2875
2876     redraw_mask |= REDRAW_DOOR_1;
2877   }
2878
2879   SetGameStatus(GAME_MODE_PLAYING);
2880 }
2881
2882 void UpdateAndDisplayGameControlValues(void)
2883 {
2884   if (tape.deactivate_display)
2885     return;
2886
2887   UpdateGameControlValues();
2888   DisplayGameControlValues();
2889 }
2890
2891 void UpdateGameDoorValues(void)
2892 {
2893   UpdateGameControlValues();
2894 }
2895
2896 void DrawGameDoorValues(void)
2897 {
2898   DisplayGameControlValues();
2899 }
2900
2901
2902 // ============================================================================
2903 // InitGameEngine()
2904 // ----------------------------------------------------------------------------
2905 // initialize game engine due to level / tape version number
2906 // ============================================================================
2907
2908 static void InitGameEngine(void)
2909 {
2910   int i, j, k, l, x, y;
2911
2912   // set game engine from tape file when re-playing, else from level file
2913   game.engine_version = (tape.playing ? tape.engine_version :
2914                          level.game_version);
2915
2916   // set single or multi-player game mode (needed for re-playing tapes)
2917   game.team_mode = setup.team_mode;
2918
2919   if (tape.playing)
2920   {
2921     int num_players = 0;
2922
2923     for (i = 0; i < MAX_PLAYERS; i++)
2924       if (tape.player_participates[i])
2925         num_players++;
2926
2927     // multi-player tapes contain input data for more than one player
2928     game.team_mode = (num_players > 1);
2929   }
2930
2931 #if 0
2932   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2933         level.game_version);
2934   Debug("game:init:level", "          tape.file_version   == %06d",
2935         tape.file_version);
2936   Debug("game:init:level", "          tape.game_version   == %06d",
2937         tape.game_version);
2938   Debug("game:init:level", "          tape.engine_version == %06d",
2939         tape.engine_version);
2940   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2941         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2942 #endif
2943
2944   // --------------------------------------------------------------------------
2945   // set flags for bugs and changes according to active game engine version
2946   // --------------------------------------------------------------------------
2947
2948   /*
2949     Summary of bugfix:
2950     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2951
2952     Bug was introduced in version:
2953     2.0.1
2954
2955     Bug was fixed in version:
2956     4.2.0.0
2957
2958     Description:
2959     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2960     but the property "can fall" was missing, which caused some levels to be
2961     unsolvable. This was fixed in version 4.2.0.0.
2962
2963     Affected levels/tapes:
2964     An example for a tape that was fixed by this bugfix is tape 029 from the
2965     level set "rnd_sam_bateman".
2966     The wrong behaviour will still be used for all levels or tapes that were
2967     created/recorded with it. An example for this is tape 023 from the level
2968     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2969   */
2970
2971   boolean use_amoeba_dropping_cannot_fall_bug =
2972     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2973       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2974      (tape.playing &&
2975       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2976       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2977
2978   /*
2979     Summary of bugfix/change:
2980     Fixed move speed of elements entering or leaving magic wall.
2981
2982     Fixed/changed in version:
2983     2.0.1
2984
2985     Description:
2986     Before 2.0.1, move speed of elements entering or leaving magic wall was
2987     twice as fast as it is now.
2988     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2989
2990     Affected levels/tapes:
2991     The first condition is generally needed for all levels/tapes before version
2992     2.0.1, which might use the old behaviour before it was changed; known tapes
2993     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2994     The second condition is an exception from the above case and is needed for
2995     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2996     above, but before it was known that this change would break tapes like the
2997     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2998     although the engine version while recording maybe was before 2.0.1. There
2999     are a lot of tapes that are affected by this exception, like tape 006 from
3000     the level set "rnd_conor_mancone".
3001   */
3002
3003   boolean use_old_move_stepsize_for_magic_wall =
3004     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3005      !(tape.playing &&
3006        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3007        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3008
3009   /*
3010     Summary of bugfix/change:
3011     Fixed handling for custom elements that change when pushed by the player.
3012
3013     Fixed/changed in version:
3014     3.1.0
3015
3016     Description:
3017     Before 3.1.0, custom elements that "change when pushing" changed directly
3018     after the player started pushing them (until then handled in "DigField()").
3019     Since 3.1.0, these custom elements are not changed until the "pushing"
3020     move of the element is finished (now handled in "ContinueMoving()").
3021
3022     Affected levels/tapes:
3023     The first condition is generally needed for all levels/tapes before version
3024     3.1.0, which might use the old behaviour before it was changed; known tapes
3025     that are affected are some tapes from the level set "Walpurgis Gardens" by
3026     Jamie Cullen.
3027     The second condition is an exception from the above case and is needed for
3028     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3029     above (including some development versions of 3.1.0), but before it was
3030     known that this change would break tapes like the above and was fixed in
3031     3.1.1, so that the changed behaviour was active although the engine version
3032     while recording maybe was before 3.1.0. There is at least one tape that is
3033     affected by this exception, which is the tape for the one-level set "Bug
3034     Machine" by Juergen Bonhagen.
3035   */
3036
3037   game.use_change_when_pushing_bug =
3038     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3039      !(tape.playing &&
3040        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3041        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3042
3043   /*
3044     Summary of bugfix/change:
3045     Fixed handling for blocking the field the player leaves when moving.
3046
3047     Fixed/changed in version:
3048     3.1.1
3049
3050     Description:
3051     Before 3.1.1, when "block last field when moving" was enabled, the field
3052     the player is leaving when moving was blocked for the time of the move,
3053     and was directly unblocked afterwards. This resulted in the last field
3054     being blocked for exactly one less than the number of frames of one player
3055     move. Additionally, even when blocking was disabled, the last field was
3056     blocked for exactly one frame.
3057     Since 3.1.1, due to changes in player movement handling, the last field
3058     is not blocked at all when blocking is disabled. When blocking is enabled,
3059     the last field is blocked for exactly the number of frames of one player
3060     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3061     last field is blocked for exactly one more than the number of frames of
3062     one player move.
3063
3064     Affected levels/tapes:
3065     (!!! yet to be determined -- probably many !!!)
3066   */
3067
3068   game.use_block_last_field_bug =
3069     (game.engine_version < VERSION_IDENT(3,1,1,0));
3070
3071   /* various special flags and settings for native Emerald Mine game engine */
3072
3073   game_em.use_single_button =
3074     (game.engine_version > VERSION_IDENT(4,0,0,2));
3075
3076   game_em.use_snap_key_bug =
3077     (game.engine_version < VERSION_IDENT(4,0,1,0));
3078
3079   game_em.use_random_bug =
3080     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3081
3082   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3083
3084   game_em.use_old_explosions            = use_old_em_engine;
3085   game_em.use_old_android               = use_old_em_engine;
3086   game_em.use_old_push_elements         = use_old_em_engine;
3087   game_em.use_old_push_into_acid        = use_old_em_engine;
3088
3089   game_em.use_wrap_around               = !use_old_em_engine;
3090
3091   // --------------------------------------------------------------------------
3092
3093   // set maximal allowed number of custom element changes per game frame
3094   game.max_num_changes_per_frame = 1;
3095
3096   // default scan direction: scan playfield from top/left to bottom/right
3097   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3098
3099   // dynamically adjust element properties according to game engine version
3100   InitElementPropertiesEngine(game.engine_version);
3101
3102   // ---------- initialize special element properties -------------------------
3103
3104   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3105   if (use_amoeba_dropping_cannot_fall_bug)
3106     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3107
3108   // ---------- initialize player's initial move delay ------------------------
3109
3110   // dynamically adjust player properties according to level information
3111   for (i = 0; i < MAX_PLAYERS; i++)
3112     game.initial_move_delay_value[i] =
3113       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3114
3115   // dynamically adjust player properties according to game engine version
3116   for (i = 0; i < MAX_PLAYERS; i++)
3117     game.initial_move_delay[i] =
3118       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3119        game.initial_move_delay_value[i] : 0);
3120
3121   // ---------- initialize player's initial push delay ------------------------
3122
3123   // dynamically adjust player properties according to game engine version
3124   game.initial_push_delay_value =
3125     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3126
3127   // ---------- initialize changing elements ----------------------------------
3128
3129   // initialize changing elements information
3130   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3131   {
3132     struct ElementInfo *ei = &element_info[i];
3133
3134     // this pointer might have been changed in the level editor
3135     ei->change = &ei->change_page[0];
3136
3137     if (!IS_CUSTOM_ELEMENT(i))
3138     {
3139       ei->change->target_element = EL_EMPTY_SPACE;
3140       ei->change->delay_fixed = 0;
3141       ei->change->delay_random = 0;
3142       ei->change->delay_frames = 1;
3143     }
3144
3145     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3146     {
3147       ei->has_change_event[j] = FALSE;
3148
3149       ei->event_page_nr[j] = 0;
3150       ei->event_page[j] = &ei->change_page[0];
3151     }
3152   }
3153
3154   // add changing elements from pre-defined list
3155   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3156   {
3157     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3158     struct ElementInfo *ei = &element_info[ch_delay->element];
3159
3160     ei->change->target_element       = ch_delay->target_element;
3161     ei->change->delay_fixed          = ch_delay->change_delay;
3162
3163     ei->change->pre_change_function  = ch_delay->pre_change_function;
3164     ei->change->change_function      = ch_delay->change_function;
3165     ei->change->post_change_function = ch_delay->post_change_function;
3166
3167     ei->change->can_change = TRUE;
3168     ei->change->can_change_or_has_action = TRUE;
3169
3170     ei->has_change_event[CE_DELAY] = TRUE;
3171
3172     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3173     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3174   }
3175
3176   // ---------- initialize internal run-time variables ------------------------
3177
3178   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3179   {
3180     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3181
3182     for (j = 0; j < ei->num_change_pages; j++)
3183     {
3184       ei->change_page[j].can_change_or_has_action =
3185         (ei->change_page[j].can_change |
3186          ei->change_page[j].has_action);
3187     }
3188   }
3189
3190   // add change events from custom element configuration
3191   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3192   {
3193     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3194
3195     for (j = 0; j < ei->num_change_pages; j++)
3196     {
3197       if (!ei->change_page[j].can_change_or_has_action)
3198         continue;
3199
3200       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3201       {
3202         // only add event page for the first page found with this event
3203         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3204         {
3205           ei->has_change_event[k] = TRUE;
3206
3207           ei->event_page_nr[k] = j;
3208           ei->event_page[k] = &ei->change_page[j];
3209         }
3210       }
3211     }
3212   }
3213
3214   // ---------- initialize reference elements in change conditions ------------
3215
3216   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3217   {
3218     int element = EL_CUSTOM_START + i;
3219     struct ElementInfo *ei = &element_info[element];
3220
3221     for (j = 0; j < ei->num_change_pages; j++)
3222     {
3223       int trigger_element = ei->change_page[j].initial_trigger_element;
3224
3225       if (trigger_element >= EL_PREV_CE_8 &&
3226           trigger_element <= EL_NEXT_CE_8)
3227         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3228
3229       ei->change_page[j].trigger_element = trigger_element;
3230     }
3231   }
3232
3233   // ---------- initialize run-time trigger player and element ----------------
3234
3235   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3236   {
3237     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3238
3239     for (j = 0; j < ei->num_change_pages; j++)
3240     {
3241       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3242       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3243       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3244       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3245       ei->change_page[j].actual_trigger_ce_value = 0;
3246       ei->change_page[j].actual_trigger_ce_score = 0;
3247     }
3248   }
3249
3250   // ---------- initialize trigger events -------------------------------------
3251
3252   // initialize trigger events information
3253   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3254     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3255       trigger_events[i][j] = FALSE;
3256
3257   // add trigger events from element change event properties
3258   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3259   {
3260     struct ElementInfo *ei = &element_info[i];
3261
3262     for (j = 0; j < ei->num_change_pages; j++)
3263     {
3264       if (!ei->change_page[j].can_change_or_has_action)
3265         continue;
3266
3267       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3268       {
3269         int trigger_element = ei->change_page[j].trigger_element;
3270
3271         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3272         {
3273           if (ei->change_page[j].has_event[k])
3274           {
3275             if (IS_GROUP_ELEMENT(trigger_element))
3276             {
3277               struct ElementGroupInfo *group =
3278                 element_info[trigger_element].group;
3279
3280               for (l = 0; l < group->num_elements_resolved; l++)
3281                 trigger_events[group->element_resolved[l]][k] = TRUE;
3282             }
3283             else if (trigger_element == EL_ANY_ELEMENT)
3284               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3285                 trigger_events[l][k] = TRUE;
3286             else
3287               trigger_events[trigger_element][k] = TRUE;
3288           }
3289         }
3290       }
3291     }
3292   }
3293
3294   // ---------- initialize push delay -----------------------------------------
3295
3296   // initialize push delay values to default
3297   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3298   {
3299     if (!IS_CUSTOM_ELEMENT(i))
3300     {
3301       // set default push delay values (corrected since version 3.0.7-1)
3302       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3303       {
3304         element_info[i].push_delay_fixed = 2;
3305         element_info[i].push_delay_random = 8;
3306       }
3307       else
3308       {
3309         element_info[i].push_delay_fixed = 8;
3310         element_info[i].push_delay_random = 8;
3311       }
3312     }
3313   }
3314
3315   // set push delay value for certain elements from pre-defined list
3316   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3317   {
3318     int e = push_delay_list[i].element;
3319
3320     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3321     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3322   }
3323
3324   // set push delay value for Supaplex elements for newer engine versions
3325   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3326   {
3327     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3328     {
3329       if (IS_SP_ELEMENT(i))
3330       {
3331         // set SP push delay to just enough to push under a falling zonk
3332         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3333
3334         element_info[i].push_delay_fixed  = delay;
3335         element_info[i].push_delay_random = 0;
3336       }
3337     }
3338   }
3339
3340   // ---------- initialize move stepsize --------------------------------------
3341
3342   // initialize move stepsize values to default
3343   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3344     if (!IS_CUSTOM_ELEMENT(i))
3345       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3346
3347   // set move stepsize value for certain elements from pre-defined list
3348   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3349   {
3350     int e = move_stepsize_list[i].element;
3351
3352     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3353
3354     // set move stepsize value for certain elements for older engine versions
3355     if (use_old_move_stepsize_for_magic_wall)
3356     {
3357       if (e == EL_MAGIC_WALL_FILLING ||
3358           e == EL_MAGIC_WALL_EMPTYING ||
3359           e == EL_BD_MAGIC_WALL_FILLING ||
3360           e == EL_BD_MAGIC_WALL_EMPTYING)
3361         element_info[e].move_stepsize *= 2;
3362     }
3363   }
3364
3365   // ---------- initialize collect score --------------------------------------
3366
3367   // initialize collect score values for custom elements from initial value
3368   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3369     if (IS_CUSTOM_ELEMENT(i))
3370       element_info[i].collect_score = element_info[i].collect_score_initial;
3371
3372   // ---------- initialize collect count --------------------------------------
3373
3374   // initialize collect count values for non-custom elements
3375   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3376     if (!IS_CUSTOM_ELEMENT(i))
3377       element_info[i].collect_count_initial = 0;
3378
3379   // add collect count values for all elements from pre-defined list
3380   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3381     element_info[collect_count_list[i].element].collect_count_initial =
3382       collect_count_list[i].count;
3383
3384   // ---------- initialize access direction -----------------------------------
3385
3386   // initialize access direction values to default (access from every side)
3387   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3388     if (!IS_CUSTOM_ELEMENT(i))
3389       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3390
3391   // set access direction value for certain elements from pre-defined list
3392   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3393     element_info[access_direction_list[i].element].access_direction =
3394       access_direction_list[i].direction;
3395
3396   // ---------- initialize explosion content ----------------------------------
3397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3398   {
3399     if (IS_CUSTOM_ELEMENT(i))
3400       continue;
3401
3402     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3403     {
3404       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3405
3406       element_info[i].content.e[x][y] =
3407         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3408          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3409          i == EL_PLAYER_3 ? EL_EMERALD :
3410          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3411          i == EL_MOLE ? EL_EMERALD_RED :
3412          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3413          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3414          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3415          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3416          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3417          i == EL_WALL_EMERALD ? EL_EMERALD :
3418          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3419          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3420          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3421          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3422          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3423          i == EL_WALL_PEARL ? EL_PEARL :
3424          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3425          EL_EMPTY);
3426     }
3427   }
3428
3429   // ---------- initialize recursion detection --------------------------------
3430   recursion_loop_depth = 0;
3431   recursion_loop_detected = FALSE;
3432   recursion_loop_element = EL_UNDEFINED;
3433
3434   // ---------- initialize graphics engine ------------------------------------
3435   game.scroll_delay_value =
3436     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3437      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3438      !setup.forced_scroll_delay           ? 0 :
3439      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3440   game.scroll_delay_value =
3441     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3442
3443   // ---------- initialize game engine snapshots ------------------------------
3444   for (i = 0; i < MAX_PLAYERS; i++)
3445     game.snapshot.last_action[i] = 0;
3446   game.snapshot.changed_action = FALSE;
3447   game.snapshot.collected_item = FALSE;
3448   game.snapshot.mode =
3449     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3450      SNAPSHOT_MODE_EVERY_STEP :
3451      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3452      SNAPSHOT_MODE_EVERY_MOVE :
3453      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3454      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3455   game.snapshot.save_snapshot = FALSE;
3456
3457   // ---------- initialize level time for Supaplex engine ---------------------
3458   // Supaplex levels with time limit currently unsupported -- should be added
3459   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3460     level.time = 0;
3461
3462   // ---------- initialize flags for handling game actions --------------------
3463
3464   // set flags for game actions to default values
3465   game.use_key_actions = TRUE;
3466   game.use_mouse_actions = FALSE;
3467
3468   // when using Mirror Magic game engine, handle mouse events only
3469   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3470   {
3471     game.use_key_actions = FALSE;
3472     game.use_mouse_actions = TRUE;
3473   }
3474
3475   // check for custom elements with mouse click events
3476   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3477   {
3478     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3479     {
3480       int element = EL_CUSTOM_START + i;
3481
3482       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3483           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3484           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3485           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3486         game.use_mouse_actions = TRUE;
3487     }
3488   }
3489 }
3490
3491 static int get_num_special_action(int element, int action_first,
3492                                   int action_last)
3493 {
3494   int num_special_action = 0;
3495   int i, j;
3496
3497   for (i = action_first; i <= action_last; i++)
3498   {
3499     boolean found = FALSE;
3500
3501     for (j = 0; j < NUM_DIRECTIONS; j++)
3502       if (el_act_dir2img(element, i, j) !=
3503           el_act_dir2img(element, ACTION_DEFAULT, j))
3504         found = TRUE;
3505
3506     if (found)
3507       num_special_action++;
3508     else
3509       break;
3510   }
3511
3512   return num_special_action;
3513 }
3514
3515
3516 // ============================================================================
3517 // InitGame()
3518 // ----------------------------------------------------------------------------
3519 // initialize and start new game
3520 // ============================================================================
3521
3522 #if DEBUG_INIT_PLAYER
3523 static void DebugPrintPlayerStatus(char *message)
3524 {
3525   int i;
3526
3527   if (!options.debug)
3528     return;
3529
3530   Debug("game:init:player", "%s:", message);
3531
3532   for (i = 0; i < MAX_PLAYERS; i++)
3533   {
3534     struct PlayerInfo *player = &stored_player[i];
3535
3536     Debug("game:init:player",
3537           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3538           i + 1,
3539           player->present,
3540           player->connected,
3541           player->connected_locally,
3542           player->connected_network,
3543           player->active,
3544           (local_player == player ? " (local player)" : ""));
3545   }
3546 }
3547 #endif
3548
3549 void InitGame(void)
3550 {
3551   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3552   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3553   int fade_mask = REDRAW_FIELD;
3554
3555   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3556   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3557   int initial_move_dir = MV_DOWN;
3558   int i, j, x, y;
3559
3560   // required here to update video display before fading (FIX THIS)
3561   DrawMaskedBorder(REDRAW_DOOR_2);
3562
3563   if (!game.restart_level)
3564     CloseDoor(DOOR_CLOSE_1);
3565
3566   SetGameStatus(GAME_MODE_PLAYING);
3567
3568   if (level_editor_test_game)
3569     FadeSkipNextFadeOut();
3570   else
3571     FadeSetEnterScreen();
3572
3573   if (CheckFadeAll())
3574     fade_mask = REDRAW_ALL;
3575
3576   FadeLevelSoundsAndMusic();
3577
3578   ExpireSoundLoops(TRUE);
3579
3580   FadeOut(fade_mask);
3581
3582   if (level_editor_test_game)
3583     FadeSkipNextFadeIn();
3584
3585   // needed if different viewport properties defined for playing
3586   ChangeViewportPropertiesIfNeeded();
3587
3588   ClearField();
3589
3590   DrawCompleteVideoDisplay();
3591
3592   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3593
3594   InitGameEngine();
3595   InitGameControlValues();
3596
3597   if (tape.recording)
3598   {
3599     // initialize tape actions from game when recording tape
3600     tape.use_key_actions   = game.use_key_actions;
3601     tape.use_mouse_actions = game.use_mouse_actions;
3602
3603     // initialize visible playfield size when recording tape (for team mode)
3604     tape.scr_fieldx = SCR_FIELDX;
3605     tape.scr_fieldy = SCR_FIELDY;
3606   }
3607
3608   // don't play tapes over network
3609   network_playing = (network.enabled && !tape.playing);
3610
3611   for (i = 0; i < MAX_PLAYERS; i++)
3612   {
3613     struct PlayerInfo *player = &stored_player[i];
3614
3615     player->index_nr = i;
3616     player->index_bit = (1 << i);
3617     player->element_nr = EL_PLAYER_1 + i;
3618
3619     player->present = FALSE;
3620     player->active = FALSE;
3621     player->mapped = FALSE;
3622
3623     player->killed = FALSE;
3624     player->reanimated = FALSE;
3625     player->buried = FALSE;
3626
3627     player->action = 0;
3628     player->effective_action = 0;
3629     player->programmed_action = 0;
3630     player->snap_action = 0;
3631
3632     player->mouse_action.lx = 0;
3633     player->mouse_action.ly = 0;
3634     player->mouse_action.button = 0;
3635     player->mouse_action.button_hint = 0;
3636
3637     player->effective_mouse_action.lx = 0;
3638     player->effective_mouse_action.ly = 0;
3639     player->effective_mouse_action.button = 0;
3640     player->effective_mouse_action.button_hint = 0;
3641
3642     for (j = 0; j < MAX_NUM_KEYS; j++)
3643       player->key[j] = FALSE;
3644
3645     player->num_white_keys = 0;
3646
3647     player->dynabomb_count = 0;
3648     player->dynabomb_size = 1;
3649     player->dynabombs_left = 0;
3650     player->dynabomb_xl = FALSE;
3651
3652     player->MovDir = initial_move_dir;
3653     player->MovPos = 0;
3654     player->GfxPos = 0;
3655     player->GfxDir = initial_move_dir;
3656     player->GfxAction = ACTION_DEFAULT;
3657     player->Frame = 0;
3658     player->StepFrame = 0;
3659
3660     player->initial_element = player->element_nr;
3661     player->artwork_element =
3662       (level.use_artwork_element[i] ? level.artwork_element[i] :
3663        player->element_nr);
3664     player->use_murphy = FALSE;
3665
3666     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3667     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3668
3669     player->gravity = level.initial_player_gravity[i];
3670
3671     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3672
3673     player->actual_frame_counter = 0;
3674
3675     player->step_counter = 0;
3676
3677     player->last_move_dir = initial_move_dir;
3678
3679     player->is_active = FALSE;
3680
3681     player->is_waiting = FALSE;
3682     player->is_moving = FALSE;
3683     player->is_auto_moving = FALSE;
3684     player->is_digging = FALSE;
3685     player->is_snapping = FALSE;
3686     player->is_collecting = FALSE;
3687     player->is_pushing = FALSE;
3688     player->is_switching = FALSE;
3689     player->is_dropping = FALSE;
3690     player->is_dropping_pressed = FALSE;
3691
3692     player->is_bored = FALSE;
3693     player->is_sleeping = FALSE;
3694
3695     player->was_waiting = TRUE;
3696     player->was_moving = FALSE;
3697     player->was_snapping = FALSE;
3698     player->was_dropping = FALSE;
3699
3700     player->force_dropping = FALSE;
3701
3702     player->frame_counter_bored = -1;
3703     player->frame_counter_sleeping = -1;
3704
3705     player->anim_delay_counter = 0;
3706     player->post_delay_counter = 0;
3707
3708     player->dir_waiting = initial_move_dir;
3709     player->action_waiting = ACTION_DEFAULT;
3710     player->last_action_waiting = ACTION_DEFAULT;
3711     player->special_action_bored = ACTION_DEFAULT;
3712     player->special_action_sleeping = ACTION_DEFAULT;
3713
3714     player->switch_x = -1;
3715     player->switch_y = -1;
3716
3717     player->drop_x = -1;
3718     player->drop_y = -1;
3719
3720     player->show_envelope = 0;
3721
3722     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3723
3724     player->push_delay       = -1;      // initialized when pushing starts
3725     player->push_delay_value = game.initial_push_delay_value;
3726
3727     player->drop_delay = 0;
3728     player->drop_pressed_delay = 0;
3729
3730     player->last_jx = -1;
3731     player->last_jy = -1;
3732     player->jx = -1;
3733     player->jy = -1;
3734
3735     player->shield_normal_time_left = 0;
3736     player->shield_deadly_time_left = 0;
3737
3738     player->last_removed_element = EL_UNDEFINED;
3739
3740     player->inventory_infinite_element = EL_UNDEFINED;
3741     player->inventory_size = 0;
3742
3743     if (level.use_initial_inventory[i])
3744     {
3745       for (j = 0; j < level.initial_inventory_size[i]; j++)
3746       {
3747         int element = level.initial_inventory_content[i][j];
3748         int collect_count = element_info[element].collect_count_initial;
3749         int k;
3750
3751         if (!IS_CUSTOM_ELEMENT(element))
3752           collect_count = 1;
3753
3754         if (collect_count == 0)
3755           player->inventory_infinite_element = element;
3756         else
3757           for (k = 0; k < collect_count; k++)
3758             if (player->inventory_size < MAX_INVENTORY_SIZE)
3759               player->inventory_element[player->inventory_size++] = element;
3760       }
3761     }
3762
3763     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3764     SnapField(player, 0, 0);
3765
3766     map_player_action[i] = i;
3767   }
3768
3769   network_player_action_received = FALSE;
3770
3771   // initial null action
3772   if (network_playing)
3773     SendToServer_MovePlayer(MV_NONE);
3774
3775   FrameCounter = 0;
3776   TimeFrames = 0;
3777   TimePlayed = 0;
3778   TimeLeft = level.time;
3779   TapeTime = 0;
3780
3781   ScreenMovDir = MV_NONE;
3782   ScreenMovPos = 0;
3783   ScreenGfxPos = 0;
3784
3785   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3786
3787   game.robot_wheel_x = -1;
3788   game.robot_wheel_y = -1;
3789
3790   game.exit_x = -1;
3791   game.exit_y = -1;
3792
3793   game.all_players_gone = FALSE;
3794
3795   game.LevelSolved = FALSE;
3796   game.GameOver = FALSE;
3797
3798   game.GamePlayed = !tape.playing;
3799
3800   game.LevelSolved_GameWon = FALSE;
3801   game.LevelSolved_GameEnd = FALSE;
3802   game.LevelSolved_SaveTape = FALSE;
3803   game.LevelSolved_SaveScore = FALSE;
3804
3805   game.LevelSolved_CountingTime = 0;
3806   game.LevelSolved_CountingScore = 0;
3807   game.LevelSolved_CountingHealth = 0;
3808
3809   game.panel.active = TRUE;
3810
3811   game.no_time_limit = (level.time == 0);
3812
3813   game.yamyam_content_nr = 0;
3814   game.robot_wheel_active = FALSE;
3815   game.magic_wall_active = FALSE;
3816   game.magic_wall_time_left = 0;
3817   game.light_time_left = 0;
3818   game.timegate_time_left = 0;
3819   game.switchgate_pos = 0;
3820   game.wind_direction = level.wind_direction_initial;
3821
3822   game.time_final = 0;
3823   game.score_time_final = 0;
3824
3825   game.score = 0;
3826   game.score_final = 0;
3827
3828   game.health = MAX_HEALTH;
3829   game.health_final = MAX_HEALTH;
3830
3831   game.gems_still_needed = level.gems_needed;
3832   game.sokoban_fields_still_needed = 0;
3833   game.sokoban_objects_still_needed = 0;
3834   game.lights_still_needed = 0;
3835   game.players_still_needed = 0;
3836   game.friends_still_needed = 0;
3837
3838   game.lenses_time_left = 0;
3839   game.magnify_time_left = 0;
3840
3841   game.ball_active = level.ball_active_initial;
3842   game.ball_content_nr = 0;
3843
3844   game.explosions_delayed = TRUE;
3845
3846   game.envelope_active = FALSE;
3847
3848   for (i = 0; i < NUM_BELTS; i++)
3849   {
3850     game.belt_dir[i] = MV_NONE;
3851     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3852   }
3853
3854   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3855     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3856
3857 #if DEBUG_INIT_PLAYER
3858   DebugPrintPlayerStatus("Player status at level initialization");
3859 #endif
3860
3861   SCAN_PLAYFIELD(x, y)
3862   {
3863     Tile[x][y] = Last[x][y] = level.field[x][y];
3864     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3865     ChangeDelay[x][y] = 0;
3866     ChangePage[x][y] = -1;
3867     CustomValue[x][y] = 0;              // initialized in InitField()
3868     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3869     AmoebaNr[x][y] = 0;
3870     WasJustMoving[x][y] = 0;
3871     WasJustFalling[x][y] = 0;
3872     CheckCollision[x][y] = 0;
3873     CheckImpact[x][y] = 0;
3874     Stop[x][y] = FALSE;
3875     Pushed[x][y] = FALSE;
3876
3877     ChangeCount[x][y] = 0;
3878     ChangeEvent[x][y] = -1;
3879
3880     ExplodePhase[x][y] = 0;
3881     ExplodeDelay[x][y] = 0;
3882     ExplodeField[x][y] = EX_TYPE_NONE;
3883
3884     RunnerVisit[x][y] = 0;
3885     PlayerVisit[x][y] = 0;
3886
3887     GfxFrame[x][y] = 0;
3888     GfxRandom[x][y] = INIT_GFX_RANDOM();
3889     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3890     GfxElement[x][y] = EL_UNDEFINED;
3891     GfxElementEmpty[x][y] = EL_EMPTY;
3892     GfxAction[x][y] = ACTION_DEFAULT;
3893     GfxDir[x][y] = MV_NONE;
3894     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3895   }
3896
3897   SCAN_PLAYFIELD(x, y)
3898   {
3899     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3900       emulate_bd = FALSE;
3901     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3902       emulate_sp = FALSE;
3903
3904     InitField(x, y, TRUE);
3905
3906     ResetGfxAnimation(x, y);
3907   }
3908
3909   InitBeltMovement();
3910
3911   for (i = 0; i < MAX_PLAYERS; i++)
3912   {
3913     struct PlayerInfo *player = &stored_player[i];
3914
3915     // set number of special actions for bored and sleeping animation
3916     player->num_special_action_bored =
3917       get_num_special_action(player->artwork_element,
3918                              ACTION_BORING_1, ACTION_BORING_LAST);
3919     player->num_special_action_sleeping =
3920       get_num_special_action(player->artwork_element,
3921                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3922   }
3923
3924   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3925                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3926
3927   // initialize type of slippery elements
3928   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3929   {
3930     if (!IS_CUSTOM_ELEMENT(i))
3931     {
3932       // default: elements slip down either to the left or right randomly
3933       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3934
3935       // SP style elements prefer to slip down on the left side
3936       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3937         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3938
3939       // BD style elements prefer to slip down on the left side
3940       if (game.emulation == EMU_BOULDERDASH)
3941         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3942     }
3943   }
3944
3945   // initialize explosion and ignition delay
3946   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3947   {
3948     if (!IS_CUSTOM_ELEMENT(i))
3949     {
3950       int num_phase = 8;
3951       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3952                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3953                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3954       int last_phase = (num_phase + 1) * delay;
3955       int half_phase = (num_phase / 2) * delay;
3956
3957       element_info[i].explosion_delay = last_phase - 1;
3958       element_info[i].ignition_delay = half_phase;
3959
3960       if (i == EL_BLACK_ORB)
3961         element_info[i].ignition_delay = 1;
3962     }
3963   }
3964
3965   // correct non-moving belts to start moving left
3966   for (i = 0; i < NUM_BELTS; i++)
3967     if (game.belt_dir[i] == MV_NONE)
3968       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3969
3970 #if USE_NEW_PLAYER_ASSIGNMENTS
3971   // use preferred player also in local single-player mode
3972   if (!network.enabled && !game.team_mode)
3973   {
3974     int new_index_nr = setup.network_player_nr;
3975
3976     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3977     {
3978       for (i = 0; i < MAX_PLAYERS; i++)
3979         stored_player[i].connected_locally = FALSE;
3980
3981       stored_player[new_index_nr].connected_locally = TRUE;
3982     }
3983   }
3984
3985   for (i = 0; i < MAX_PLAYERS; i++)
3986   {
3987     stored_player[i].connected = FALSE;
3988
3989     // in network game mode, the local player might not be the first player
3990     if (stored_player[i].connected_locally)
3991       local_player = &stored_player[i];
3992   }
3993
3994   if (!network.enabled)
3995     local_player->connected = TRUE;
3996
3997   if (tape.playing)
3998   {
3999     for (i = 0; i < MAX_PLAYERS; i++)
4000       stored_player[i].connected = tape.player_participates[i];
4001   }
4002   else if (network.enabled)
4003   {
4004     // add team mode players connected over the network (needed for correct
4005     // assignment of player figures from level to locally playing players)
4006
4007     for (i = 0; i < MAX_PLAYERS; i++)
4008       if (stored_player[i].connected_network)
4009         stored_player[i].connected = TRUE;
4010   }
4011   else if (game.team_mode)
4012   {
4013     // try to guess locally connected team mode players (needed for correct
4014     // assignment of player figures from level to locally playing players)
4015
4016     for (i = 0; i < MAX_PLAYERS; i++)
4017       if (setup.input[i].use_joystick ||
4018           setup.input[i].key.left != KSYM_UNDEFINED)
4019         stored_player[i].connected = TRUE;
4020   }
4021
4022 #if DEBUG_INIT_PLAYER
4023   DebugPrintPlayerStatus("Player status after level initialization");
4024 #endif
4025
4026 #if DEBUG_INIT_PLAYER
4027   Debug("game:init:player", "Reassigning players ...");
4028 #endif
4029
4030   // check if any connected player was not found in playfield
4031   for (i = 0; i < MAX_PLAYERS; i++)
4032   {
4033     struct PlayerInfo *player = &stored_player[i];
4034
4035     if (player->connected && !player->present)
4036     {
4037       struct PlayerInfo *field_player = NULL;
4038
4039 #if DEBUG_INIT_PLAYER
4040       Debug("game:init:player",
4041             "- looking for field player for player %d ...", i + 1);
4042 #endif
4043
4044       // assign first free player found that is present in the playfield
4045
4046       // first try: look for unmapped playfield player that is not connected
4047       for (j = 0; j < MAX_PLAYERS; j++)
4048         if (field_player == NULL &&
4049             stored_player[j].present &&
4050             !stored_player[j].mapped &&
4051             !stored_player[j].connected)
4052           field_player = &stored_player[j];
4053
4054       // second try: look for *any* unmapped playfield player
4055       for (j = 0; j < MAX_PLAYERS; j++)
4056         if (field_player == NULL &&
4057             stored_player[j].present &&
4058             !stored_player[j].mapped)
4059           field_player = &stored_player[j];
4060
4061       if (field_player != NULL)
4062       {
4063         int jx = field_player->jx, jy = field_player->jy;
4064
4065 #if DEBUG_INIT_PLAYER
4066         Debug("game:init:player", "- found player %d",
4067               field_player->index_nr + 1);
4068 #endif
4069
4070         player->present = FALSE;
4071         player->active = FALSE;
4072
4073         field_player->present = TRUE;
4074         field_player->active = TRUE;
4075
4076         /*
4077         player->initial_element = field_player->initial_element;
4078         player->artwork_element = field_player->artwork_element;
4079
4080         player->block_last_field       = field_player->block_last_field;
4081         player->block_delay_adjustment = field_player->block_delay_adjustment;
4082         */
4083
4084         StorePlayer[jx][jy] = field_player->element_nr;
4085
4086         field_player->jx = field_player->last_jx = jx;
4087         field_player->jy = field_player->last_jy = jy;
4088
4089         if (local_player == player)
4090           local_player = field_player;
4091
4092         map_player_action[field_player->index_nr] = i;
4093
4094         field_player->mapped = TRUE;
4095
4096 #if DEBUG_INIT_PLAYER
4097         Debug("game:init:player", "- map_player_action[%d] == %d",
4098               field_player->index_nr + 1, i + 1);
4099 #endif
4100       }
4101     }
4102
4103     if (player->connected && player->present)
4104       player->mapped = TRUE;
4105   }
4106
4107 #if DEBUG_INIT_PLAYER
4108   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4109 #endif
4110
4111 #else
4112
4113   // check if any connected player was not found in playfield
4114   for (i = 0; i < MAX_PLAYERS; i++)
4115   {
4116     struct PlayerInfo *player = &stored_player[i];
4117
4118     if (player->connected && !player->present)
4119     {
4120       for (j = 0; j < MAX_PLAYERS; j++)
4121       {
4122         struct PlayerInfo *field_player = &stored_player[j];
4123         int jx = field_player->jx, jy = field_player->jy;
4124
4125         // assign first free player found that is present in the playfield
4126         if (field_player->present && !field_player->connected)
4127         {
4128           player->present = TRUE;
4129           player->active = TRUE;
4130
4131           field_player->present = FALSE;
4132           field_player->active = FALSE;
4133
4134           player->initial_element = field_player->initial_element;
4135           player->artwork_element = field_player->artwork_element;
4136
4137           player->block_last_field       = field_player->block_last_field;
4138           player->block_delay_adjustment = field_player->block_delay_adjustment;
4139
4140           StorePlayer[jx][jy] = player->element_nr;
4141
4142           player->jx = player->last_jx = jx;
4143           player->jy = player->last_jy = jy;
4144
4145           break;
4146         }
4147       }
4148     }
4149   }
4150 #endif
4151
4152 #if 0
4153   Debug("game:init:player", "local_player->present == %d",
4154         local_player->present);
4155 #endif
4156
4157   // set focus to local player for network games, else to all players
4158   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4159   game.centered_player_nr_next = game.centered_player_nr;
4160   game.set_centered_player = FALSE;
4161   game.set_centered_player_wrap = FALSE;
4162
4163   if (network_playing && tape.recording)
4164   {
4165     // store client dependent player focus when recording network games
4166     tape.centered_player_nr_next = game.centered_player_nr_next;
4167     tape.set_centered_player = TRUE;
4168   }
4169
4170   if (tape.playing)
4171   {
4172     // when playing a tape, eliminate all players who do not participate
4173
4174 #if USE_NEW_PLAYER_ASSIGNMENTS
4175
4176     if (!game.team_mode)
4177     {
4178       for (i = 0; i < MAX_PLAYERS; i++)
4179       {
4180         if (stored_player[i].active &&
4181             !tape.player_participates[map_player_action[i]])
4182         {
4183           struct PlayerInfo *player = &stored_player[i];
4184           int jx = player->jx, jy = player->jy;
4185
4186 #if DEBUG_INIT_PLAYER
4187           Debug("game:init:player", "Removing player %d at (%d, %d)",
4188                 i + 1, jx, jy);
4189 #endif
4190
4191           player->active = FALSE;
4192           StorePlayer[jx][jy] = 0;
4193           Tile[jx][jy] = EL_EMPTY;
4194         }
4195       }
4196     }
4197
4198 #else
4199
4200     for (i = 0; i < MAX_PLAYERS; i++)
4201     {
4202       if (stored_player[i].active &&
4203           !tape.player_participates[i])
4204       {
4205         struct PlayerInfo *player = &stored_player[i];
4206         int jx = player->jx, jy = player->jy;
4207
4208         player->active = FALSE;
4209         StorePlayer[jx][jy] = 0;
4210         Tile[jx][jy] = EL_EMPTY;
4211       }
4212     }
4213 #endif
4214   }
4215   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4216   {
4217     // when in single player mode, eliminate all but the local player
4218
4219     for (i = 0; i < MAX_PLAYERS; i++)
4220     {
4221       struct PlayerInfo *player = &stored_player[i];
4222
4223       if (player->active && player != local_player)
4224       {
4225         int jx = player->jx, jy = player->jy;
4226
4227         player->active = FALSE;
4228         player->present = FALSE;
4229
4230         StorePlayer[jx][jy] = 0;
4231         Tile[jx][jy] = EL_EMPTY;
4232       }
4233     }
4234   }
4235
4236   for (i = 0; i < MAX_PLAYERS; i++)
4237     if (stored_player[i].active)
4238       game.players_still_needed++;
4239
4240   if (level.solved_by_one_player)
4241     game.players_still_needed = 1;
4242
4243   // when recording the game, store which players take part in the game
4244   if (tape.recording)
4245   {
4246 #if USE_NEW_PLAYER_ASSIGNMENTS
4247     for (i = 0; i < MAX_PLAYERS; i++)
4248       if (stored_player[i].connected)
4249         tape.player_participates[i] = TRUE;
4250 #else
4251     for (i = 0; i < MAX_PLAYERS; i++)
4252       if (stored_player[i].active)
4253         tape.player_participates[i] = TRUE;
4254 #endif
4255   }
4256
4257 #if DEBUG_INIT_PLAYER
4258   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4259 #endif
4260
4261   if (BorderElement == EL_EMPTY)
4262   {
4263     SBX_Left = 0;
4264     SBX_Right = lev_fieldx - SCR_FIELDX;
4265     SBY_Upper = 0;
4266     SBY_Lower = lev_fieldy - SCR_FIELDY;
4267   }
4268   else
4269   {
4270     SBX_Left = -1;
4271     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4272     SBY_Upper = -1;
4273     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4274   }
4275
4276   if (full_lev_fieldx <= SCR_FIELDX)
4277     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4278   if (full_lev_fieldy <= SCR_FIELDY)
4279     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4280
4281   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4282     SBX_Left--;
4283   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4284     SBY_Upper--;
4285
4286   // if local player not found, look for custom element that might create
4287   // the player (make some assumptions about the right custom element)
4288   if (!local_player->present)
4289   {
4290     int start_x = 0, start_y = 0;
4291     int found_rating = 0;
4292     int found_element = EL_UNDEFINED;
4293     int player_nr = local_player->index_nr;
4294
4295     SCAN_PLAYFIELD(x, y)
4296     {
4297       int element = Tile[x][y];
4298       int content;
4299       int xx, yy;
4300       boolean is_player;
4301
4302       if (level.use_start_element[player_nr] &&
4303           level.start_element[player_nr] == element &&
4304           found_rating < 4)
4305       {
4306         start_x = x;
4307         start_y = y;
4308
4309         found_rating = 4;
4310         found_element = element;
4311       }
4312
4313       if (!IS_CUSTOM_ELEMENT(element))
4314         continue;
4315
4316       if (CAN_CHANGE(element))
4317       {
4318         for (i = 0; i < element_info[element].num_change_pages; i++)
4319         {
4320           // check for player created from custom element as single target
4321           content = element_info[element].change_page[i].target_element;
4322           is_player = IS_PLAYER_ELEMENT(content);
4323
4324           if (is_player && (found_rating < 3 ||
4325                             (found_rating == 3 && element < found_element)))
4326           {
4327             start_x = x;
4328             start_y = y;
4329
4330             found_rating = 3;
4331             found_element = element;
4332           }
4333         }
4334       }
4335
4336       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4337       {
4338         // check for player created from custom element as explosion content
4339         content = element_info[element].content.e[xx][yy];
4340         is_player = IS_PLAYER_ELEMENT(content);
4341
4342         if (is_player && (found_rating < 2 ||
4343                           (found_rating == 2 && element < found_element)))
4344         {
4345           start_x = x + xx - 1;
4346           start_y = y + yy - 1;
4347
4348           found_rating = 2;
4349           found_element = element;
4350         }
4351
4352         if (!CAN_CHANGE(element))
4353           continue;
4354
4355         for (i = 0; i < element_info[element].num_change_pages; i++)
4356         {
4357           // check for player created from custom element as extended target
4358           content =
4359             element_info[element].change_page[i].target_content.e[xx][yy];
4360
4361           is_player = IS_PLAYER_ELEMENT(content);
4362
4363           if (is_player && (found_rating < 1 ||
4364                             (found_rating == 1 && element < found_element)))
4365           {
4366             start_x = x + xx - 1;
4367             start_y = y + yy - 1;
4368
4369             found_rating = 1;
4370             found_element = element;
4371           }
4372         }
4373       }
4374     }
4375
4376     scroll_x = SCROLL_POSITION_X(start_x);
4377     scroll_y = SCROLL_POSITION_Y(start_y);
4378   }
4379   else
4380   {
4381     scroll_x = SCROLL_POSITION_X(local_player->jx);
4382     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4383   }
4384
4385   // !!! FIX THIS (START) !!!
4386   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4387   {
4388     InitGameEngine_EM();
4389   }
4390   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4391   {
4392     InitGameEngine_SP();
4393   }
4394   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4395   {
4396     InitGameEngine_MM();
4397   }
4398   else
4399   {
4400     DrawLevel(REDRAW_FIELD);
4401     DrawAllPlayers();
4402
4403     // after drawing the level, correct some elements
4404     if (game.timegate_time_left == 0)
4405       CloseAllOpenTimegates();
4406   }
4407
4408   // blit playfield from scroll buffer to normal back buffer for fading in
4409   BlitScreenToBitmap(backbuffer);
4410   // !!! FIX THIS (END) !!!
4411
4412   DrawMaskedBorder(fade_mask);
4413
4414   FadeIn(fade_mask);
4415
4416 #if 1
4417   // full screen redraw is required at this point in the following cases:
4418   // - special editor door undrawn when game was started from level editor
4419   // - drawing area (playfield) was changed and has to be removed completely
4420   redraw_mask = REDRAW_ALL;
4421   BackToFront();
4422 #endif
4423
4424   if (!game.restart_level)
4425   {
4426     // copy default game door content to main double buffer
4427
4428     // !!! CHECK AGAIN !!!
4429     SetPanelBackground();
4430     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4431     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4432   }
4433
4434   SetPanelBackground();
4435   SetDrawBackgroundMask(REDRAW_DOOR_1);
4436
4437   UpdateAndDisplayGameControlValues();
4438
4439   if (!game.restart_level)
4440   {
4441     UnmapGameButtons();
4442     UnmapTapeButtons();
4443
4444     FreeGameButtons();
4445     CreateGameButtons();
4446
4447     MapGameButtons();
4448     MapTapeButtons();
4449
4450     // copy actual game door content to door double buffer for OpenDoor()
4451     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4452
4453     OpenDoor(DOOR_OPEN_ALL);
4454
4455     KeyboardAutoRepeatOffUnlessAutoplay();
4456
4457 #if DEBUG_INIT_PLAYER
4458     DebugPrintPlayerStatus("Player status (final)");
4459 #endif
4460   }
4461
4462   UnmapAllGadgets();
4463
4464   MapGameButtons();
4465   MapTapeButtons();
4466
4467   if (!game.restart_level && !tape.playing)
4468   {
4469     LevelStats_incPlayed(level_nr);
4470
4471     SaveLevelSetup_SeriesInfo();
4472   }
4473
4474   game.restart_level = FALSE;
4475   game.restart_game_message = NULL;
4476
4477   game.request_active = FALSE;
4478   game.request_active_or_moving = FALSE;
4479
4480   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4481     InitGameActions_MM();
4482
4483   SaveEngineSnapshotToListInitial();
4484
4485   if (!game.restart_level)
4486   {
4487     PlaySound(SND_GAME_STARTING);
4488
4489     if (setup.sound_music)
4490       PlayLevelMusic();
4491   }
4492
4493   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4494 }
4495
4496 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4497                         int actual_player_x, int actual_player_y)
4498 {
4499   // this is used for non-R'n'D game engines to update certain engine values
4500
4501   // needed to determine if sounds are played within the visible screen area
4502   scroll_x = actual_scroll_x;
4503   scroll_y = actual_scroll_y;
4504
4505   // needed to get player position for "follow finger" playing input method
4506   local_player->jx = actual_player_x;
4507   local_player->jy = actual_player_y;
4508 }
4509
4510 void InitMovDir(int x, int y)
4511 {
4512   int i, element = Tile[x][y];
4513   static int xy[4][2] =
4514   {
4515     {  0, +1 },
4516     { +1,  0 },
4517     {  0, -1 },
4518     { -1,  0 }
4519   };
4520   static int direction[3][4] =
4521   {
4522     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4523     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4524     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4525   };
4526
4527   switch (element)
4528   {
4529     case EL_BUG_RIGHT:
4530     case EL_BUG_UP:
4531     case EL_BUG_LEFT:
4532     case EL_BUG_DOWN:
4533       Tile[x][y] = EL_BUG;
4534       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4535       break;
4536
4537     case EL_SPACESHIP_RIGHT:
4538     case EL_SPACESHIP_UP:
4539     case EL_SPACESHIP_LEFT:
4540     case EL_SPACESHIP_DOWN:
4541       Tile[x][y] = EL_SPACESHIP;
4542       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4543       break;
4544
4545     case EL_BD_BUTTERFLY_RIGHT:
4546     case EL_BD_BUTTERFLY_UP:
4547     case EL_BD_BUTTERFLY_LEFT:
4548     case EL_BD_BUTTERFLY_DOWN:
4549       Tile[x][y] = EL_BD_BUTTERFLY;
4550       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4551       break;
4552
4553     case EL_BD_FIREFLY_RIGHT:
4554     case EL_BD_FIREFLY_UP:
4555     case EL_BD_FIREFLY_LEFT:
4556     case EL_BD_FIREFLY_DOWN:
4557       Tile[x][y] = EL_BD_FIREFLY;
4558       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4559       break;
4560
4561     case EL_PACMAN_RIGHT:
4562     case EL_PACMAN_UP:
4563     case EL_PACMAN_LEFT:
4564     case EL_PACMAN_DOWN:
4565       Tile[x][y] = EL_PACMAN;
4566       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4567       break;
4568
4569     case EL_YAMYAM_LEFT:
4570     case EL_YAMYAM_RIGHT:
4571     case EL_YAMYAM_UP:
4572     case EL_YAMYAM_DOWN:
4573       Tile[x][y] = EL_YAMYAM;
4574       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4575       break;
4576
4577     case EL_SP_SNIKSNAK:
4578       MovDir[x][y] = MV_UP;
4579       break;
4580
4581     case EL_SP_ELECTRON:
4582       MovDir[x][y] = MV_LEFT;
4583       break;
4584
4585     case EL_MOLE_LEFT:
4586     case EL_MOLE_RIGHT:
4587     case EL_MOLE_UP:
4588     case EL_MOLE_DOWN:
4589       Tile[x][y] = EL_MOLE;
4590       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4591       break;
4592
4593     case EL_SPRING_LEFT:
4594     case EL_SPRING_RIGHT:
4595       Tile[x][y] = EL_SPRING;
4596       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4597       break;
4598
4599     default:
4600       if (IS_CUSTOM_ELEMENT(element))
4601       {
4602         struct ElementInfo *ei = &element_info[element];
4603         int move_direction_initial = ei->move_direction_initial;
4604         int move_pattern = ei->move_pattern;
4605
4606         if (move_direction_initial == MV_START_PREVIOUS)
4607         {
4608           if (MovDir[x][y] != MV_NONE)
4609             return;
4610
4611           move_direction_initial = MV_START_AUTOMATIC;
4612         }
4613
4614         if (move_direction_initial == MV_START_RANDOM)
4615           MovDir[x][y] = 1 << RND(4);
4616         else if (move_direction_initial & MV_ANY_DIRECTION)
4617           MovDir[x][y] = move_direction_initial;
4618         else if (move_pattern == MV_ALL_DIRECTIONS ||
4619                  move_pattern == MV_TURNING_LEFT ||
4620                  move_pattern == MV_TURNING_RIGHT ||
4621                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4622                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4623                  move_pattern == MV_TURNING_RANDOM)
4624           MovDir[x][y] = 1 << RND(4);
4625         else if (move_pattern == MV_HORIZONTAL)
4626           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4627         else if (move_pattern == MV_VERTICAL)
4628           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4629         else if (move_pattern & MV_ANY_DIRECTION)
4630           MovDir[x][y] = element_info[element].move_pattern;
4631         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4632                  move_pattern == MV_ALONG_RIGHT_SIDE)
4633         {
4634           // use random direction as default start direction
4635           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4636             MovDir[x][y] = 1 << RND(4);
4637
4638           for (i = 0; i < NUM_DIRECTIONS; i++)
4639           {
4640             int x1 = x + xy[i][0];
4641             int y1 = y + xy[i][1];
4642
4643             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4644             {
4645               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4646                 MovDir[x][y] = direction[0][i];
4647               else
4648                 MovDir[x][y] = direction[1][i];
4649
4650               break;
4651             }
4652           }
4653         }                
4654       }
4655       else
4656       {
4657         MovDir[x][y] = 1 << RND(4);
4658
4659         if (element != EL_BUG &&
4660             element != EL_SPACESHIP &&
4661             element != EL_BD_BUTTERFLY &&
4662             element != EL_BD_FIREFLY)
4663           break;
4664
4665         for (i = 0; i < NUM_DIRECTIONS; i++)
4666         {
4667           int x1 = x + xy[i][0];
4668           int y1 = y + xy[i][1];
4669
4670           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4671           {
4672             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4673             {
4674               MovDir[x][y] = direction[0][i];
4675               break;
4676             }
4677             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4678                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4679             {
4680               MovDir[x][y] = direction[1][i];
4681               break;
4682             }
4683           }
4684         }
4685       }
4686       break;
4687   }
4688
4689   GfxDir[x][y] = MovDir[x][y];
4690 }
4691
4692 void InitAmoebaNr(int x, int y)
4693 {
4694   int i;
4695   int group_nr = AmoebaNeighbourNr(x, y);
4696
4697   if (group_nr == 0)
4698   {
4699     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4700     {
4701       if (AmoebaCnt[i] == 0)
4702       {
4703         group_nr = i;
4704         break;
4705       }
4706     }
4707   }
4708
4709   AmoebaNr[x][y] = group_nr;
4710   AmoebaCnt[group_nr]++;
4711   AmoebaCnt2[group_nr]++;
4712 }
4713
4714 static void LevelSolved_SetFinalGameValues(void)
4715 {
4716   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4717   game.score_time_final = (level.use_step_counter ? TimePlayed :
4718                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4719
4720   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4721                       game_em.lev->score :
4722                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4723                       game_mm.score :
4724                       game.score);
4725
4726   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4727                        MM_HEALTH(game_mm.laser_overload_value) :
4728                        game.health);
4729
4730   game.LevelSolved_CountingTime = game.time_final;
4731   game.LevelSolved_CountingScore = game.score_final;
4732   game.LevelSolved_CountingHealth = game.health_final;
4733 }
4734
4735 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4736 {
4737   game.LevelSolved_CountingTime = time;
4738   game.LevelSolved_CountingScore = score;
4739   game.LevelSolved_CountingHealth = health;
4740
4741   game_panel_controls[GAME_PANEL_TIME].value = time;
4742   game_panel_controls[GAME_PANEL_SCORE].value = score;
4743   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4744
4745   DisplayGameControlValues();
4746 }
4747
4748 static void LevelSolved(void)
4749 {
4750   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4751       game.players_still_needed > 0)
4752     return;
4753
4754   game.LevelSolved = TRUE;
4755   game.GameOver = TRUE;
4756
4757   // needed here to display correct panel values while player walks into exit
4758   LevelSolved_SetFinalGameValues();
4759 }
4760
4761 void GameWon(void)
4762 {
4763   static int time_count_steps;
4764   static int time, time_final;
4765   static float score, score_final; // needed for time score < 10 for 10 seconds
4766   static int health, health_final;
4767   static int game_over_delay_1 = 0;
4768   static int game_over_delay_2 = 0;
4769   static int game_over_delay_3 = 0;
4770   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4771   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4772
4773   if (!game.LevelSolved_GameWon)
4774   {
4775     int i;
4776
4777     // do not start end game actions before the player stops moving (to exit)
4778     if (local_player->active && local_player->MovPos)
4779       return;
4780
4781     // calculate final game values after player finished walking into exit
4782     LevelSolved_SetFinalGameValues();
4783
4784     game.LevelSolved_GameWon = TRUE;
4785     game.LevelSolved_SaveTape = tape.recording;
4786     game.LevelSolved_SaveScore = !tape.playing;
4787
4788     if (!tape.playing)
4789     {
4790       LevelStats_incSolved(level_nr);
4791
4792       SaveLevelSetup_SeriesInfo();
4793     }
4794
4795     if (tape.auto_play)         // tape might already be stopped here
4796       tape.auto_play_level_solved = TRUE;
4797
4798     TapeStop();
4799
4800     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4801     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4802     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4803
4804     time = time_final = game.time_final;
4805     score = score_final = game.score_final;
4806     health = health_final = game.health_final;
4807
4808     // update game panel values before (delayed) counting of score (if any)
4809     LevelSolved_DisplayFinalGameValues(time, score, health);
4810
4811     // if level has time score defined, calculate new final game values
4812     if (time_score > 0)
4813     {
4814       int time_final_max = 999;
4815       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4816       int time_frames = 0;
4817       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4818       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4819
4820       if (TimeLeft > 0)
4821       {
4822         time_final = 0;
4823         time_frames = time_frames_left;
4824       }
4825       else if (game.no_time_limit && TimePlayed < time_final_max)
4826       {
4827         time_final = time_final_max;
4828         time_frames = time_frames_final_max - time_frames_played;
4829       }
4830
4831       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4832
4833       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4834
4835       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4836       {
4837         health_final = 0;
4838         score_final += health * time_score;
4839       }
4840
4841       game.score_final = score_final;
4842       game.health_final = health_final;
4843     }
4844
4845     // if not counting score after game, immediately update game panel values
4846     if (level_editor_test_game || !setup.count_score_after_game)
4847     {
4848       time = time_final;
4849       score = score_final;
4850
4851       LevelSolved_DisplayFinalGameValues(time, score, health);
4852     }
4853
4854     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4855     {
4856       // check if last player has left the level
4857       if (game.exit_x >= 0 &&
4858           game.exit_y >= 0)
4859       {
4860         int x = game.exit_x;
4861         int y = game.exit_y;
4862         int element = Tile[x][y];
4863
4864         // close exit door after last player
4865         if ((game.all_players_gone &&
4866              (element == EL_EXIT_OPEN ||
4867               element == EL_SP_EXIT_OPEN ||
4868               element == EL_STEEL_EXIT_OPEN)) ||
4869             element == EL_EM_EXIT_OPEN ||
4870             element == EL_EM_STEEL_EXIT_OPEN)
4871         {
4872
4873           Tile[x][y] =
4874             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4875              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4876              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4877              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4878              EL_EM_STEEL_EXIT_CLOSING);
4879
4880           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4881         }
4882
4883         // player disappears
4884         DrawLevelField(x, y);
4885       }
4886
4887       for (i = 0; i < MAX_PLAYERS; i++)
4888       {
4889         struct PlayerInfo *player = &stored_player[i];
4890
4891         if (player->present)
4892         {
4893           RemovePlayer(player);
4894
4895           // player disappears
4896           DrawLevelField(player->jx, player->jy);
4897         }
4898       }
4899     }
4900
4901     PlaySound(SND_GAME_WINNING);
4902   }
4903
4904   if (setup.count_score_after_game)
4905   {
4906     if (time != time_final)
4907     {
4908       if (game_over_delay_1 > 0)
4909       {
4910         game_over_delay_1--;
4911
4912         return;
4913       }
4914
4915       int time_to_go = ABS(time_final - time);
4916       int time_count_dir = (time < time_final ? +1 : -1);
4917
4918       if (time_to_go < time_count_steps)
4919         time_count_steps = 1;
4920
4921       time  += time_count_steps * time_count_dir;
4922       score += time_count_steps * time_score;
4923
4924       // set final score to correct rounding differences after counting score
4925       if (time == time_final)
4926         score = score_final;
4927
4928       LevelSolved_DisplayFinalGameValues(time, score, health);
4929
4930       if (time == time_final)
4931         StopSound(SND_GAME_LEVELTIME_BONUS);
4932       else if (setup.sound_loops)
4933         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4934       else
4935         PlaySound(SND_GAME_LEVELTIME_BONUS);
4936
4937       return;
4938     }
4939
4940     if (health != health_final)
4941     {
4942       if (game_over_delay_2 > 0)
4943       {
4944         game_over_delay_2--;
4945
4946         return;
4947       }
4948
4949       int health_count_dir = (health < health_final ? +1 : -1);
4950
4951       health += health_count_dir;
4952       score  += time_score;
4953
4954       LevelSolved_DisplayFinalGameValues(time, score, health);
4955
4956       if (health == health_final)
4957         StopSound(SND_GAME_LEVELTIME_BONUS);
4958       else if (setup.sound_loops)
4959         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4960       else
4961         PlaySound(SND_GAME_LEVELTIME_BONUS);
4962
4963       return;
4964     }
4965   }
4966
4967   game.panel.active = FALSE;
4968
4969   if (game_over_delay_3 > 0)
4970   {
4971     game_over_delay_3--;
4972
4973     return;
4974   }
4975
4976   GameEnd();
4977 }
4978
4979 void GameEnd(void)
4980 {
4981   // used instead of "level_nr" (needed for network games)
4982   int last_level_nr = levelset.level_nr;
4983   boolean tape_saved = FALSE;
4984
4985   game.LevelSolved_GameEnd = TRUE;
4986
4987   if (game.LevelSolved_SaveTape)
4988   {
4989     // make sure that request dialog to save tape does not open door again
4990     if (!global.use_envelope_request)
4991       CloseDoor(DOOR_CLOSE_1);
4992
4993     // ask to save tape
4994     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
4995
4996     // set unique basename for score tape (also saved in high score table)
4997     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
4998   }
4999
5000   // if no tape is to be saved, close both doors simultaneously
5001   CloseDoor(DOOR_CLOSE_ALL);
5002
5003   if (level_editor_test_game)
5004   {
5005     SetGameStatus(GAME_MODE_MAIN);
5006
5007     DrawMainMenu();
5008
5009     return;
5010   }
5011
5012   if (!game.LevelSolved_SaveScore)
5013   {
5014     SetGameStatus(GAME_MODE_MAIN);
5015
5016     DrawMainMenu();
5017
5018     return;
5019   }
5020
5021   if (level_nr == leveldir_current->handicap_level)
5022   {
5023     leveldir_current->handicap_level++;
5024
5025     SaveLevelSetup_SeriesInfo();
5026   }
5027
5028   // save score and score tape before potentially erasing tape below
5029   NewHighScore(last_level_nr, tape_saved);
5030
5031   if (setup.increment_levels &&
5032       level_nr < leveldir_current->last_level &&
5033       !network_playing)
5034   {
5035     level_nr++;         // advance to next level
5036     TapeErase();        // start with empty tape
5037
5038     if (setup.auto_play_next_level)
5039     {
5040       LoadLevel(level_nr);
5041
5042       SaveLevelSetup_SeriesInfo();
5043     }
5044   }
5045
5046   if (scores.last_added >= 0 && setup.show_scores_after_game)
5047   {
5048     SetGameStatus(GAME_MODE_SCORES);
5049
5050     DrawHallOfFame(last_level_nr);
5051   }
5052   else if (setup.auto_play_next_level && setup.increment_levels &&
5053            last_level_nr < leveldir_current->last_level &&
5054            !network_playing)
5055   {
5056     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5057   }
5058   else
5059   {
5060     SetGameStatus(GAME_MODE_MAIN);
5061
5062     DrawMainMenu();
5063   }
5064 }
5065
5066 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5067                          boolean one_score_entry_per_name)
5068 {
5069   int i;
5070
5071   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5072     return -1;
5073
5074   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5075   {
5076     struct ScoreEntry *entry = &list->entry[i];
5077     boolean score_is_better = (new_entry->score >  entry->score);
5078     boolean score_is_equal  = (new_entry->score == entry->score);
5079     boolean time_is_better  = (new_entry->time  <  entry->time);
5080     boolean time_is_equal   = (new_entry->time  == entry->time);
5081     boolean better_by_score = (score_is_better ||
5082                                (score_is_equal && time_is_better));
5083     boolean better_by_time  = (time_is_better ||
5084                                (time_is_equal && score_is_better));
5085     boolean is_better = (level.rate_time_over_score ? better_by_time :
5086                          better_by_score);
5087     boolean entry_is_empty = (entry->score == 0 &&
5088                               entry->time == 0);
5089
5090     // prevent adding server score entries if also existing in local score file
5091     // (special case: historic score entries have an empty tape basename entry)
5092     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5093         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5094       return -1;
5095
5096     if (is_better || entry_is_empty)
5097     {
5098       // player has made it to the hall of fame
5099
5100       if (i < MAX_SCORE_ENTRIES - 1)
5101       {
5102         int m = MAX_SCORE_ENTRIES - 1;
5103         int l;
5104
5105         if (one_score_entry_per_name)
5106         {
5107           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5108             if (strEqual(list->entry[l].name, new_entry->name))
5109               m = l;
5110
5111           if (m == i)   // player's new highscore overwrites his old one
5112             goto put_into_list;
5113         }
5114
5115         for (l = m; l > i; l--)
5116           list->entry[l] = list->entry[l - 1];
5117       }
5118
5119       put_into_list:
5120
5121       *entry = *new_entry;
5122
5123       return i;
5124     }
5125     else if (one_score_entry_per_name &&
5126              strEqual(entry->name, new_entry->name))
5127     {
5128       // player already in high score list with better score or time
5129
5130       return -1;
5131     }
5132   }
5133
5134   return -1;
5135 }
5136
5137 void NewHighScore(int level_nr, boolean tape_saved)
5138 {
5139   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5140   boolean one_per_name = FALSE;
5141
5142   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5143   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5144
5145   new_entry.score = game.score_final;
5146   new_entry.time = game.score_time_final;
5147
5148   LoadScore(level_nr);
5149
5150   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5151
5152   if (scores.last_added < 0)
5153     return;
5154
5155   SaveScore(level_nr);
5156
5157   // store last added local score entry (before merging server scores)
5158   scores.last_added_local = scores.last_added;
5159
5160   if (!game.LevelSolved_SaveTape)
5161     return;
5162
5163   SaveScoreTape(level_nr);
5164
5165   if (setup.ask_for_using_api_server)
5166   {
5167     setup.use_api_server =
5168       Request("Upload your score and tape to the high score server?", REQ_ASK);
5169
5170     if (!setup.use_api_server)
5171       Request("Not using high score server! Use setup menu to enable again!",
5172               REQ_CONFIRM);
5173
5174     runtime.use_api_server = setup.use_api_server;
5175
5176     // after asking for using API server once, do not ask again
5177     setup.ask_for_using_api_server = FALSE;
5178
5179     SaveSetup_ServerSetup();
5180   }
5181
5182   SaveServerScore(level_nr, tape_saved);
5183 }
5184
5185 void MergeServerScore(void)
5186 {
5187   struct ScoreEntry last_added_entry;
5188   boolean one_per_name = FALSE;
5189   int i;
5190
5191   if (scores.last_added >= 0)
5192     last_added_entry = scores.entry[scores.last_added];
5193
5194   for (i = 0; i < server_scores.num_entries; i++)
5195   {
5196     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5197
5198     if (pos >= 0 && pos <= scores.last_added)
5199       scores.last_added++;
5200   }
5201
5202   if (scores.last_added >= MAX_SCORE_ENTRIES)
5203   {
5204     scores.last_added = MAX_SCORE_ENTRIES - 1;
5205     scores.force_last_added = TRUE;
5206
5207     scores.entry[scores.last_added] = last_added_entry;
5208   }
5209 }
5210
5211 static int getElementMoveStepsizeExt(int x, int y, int direction)
5212 {
5213   int element = Tile[x][y];
5214   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5215   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5216   int horiz_move = (dx != 0);
5217   int sign = (horiz_move ? dx : dy);
5218   int step = sign * element_info[element].move_stepsize;
5219
5220   // special values for move stepsize for spring and things on conveyor belt
5221   if (horiz_move)
5222   {
5223     if (CAN_FALL(element) &&
5224         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5225       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5226     else if (element == EL_SPRING)
5227       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5228   }
5229
5230   return step;
5231 }
5232
5233 static int getElementMoveStepsize(int x, int y)
5234 {
5235   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5236 }
5237
5238 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5239 {
5240   if (player->GfxAction != action || player->GfxDir != dir)
5241   {
5242     player->GfxAction = action;
5243     player->GfxDir = dir;
5244     player->Frame = 0;
5245     player->StepFrame = 0;
5246   }
5247 }
5248
5249 static void ResetGfxFrame(int x, int y)
5250 {
5251   // profiling showed that "autotest" spends 10~20% of its time in this function
5252   if (DrawingDeactivatedField())
5253     return;
5254
5255   int element = Tile[x][y];
5256   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5257
5258   if (graphic_info[graphic].anim_global_sync)
5259     GfxFrame[x][y] = FrameCounter;
5260   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5261     GfxFrame[x][y] = CustomValue[x][y];
5262   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5263     GfxFrame[x][y] = element_info[element].collect_score;
5264   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5265     GfxFrame[x][y] = ChangeDelay[x][y];
5266 }
5267
5268 static void ResetGfxAnimation(int x, int y)
5269 {
5270   GfxAction[x][y] = ACTION_DEFAULT;
5271   GfxDir[x][y] = MovDir[x][y];
5272   GfxFrame[x][y] = 0;
5273
5274   ResetGfxFrame(x, y);
5275 }
5276
5277 static void ResetRandomAnimationValue(int x, int y)
5278 {
5279   GfxRandom[x][y] = INIT_GFX_RANDOM();
5280 }
5281
5282 static void InitMovingField(int x, int y, int direction)
5283 {
5284   int element = Tile[x][y];
5285   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5286   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5287   int newx = x + dx;
5288   int newy = y + dy;
5289   boolean is_moving_before, is_moving_after;
5290
5291   // check if element was/is moving or being moved before/after mode change
5292   is_moving_before = (WasJustMoving[x][y] != 0);
5293   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5294
5295   // reset animation only for moving elements which change direction of moving
5296   // or which just started or stopped moving
5297   // (else CEs with property "can move" / "not moving" are reset each frame)
5298   if (is_moving_before != is_moving_after ||
5299       direction != MovDir[x][y])
5300     ResetGfxAnimation(x, y);
5301
5302   MovDir[x][y] = direction;
5303   GfxDir[x][y] = direction;
5304
5305   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5306                      direction == MV_DOWN && CAN_FALL(element) ?
5307                      ACTION_FALLING : ACTION_MOVING);
5308
5309   // this is needed for CEs with property "can move" / "not moving"
5310
5311   if (is_moving_after)
5312   {
5313     if (Tile[newx][newy] == EL_EMPTY)
5314       Tile[newx][newy] = EL_BLOCKED;
5315
5316     MovDir[newx][newy] = MovDir[x][y];
5317
5318     CustomValue[newx][newy] = CustomValue[x][y];
5319
5320     GfxFrame[newx][newy] = GfxFrame[x][y];
5321     GfxRandom[newx][newy] = GfxRandom[x][y];
5322     GfxAction[newx][newy] = GfxAction[x][y];
5323     GfxDir[newx][newy] = GfxDir[x][y];
5324   }
5325 }
5326
5327 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5328 {
5329   int direction = MovDir[x][y];
5330   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5331   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5332
5333   *goes_to_x = newx;
5334   *goes_to_y = newy;
5335 }
5336
5337 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5338 {
5339   int oldx = x, oldy = y;
5340   int direction = MovDir[x][y];
5341
5342   if (direction == MV_LEFT)
5343     oldx++;
5344   else if (direction == MV_RIGHT)
5345     oldx--;
5346   else if (direction == MV_UP)
5347     oldy++;
5348   else if (direction == MV_DOWN)
5349     oldy--;
5350
5351   *comes_from_x = oldx;
5352   *comes_from_y = oldy;
5353 }
5354
5355 static int MovingOrBlocked2Element(int x, int y)
5356 {
5357   int element = Tile[x][y];
5358
5359   if (element == EL_BLOCKED)
5360   {
5361     int oldx, oldy;
5362
5363     Blocked2Moving(x, y, &oldx, &oldy);
5364     return Tile[oldx][oldy];
5365   }
5366   else
5367     return element;
5368 }
5369
5370 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5371 {
5372   // like MovingOrBlocked2Element(), but if element is moving
5373   // and (x,y) is the field the moving element is just leaving,
5374   // return EL_BLOCKED instead of the element value
5375   int element = Tile[x][y];
5376
5377   if (IS_MOVING(x, y))
5378   {
5379     if (element == EL_BLOCKED)
5380     {
5381       int oldx, oldy;
5382
5383       Blocked2Moving(x, y, &oldx, &oldy);
5384       return Tile[oldx][oldy];
5385     }
5386     else
5387       return EL_BLOCKED;
5388   }
5389   else
5390     return element;
5391 }
5392
5393 static void RemoveField(int x, int y)
5394 {
5395   Tile[x][y] = EL_EMPTY;
5396
5397   MovPos[x][y] = 0;
5398   MovDir[x][y] = 0;
5399   MovDelay[x][y] = 0;
5400
5401   CustomValue[x][y] = 0;
5402
5403   AmoebaNr[x][y] = 0;
5404   ChangeDelay[x][y] = 0;
5405   ChangePage[x][y] = -1;
5406   Pushed[x][y] = FALSE;
5407
5408   GfxElement[x][y] = EL_UNDEFINED;
5409   GfxAction[x][y] = ACTION_DEFAULT;
5410   GfxDir[x][y] = MV_NONE;
5411 }
5412
5413 static void RemoveMovingField(int x, int y)
5414 {
5415   int oldx = x, oldy = y, newx = x, newy = y;
5416   int element = Tile[x][y];
5417   int next_element = EL_UNDEFINED;
5418
5419   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5420     return;
5421
5422   if (IS_MOVING(x, y))
5423   {
5424     Moving2Blocked(x, y, &newx, &newy);
5425
5426     if (Tile[newx][newy] != EL_BLOCKED)
5427     {
5428       // element is moving, but target field is not free (blocked), but
5429       // already occupied by something different (example: acid pool);
5430       // in this case, only remove the moving field, but not the target
5431
5432       RemoveField(oldx, oldy);
5433
5434       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5435
5436       TEST_DrawLevelField(oldx, oldy);
5437
5438       return;
5439     }
5440   }
5441   else if (element == EL_BLOCKED)
5442   {
5443     Blocked2Moving(x, y, &oldx, &oldy);
5444     if (!IS_MOVING(oldx, oldy))
5445       return;
5446   }
5447
5448   if (element == EL_BLOCKED &&
5449       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5450        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5451        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5452        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5453        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5454        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5455     next_element = get_next_element(Tile[oldx][oldy]);
5456
5457   RemoveField(oldx, oldy);
5458   RemoveField(newx, newy);
5459
5460   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5461
5462   if (next_element != EL_UNDEFINED)
5463     Tile[oldx][oldy] = next_element;
5464
5465   TEST_DrawLevelField(oldx, oldy);
5466   TEST_DrawLevelField(newx, newy);
5467 }
5468
5469 void DrawDynamite(int x, int y)
5470 {
5471   int sx = SCREENX(x), sy = SCREENY(y);
5472   int graphic = el2img(Tile[x][y]);
5473   int frame;
5474
5475   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5476     return;
5477
5478   if (IS_WALKABLE_INSIDE(Back[x][y]))
5479     return;
5480
5481   if (Back[x][y])
5482     DrawLevelElement(x, y, Back[x][y]);
5483   else if (Store[x][y])
5484     DrawLevelElement(x, y, Store[x][y]);
5485   else if (game.use_masked_elements)
5486     DrawLevelElement(x, y, EL_EMPTY);
5487
5488   frame = getGraphicAnimationFrameXY(graphic, x, y);
5489
5490   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5491     DrawGraphicThruMask(sx, sy, graphic, frame);
5492   else
5493     DrawGraphic(sx, sy, graphic, frame);
5494 }
5495
5496 static void CheckDynamite(int x, int y)
5497 {
5498   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5499   {
5500     MovDelay[x][y]--;
5501
5502     if (MovDelay[x][y] != 0)
5503     {
5504       DrawDynamite(x, y);
5505       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5506
5507       return;
5508     }
5509   }
5510
5511   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5512
5513   Bang(x, y);
5514 }
5515
5516 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5517 {
5518   boolean num_checked_players = 0;
5519   int i;
5520
5521   for (i = 0; i < MAX_PLAYERS; i++)
5522   {
5523     if (stored_player[i].active)
5524     {
5525       int sx = stored_player[i].jx;
5526       int sy = stored_player[i].jy;
5527
5528       if (num_checked_players == 0)
5529       {
5530         *sx1 = *sx2 = sx;
5531         *sy1 = *sy2 = sy;
5532       }
5533       else
5534       {
5535         *sx1 = MIN(*sx1, sx);
5536         *sy1 = MIN(*sy1, sy);
5537         *sx2 = MAX(*sx2, sx);
5538         *sy2 = MAX(*sy2, sy);
5539       }
5540
5541       num_checked_players++;
5542     }
5543   }
5544 }
5545
5546 static boolean checkIfAllPlayersFitToScreen_RND(void)
5547 {
5548   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5549
5550   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5551
5552   return (sx2 - sx1 < SCR_FIELDX &&
5553           sy2 - sy1 < SCR_FIELDY);
5554 }
5555
5556 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5557 {
5558   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5559
5560   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5561
5562   *sx = (sx1 + sx2) / 2;
5563   *sy = (sy1 + sy2) / 2;
5564 }
5565
5566 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5567                                boolean center_screen, boolean quick_relocation)
5568 {
5569   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5570   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5571   boolean no_delay = (tape.warp_forward);
5572   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5573   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5574   int new_scroll_x, new_scroll_y;
5575
5576   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5577   {
5578     // case 1: quick relocation inside visible screen (without scrolling)
5579
5580     RedrawPlayfield();
5581
5582     return;
5583   }
5584
5585   if (!level.shifted_relocation || center_screen)
5586   {
5587     // relocation _with_ centering of screen
5588
5589     new_scroll_x = SCROLL_POSITION_X(x);
5590     new_scroll_y = SCROLL_POSITION_Y(y);
5591   }
5592   else
5593   {
5594     // relocation _without_ centering of screen
5595
5596     int center_scroll_x = SCROLL_POSITION_X(old_x);
5597     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5598     int offset_x = x + (scroll_x - center_scroll_x);
5599     int offset_y = y + (scroll_y - center_scroll_y);
5600
5601     // for new screen position, apply previous offset to center position
5602     new_scroll_x = SCROLL_POSITION_X(offset_x);
5603     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5604   }
5605
5606   if (quick_relocation)
5607   {
5608     // case 2: quick relocation (redraw without visible scrolling)
5609
5610     scroll_x = new_scroll_x;
5611     scroll_y = new_scroll_y;
5612
5613     RedrawPlayfield();
5614
5615     return;
5616   }
5617
5618   // case 3: visible relocation (with scrolling to new position)
5619
5620   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5621
5622   SetVideoFrameDelay(wait_delay_value);
5623
5624   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5625   {
5626     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5627     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5628
5629     if (dx == 0 && dy == 0)             // no scrolling needed at all
5630       break;
5631
5632     scroll_x -= dx;
5633     scroll_y -= dy;
5634
5635     // set values for horizontal/vertical screen scrolling (half tile size)
5636     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5637     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5638     int pos_x = dx * TILEX / 2;
5639     int pos_y = dy * TILEY / 2;
5640     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5641     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5642
5643     ScrollLevel(dx, dy);
5644     DrawAllPlayers();
5645
5646     // scroll in two steps of half tile size to make things smoother
5647     BlitScreenToBitmapExt_RND(window, fx, fy);
5648
5649     // scroll second step to align at full tile size
5650     BlitScreenToBitmap(window);
5651   }
5652
5653   DrawAllPlayers();
5654   BackToFront();
5655
5656   SetVideoFrameDelay(frame_delay_value_old);
5657 }
5658
5659 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5660 {
5661   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5662   int player_nr = GET_PLAYER_NR(el_player);
5663   struct PlayerInfo *player = &stored_player[player_nr];
5664   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5665   boolean no_delay = (tape.warp_forward);
5666   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5667   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5668   int old_jx = player->jx;
5669   int old_jy = player->jy;
5670   int old_element = Tile[old_jx][old_jy];
5671   int element = Tile[jx][jy];
5672   boolean player_relocated = (old_jx != jx || old_jy != jy);
5673
5674   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5675   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5676   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5677   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5678   int leave_side_horiz = move_dir_horiz;
5679   int leave_side_vert  = move_dir_vert;
5680   int enter_side = enter_side_horiz | enter_side_vert;
5681   int leave_side = leave_side_horiz | leave_side_vert;
5682
5683   if (player->buried)           // do not reanimate dead player
5684     return;
5685
5686   if (!player_relocated)        // no need to relocate the player
5687     return;
5688
5689   if (IS_PLAYER(jx, jy))        // player already placed at new position
5690   {
5691     RemoveField(jx, jy);        // temporarily remove newly placed player
5692     DrawLevelField(jx, jy);
5693   }
5694
5695   if (player->present)
5696   {
5697     while (player->MovPos)
5698     {
5699       ScrollPlayer(player, SCROLL_GO_ON);
5700       ScrollScreen(NULL, SCROLL_GO_ON);
5701
5702       AdvanceFrameAndPlayerCounters(player->index_nr);
5703
5704       DrawPlayer(player);
5705
5706       BackToFront_WithFrameDelay(wait_delay_value);
5707     }
5708
5709     DrawPlayer(player);         // needed here only to cleanup last field
5710     DrawLevelField(player->jx, player->jy);     // remove player graphic
5711
5712     player->is_moving = FALSE;
5713   }
5714
5715   if (IS_CUSTOM_ELEMENT(old_element))
5716     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5717                                CE_LEFT_BY_PLAYER,
5718                                player->index_bit, leave_side);
5719
5720   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5721                                       CE_PLAYER_LEAVES_X,
5722                                       player->index_bit, leave_side);
5723
5724   Tile[jx][jy] = el_player;
5725   InitPlayerField(jx, jy, el_player, TRUE);
5726
5727   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5728      possible that the relocation target field did not contain a player element,
5729      but a walkable element, to which the new player was relocated -- in this
5730      case, restore that (already initialized!) element on the player field */
5731   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5732   {
5733     Tile[jx][jy] = element;     // restore previously existing element
5734   }
5735
5736   // only visually relocate centered player
5737   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5738                      FALSE, level.instant_relocation);
5739
5740   TestIfPlayerTouchesBadThing(jx, jy);
5741   TestIfPlayerTouchesCustomElement(jx, jy);
5742
5743   if (IS_CUSTOM_ELEMENT(element))
5744     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5745                                player->index_bit, enter_side);
5746
5747   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5748                                       player->index_bit, enter_side);
5749
5750   if (player->is_switching)
5751   {
5752     /* ensure that relocation while still switching an element does not cause
5753        a new element to be treated as also switched directly after relocation
5754        (this is important for teleporter switches that teleport the player to
5755        a place where another teleporter switch is in the same direction, which
5756        would then incorrectly be treated as immediately switched before the
5757        direction key that caused the switch was released) */
5758
5759     player->switch_x += jx - old_jx;
5760     player->switch_y += jy - old_jy;
5761   }
5762 }
5763
5764 static void Explode(int ex, int ey, int phase, int mode)
5765 {
5766   int x, y;
5767   int last_phase;
5768   int border_element;
5769
5770   // !!! eliminate this variable !!!
5771   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5772
5773   if (game.explosions_delayed)
5774   {
5775     ExplodeField[ex][ey] = mode;
5776     return;
5777   }
5778
5779   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5780   {
5781     int center_element = Tile[ex][ey];
5782     int artwork_element, explosion_element;     // set these values later
5783
5784     // remove things displayed in background while burning dynamite
5785     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5786       Back[ex][ey] = 0;
5787
5788     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5789     {
5790       // put moving element to center field (and let it explode there)
5791       center_element = MovingOrBlocked2Element(ex, ey);
5792       RemoveMovingField(ex, ey);
5793       Tile[ex][ey] = center_element;
5794     }
5795
5796     // now "center_element" is finally determined -- set related values now
5797     artwork_element = center_element;           // for custom player artwork
5798     explosion_element = center_element;         // for custom player artwork
5799
5800     if (IS_PLAYER(ex, ey))
5801     {
5802       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5803
5804       artwork_element = stored_player[player_nr].artwork_element;
5805
5806       if (level.use_explosion_element[player_nr])
5807       {
5808         explosion_element = level.explosion_element[player_nr];
5809         artwork_element = explosion_element;
5810       }
5811     }
5812
5813     if (mode == EX_TYPE_NORMAL ||
5814         mode == EX_TYPE_CENTER ||
5815         mode == EX_TYPE_CROSS)
5816       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5817
5818     last_phase = element_info[explosion_element].explosion_delay + 1;
5819
5820     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5821     {
5822       int xx = x - ex + 1;
5823       int yy = y - ey + 1;
5824       int element;
5825
5826       if (!IN_LEV_FIELD(x, y) ||
5827           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5828           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5829         continue;
5830
5831       element = Tile[x][y];
5832
5833       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5834       {
5835         element = MovingOrBlocked2Element(x, y);
5836
5837         if (!IS_EXPLOSION_PROOF(element))
5838           RemoveMovingField(x, y);
5839       }
5840
5841       // indestructible elements can only explode in center (but not flames)
5842       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5843                                            mode == EX_TYPE_BORDER)) ||
5844           element == EL_FLAMES)
5845         continue;
5846
5847       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5848          behaviour, for example when touching a yamyam that explodes to rocks
5849          with active deadly shield, a rock is created under the player !!! */
5850       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5851 #if 0
5852       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5853           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5854            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5855 #else
5856       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5857 #endif
5858       {
5859         if (IS_ACTIVE_BOMB(element))
5860         {
5861           // re-activate things under the bomb like gate or penguin
5862           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5863           Back[x][y] = 0;
5864         }
5865
5866         continue;
5867       }
5868
5869       // save walkable background elements while explosion on same tile
5870       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5871           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5872         Back[x][y] = element;
5873
5874       // ignite explodable elements reached by other explosion
5875       if (element == EL_EXPLOSION)
5876         element = Store2[x][y];
5877
5878       if (AmoebaNr[x][y] &&
5879           (element == EL_AMOEBA_FULL ||
5880            element == EL_BD_AMOEBA ||
5881            element == EL_AMOEBA_GROWING))
5882       {
5883         AmoebaCnt[AmoebaNr[x][y]]--;
5884         AmoebaCnt2[AmoebaNr[x][y]]--;
5885       }
5886
5887       RemoveField(x, y);
5888
5889       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5890       {
5891         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5892
5893         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5894
5895         if (PLAYERINFO(ex, ey)->use_murphy)
5896           Store[x][y] = EL_EMPTY;
5897       }
5898
5899       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5900       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5901       else if (IS_PLAYER_ELEMENT(center_element))
5902         Store[x][y] = EL_EMPTY;
5903       else if (center_element == EL_YAMYAM)
5904         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5905       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5906         Store[x][y] = element_info[center_element].content.e[xx][yy];
5907 #if 1
5908       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5909       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5910       // otherwise) -- FIX THIS !!!
5911       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5912         Store[x][y] = element_info[element].content.e[1][1];
5913 #else
5914       else if (!CAN_EXPLODE(element))
5915         Store[x][y] = element_info[element].content.e[1][1];
5916 #endif
5917       else
5918         Store[x][y] = EL_EMPTY;
5919
5920       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5921           center_element == EL_AMOEBA_TO_DIAMOND)
5922         Store2[x][y] = element;
5923
5924       Tile[x][y] = EL_EXPLOSION;
5925       GfxElement[x][y] = artwork_element;
5926
5927       ExplodePhase[x][y] = 1;
5928       ExplodeDelay[x][y] = last_phase;
5929
5930       Stop[x][y] = TRUE;
5931     }
5932
5933     if (center_element == EL_YAMYAM)
5934       game.yamyam_content_nr =
5935         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5936
5937     return;
5938   }
5939
5940   if (Stop[ex][ey])
5941     return;
5942
5943   x = ex;
5944   y = ey;
5945
5946   if (phase == 1)
5947     GfxFrame[x][y] = 0;         // restart explosion animation
5948
5949   last_phase = ExplodeDelay[x][y];
5950
5951   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5952
5953   // this can happen if the player leaves an explosion just in time
5954   if (GfxElement[x][y] == EL_UNDEFINED)
5955     GfxElement[x][y] = EL_EMPTY;
5956
5957   border_element = Store2[x][y];
5958   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5959     border_element = StorePlayer[x][y];
5960
5961   if (phase == element_info[border_element].ignition_delay ||
5962       phase == last_phase)
5963   {
5964     boolean border_explosion = FALSE;
5965
5966     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5967         !PLAYER_EXPLOSION_PROTECTED(x, y))
5968     {
5969       KillPlayerUnlessExplosionProtected(x, y);
5970       border_explosion = TRUE;
5971     }
5972     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5973     {
5974       Tile[x][y] = Store2[x][y];
5975       Store2[x][y] = 0;
5976       Bang(x, y);
5977       border_explosion = TRUE;
5978     }
5979     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5980     {
5981       AmoebaToDiamond(x, y);
5982       Store2[x][y] = 0;
5983       border_explosion = TRUE;
5984     }
5985
5986     // if an element just explodes due to another explosion (chain-reaction),
5987     // do not immediately end the new explosion when it was the last frame of
5988     // the explosion (as it would be done in the following "if"-statement!)
5989     if (border_explosion && phase == last_phase)
5990       return;
5991   }
5992
5993   if (phase == last_phase)
5994   {
5995     int element;
5996
5997     element = Tile[x][y] = Store[x][y];
5998     Store[x][y] = Store2[x][y] = 0;
5999     GfxElement[x][y] = EL_UNDEFINED;
6000
6001     // player can escape from explosions and might therefore be still alive
6002     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6003         element <= EL_PLAYER_IS_EXPLODING_4)
6004     {
6005       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6006       int explosion_element = EL_PLAYER_1 + player_nr;
6007       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6008       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6009
6010       if (level.use_explosion_element[player_nr])
6011         explosion_element = level.explosion_element[player_nr];
6012
6013       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6014                     element_info[explosion_element].content.e[xx][yy]);
6015     }
6016
6017     // restore probably existing indestructible background element
6018     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6019       element = Tile[x][y] = Back[x][y];
6020     Back[x][y] = 0;
6021
6022     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6023     GfxDir[x][y] = MV_NONE;
6024     ChangeDelay[x][y] = 0;
6025     ChangePage[x][y] = -1;
6026
6027     CustomValue[x][y] = 0;
6028
6029     InitField_WithBug2(x, y, FALSE);
6030
6031     TEST_DrawLevelField(x, y);
6032
6033     TestIfElementTouchesCustomElement(x, y);
6034
6035     if (GFX_CRUMBLED(element))
6036       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6037
6038     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6039       StorePlayer[x][y] = 0;
6040
6041     if (IS_PLAYER_ELEMENT(element))
6042       RelocatePlayer(x, y, element);
6043   }
6044   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6045   {
6046     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6047     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6048
6049     if (phase == delay)
6050       TEST_DrawLevelFieldCrumbled(x, y);
6051
6052     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6053     {
6054       DrawLevelElement(x, y, Back[x][y]);
6055       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6056     }
6057     else if (IS_WALKABLE_UNDER(Back[x][y]))
6058     {
6059       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6060       DrawLevelElementThruMask(x, y, Back[x][y]);
6061     }
6062     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6063       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6064   }
6065 }
6066
6067 static void DynaExplode(int ex, int ey)
6068 {
6069   int i, j;
6070   int dynabomb_element = Tile[ex][ey];
6071   int dynabomb_size = 1;
6072   boolean dynabomb_xl = FALSE;
6073   struct PlayerInfo *player;
6074   static int xy[4][2] =
6075   {
6076     { 0, -1 },
6077     { -1, 0 },
6078     { +1, 0 },
6079     { 0, +1 }
6080   };
6081
6082   if (IS_ACTIVE_BOMB(dynabomb_element))
6083   {
6084     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6085     dynabomb_size = player->dynabomb_size;
6086     dynabomb_xl = player->dynabomb_xl;
6087     player->dynabombs_left++;
6088   }
6089
6090   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6091
6092   for (i = 0; i < NUM_DIRECTIONS; i++)
6093   {
6094     for (j = 1; j <= dynabomb_size; j++)
6095     {
6096       int x = ex + j * xy[i][0];
6097       int y = ey + j * xy[i][1];
6098       int element;
6099
6100       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6101         break;
6102
6103       element = Tile[x][y];
6104
6105       // do not restart explosions of fields with active bombs
6106       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6107         continue;
6108
6109       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6110
6111       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6112           !IS_DIGGABLE(element) && !dynabomb_xl)
6113         break;
6114     }
6115   }
6116 }
6117
6118 void Bang(int x, int y)
6119 {
6120   int element = MovingOrBlocked2Element(x, y);
6121   int explosion_type = EX_TYPE_NORMAL;
6122
6123   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6124   {
6125     struct PlayerInfo *player = PLAYERINFO(x, y);
6126
6127     element = Tile[x][y] = player->initial_element;
6128
6129     if (level.use_explosion_element[player->index_nr])
6130     {
6131       int explosion_element = level.explosion_element[player->index_nr];
6132
6133       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6134         explosion_type = EX_TYPE_CROSS;
6135       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6136         explosion_type = EX_TYPE_CENTER;
6137     }
6138   }
6139
6140   switch (element)
6141   {
6142     case EL_BUG:
6143     case EL_SPACESHIP:
6144     case EL_BD_BUTTERFLY:
6145     case EL_BD_FIREFLY:
6146     case EL_YAMYAM:
6147     case EL_DARK_YAMYAM:
6148     case EL_ROBOT:
6149     case EL_PACMAN:
6150     case EL_MOLE:
6151       RaiseScoreElement(element);
6152       break;
6153
6154     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6155     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6156     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6157     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6158     case EL_DYNABOMB_INCREASE_NUMBER:
6159     case EL_DYNABOMB_INCREASE_SIZE:
6160     case EL_DYNABOMB_INCREASE_POWER:
6161       explosion_type = EX_TYPE_DYNA;
6162       break;
6163
6164     case EL_DC_LANDMINE:
6165       explosion_type = EX_TYPE_CENTER;
6166       break;
6167
6168     case EL_PENGUIN:
6169     case EL_LAMP:
6170     case EL_LAMP_ACTIVE:
6171     case EL_AMOEBA_TO_DIAMOND:
6172       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6173         explosion_type = EX_TYPE_CENTER;
6174       break;
6175
6176     default:
6177       if (element_info[element].explosion_type == EXPLODES_CROSS)
6178         explosion_type = EX_TYPE_CROSS;
6179       else if (element_info[element].explosion_type == EXPLODES_1X1)
6180         explosion_type = EX_TYPE_CENTER;
6181       break;
6182   }
6183
6184   if (explosion_type == EX_TYPE_DYNA)
6185     DynaExplode(x, y);
6186   else
6187     Explode(x, y, EX_PHASE_START, explosion_type);
6188
6189   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6190 }
6191
6192 static void SplashAcid(int x, int y)
6193 {
6194   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6195       (!IN_LEV_FIELD(x - 1, y - 2) ||
6196        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6197     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6198
6199   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6200       (!IN_LEV_FIELD(x + 1, y - 2) ||
6201        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6202     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6203
6204   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6205 }
6206
6207 static void InitBeltMovement(void)
6208 {
6209   static int belt_base_element[4] =
6210   {
6211     EL_CONVEYOR_BELT_1_LEFT,
6212     EL_CONVEYOR_BELT_2_LEFT,
6213     EL_CONVEYOR_BELT_3_LEFT,
6214     EL_CONVEYOR_BELT_4_LEFT
6215   };
6216   static int belt_base_active_element[4] =
6217   {
6218     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6219     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6220     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6221     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6222   };
6223
6224   int x, y, i, j;
6225
6226   // set frame order for belt animation graphic according to belt direction
6227   for (i = 0; i < NUM_BELTS; i++)
6228   {
6229     int belt_nr = i;
6230
6231     for (j = 0; j < NUM_BELT_PARTS; j++)
6232     {
6233       int element = belt_base_active_element[belt_nr] + j;
6234       int graphic_1 = el2img(element);
6235       int graphic_2 = el2panelimg(element);
6236
6237       if (game.belt_dir[i] == MV_LEFT)
6238       {
6239         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6240         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6241       }
6242       else
6243       {
6244         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6245         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6246       }
6247     }
6248   }
6249
6250   SCAN_PLAYFIELD(x, y)
6251   {
6252     int element = Tile[x][y];
6253
6254     for (i = 0; i < NUM_BELTS; i++)
6255     {
6256       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6257       {
6258         int e_belt_nr = getBeltNrFromBeltElement(element);
6259         int belt_nr = i;
6260
6261         if (e_belt_nr == belt_nr)
6262         {
6263           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6264
6265           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6266         }
6267       }
6268     }
6269   }
6270 }
6271
6272 static void ToggleBeltSwitch(int x, int y)
6273 {
6274   static int belt_base_element[4] =
6275   {
6276     EL_CONVEYOR_BELT_1_LEFT,
6277     EL_CONVEYOR_BELT_2_LEFT,
6278     EL_CONVEYOR_BELT_3_LEFT,
6279     EL_CONVEYOR_BELT_4_LEFT
6280   };
6281   static int belt_base_active_element[4] =
6282   {
6283     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6284     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6285     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6286     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6287   };
6288   static int belt_base_switch_element[4] =
6289   {
6290     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6291     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6292     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6293     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6294   };
6295   static int belt_move_dir[4] =
6296   {
6297     MV_LEFT,
6298     MV_NONE,
6299     MV_RIGHT,
6300     MV_NONE,
6301   };
6302
6303   int element = Tile[x][y];
6304   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6305   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6306   int belt_dir = belt_move_dir[belt_dir_nr];
6307   int xx, yy, i;
6308
6309   if (!IS_BELT_SWITCH(element))
6310     return;
6311
6312   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6313   game.belt_dir[belt_nr] = belt_dir;
6314
6315   if (belt_dir_nr == 3)
6316     belt_dir_nr = 1;
6317
6318   // set frame order for belt animation graphic according to belt direction
6319   for (i = 0; i < NUM_BELT_PARTS; i++)
6320   {
6321     int element = belt_base_active_element[belt_nr] + i;
6322     int graphic_1 = el2img(element);
6323     int graphic_2 = el2panelimg(element);
6324
6325     if (belt_dir == MV_LEFT)
6326     {
6327       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6328       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6329     }
6330     else
6331     {
6332       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6333       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6334     }
6335   }
6336
6337   SCAN_PLAYFIELD(xx, yy)
6338   {
6339     int element = Tile[xx][yy];
6340
6341     if (IS_BELT_SWITCH(element))
6342     {
6343       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6344
6345       if (e_belt_nr == belt_nr)
6346       {
6347         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6348         TEST_DrawLevelField(xx, yy);
6349       }
6350     }
6351     else if (IS_BELT(element) && belt_dir != MV_NONE)
6352     {
6353       int e_belt_nr = getBeltNrFromBeltElement(element);
6354
6355       if (e_belt_nr == belt_nr)
6356       {
6357         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6358
6359         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6360         TEST_DrawLevelField(xx, yy);
6361       }
6362     }
6363     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6364     {
6365       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6366
6367       if (e_belt_nr == belt_nr)
6368       {
6369         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6370
6371         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6372         TEST_DrawLevelField(xx, yy);
6373       }
6374     }
6375   }
6376 }
6377
6378 static void ToggleSwitchgateSwitch(int x, int y)
6379 {
6380   int xx, yy;
6381
6382   game.switchgate_pos = !game.switchgate_pos;
6383
6384   SCAN_PLAYFIELD(xx, yy)
6385   {
6386     int element = Tile[xx][yy];
6387
6388     if (element == EL_SWITCHGATE_SWITCH_UP)
6389     {
6390       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6391       TEST_DrawLevelField(xx, yy);
6392     }
6393     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6394     {
6395       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6396       TEST_DrawLevelField(xx, yy);
6397     }
6398     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6399     {
6400       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6401       TEST_DrawLevelField(xx, yy);
6402     }
6403     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6404     {
6405       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6406       TEST_DrawLevelField(xx, yy);
6407     }
6408     else if (element == EL_SWITCHGATE_OPEN ||
6409              element == EL_SWITCHGATE_OPENING)
6410     {
6411       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6412
6413       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6414     }
6415     else if (element == EL_SWITCHGATE_CLOSED ||
6416              element == EL_SWITCHGATE_CLOSING)
6417     {
6418       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6419
6420       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6421     }
6422   }
6423 }
6424
6425 static int getInvisibleActiveFromInvisibleElement(int element)
6426 {
6427   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6428           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6429           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6430           element);
6431 }
6432
6433 static int getInvisibleFromInvisibleActiveElement(int element)
6434 {
6435   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6436           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6437           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6438           element);
6439 }
6440
6441 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6442 {
6443   int x, y;
6444
6445   SCAN_PLAYFIELD(x, y)
6446   {
6447     int element = Tile[x][y];
6448
6449     if (element == EL_LIGHT_SWITCH &&
6450         game.light_time_left > 0)
6451     {
6452       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6453       TEST_DrawLevelField(x, y);
6454     }
6455     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6456              game.light_time_left == 0)
6457     {
6458       Tile[x][y] = EL_LIGHT_SWITCH;
6459       TEST_DrawLevelField(x, y);
6460     }
6461     else if (element == EL_EMC_DRIPPER &&
6462              game.light_time_left > 0)
6463     {
6464       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6465       TEST_DrawLevelField(x, y);
6466     }
6467     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6468              game.light_time_left == 0)
6469     {
6470       Tile[x][y] = EL_EMC_DRIPPER;
6471       TEST_DrawLevelField(x, y);
6472     }
6473     else if (element == EL_INVISIBLE_STEELWALL ||
6474              element == EL_INVISIBLE_WALL ||
6475              element == EL_INVISIBLE_SAND)
6476     {
6477       if (game.light_time_left > 0)
6478         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6479
6480       TEST_DrawLevelField(x, y);
6481
6482       // uncrumble neighbour fields, if needed
6483       if (element == EL_INVISIBLE_SAND)
6484         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6485     }
6486     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6487              element == EL_INVISIBLE_WALL_ACTIVE ||
6488              element == EL_INVISIBLE_SAND_ACTIVE)
6489     {
6490       if (game.light_time_left == 0)
6491         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6492
6493       TEST_DrawLevelField(x, y);
6494
6495       // re-crumble neighbour fields, if needed
6496       if (element == EL_INVISIBLE_SAND)
6497         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6498     }
6499   }
6500 }
6501
6502 static void RedrawAllInvisibleElementsForLenses(void)
6503 {
6504   int x, y;
6505
6506   SCAN_PLAYFIELD(x, y)
6507   {
6508     int element = Tile[x][y];
6509
6510     if (element == EL_EMC_DRIPPER &&
6511         game.lenses_time_left > 0)
6512     {
6513       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6514       TEST_DrawLevelField(x, y);
6515     }
6516     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6517              game.lenses_time_left == 0)
6518     {
6519       Tile[x][y] = EL_EMC_DRIPPER;
6520       TEST_DrawLevelField(x, y);
6521     }
6522     else if (element == EL_INVISIBLE_STEELWALL ||
6523              element == EL_INVISIBLE_WALL ||
6524              element == EL_INVISIBLE_SAND)
6525     {
6526       if (game.lenses_time_left > 0)
6527         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6528
6529       TEST_DrawLevelField(x, y);
6530
6531       // uncrumble neighbour fields, if needed
6532       if (element == EL_INVISIBLE_SAND)
6533         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6534     }
6535     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6536              element == EL_INVISIBLE_WALL_ACTIVE ||
6537              element == EL_INVISIBLE_SAND_ACTIVE)
6538     {
6539       if (game.lenses_time_left == 0)
6540         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6541
6542       TEST_DrawLevelField(x, y);
6543
6544       // re-crumble neighbour fields, if needed
6545       if (element == EL_INVISIBLE_SAND)
6546         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6547     }
6548   }
6549 }
6550
6551 static void RedrawAllInvisibleElementsForMagnifier(void)
6552 {
6553   int x, y;
6554
6555   SCAN_PLAYFIELD(x, y)
6556   {
6557     int element = Tile[x][y];
6558
6559     if (element == EL_EMC_FAKE_GRASS &&
6560         game.magnify_time_left > 0)
6561     {
6562       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6563       TEST_DrawLevelField(x, y);
6564     }
6565     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6566              game.magnify_time_left == 0)
6567     {
6568       Tile[x][y] = EL_EMC_FAKE_GRASS;
6569       TEST_DrawLevelField(x, y);
6570     }
6571     else if (IS_GATE_GRAY(element) &&
6572              game.magnify_time_left > 0)
6573     {
6574       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6575                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6576                     IS_EM_GATE_GRAY(element) ?
6577                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6578                     IS_EMC_GATE_GRAY(element) ?
6579                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6580                     IS_DC_GATE_GRAY(element) ?
6581                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6582                     element);
6583       TEST_DrawLevelField(x, y);
6584     }
6585     else if (IS_GATE_GRAY_ACTIVE(element) &&
6586              game.magnify_time_left == 0)
6587     {
6588       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6589                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6590                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6591                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6592                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6593                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6594                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6595                     EL_DC_GATE_WHITE_GRAY :
6596                     element);
6597       TEST_DrawLevelField(x, y);
6598     }
6599   }
6600 }
6601
6602 static void ToggleLightSwitch(int x, int y)
6603 {
6604   int element = Tile[x][y];
6605
6606   game.light_time_left =
6607     (element == EL_LIGHT_SWITCH ?
6608      level.time_light * FRAMES_PER_SECOND : 0);
6609
6610   RedrawAllLightSwitchesAndInvisibleElements();
6611 }
6612
6613 static void ActivateTimegateSwitch(int x, int y)
6614 {
6615   int xx, yy;
6616
6617   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6618
6619   SCAN_PLAYFIELD(xx, yy)
6620   {
6621     int element = Tile[xx][yy];
6622
6623     if (element == EL_TIMEGATE_CLOSED ||
6624         element == EL_TIMEGATE_CLOSING)
6625     {
6626       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6627       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6628     }
6629
6630     /*
6631     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6632     {
6633       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6634       TEST_DrawLevelField(xx, yy);
6635     }
6636     */
6637
6638   }
6639
6640   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6641                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6642 }
6643
6644 static void Impact(int x, int y)
6645 {
6646   boolean last_line = (y == lev_fieldy - 1);
6647   boolean object_hit = FALSE;
6648   boolean impact = (last_line || object_hit);
6649   int element = Tile[x][y];
6650   int smashed = EL_STEELWALL;
6651
6652   if (!last_line)       // check if element below was hit
6653   {
6654     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6655       return;
6656
6657     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6658                                          MovDir[x][y + 1] != MV_DOWN ||
6659                                          MovPos[x][y + 1] <= TILEY / 2));
6660
6661     // do not smash moving elements that left the smashed field in time
6662     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6663         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6664       object_hit = FALSE;
6665
6666 #if USE_QUICKSAND_IMPACT_BUGFIX
6667     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6668     {
6669       RemoveMovingField(x, y + 1);
6670       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6671       Tile[x][y + 2] = EL_ROCK;
6672       TEST_DrawLevelField(x, y + 2);
6673
6674       object_hit = TRUE;
6675     }
6676
6677     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6678     {
6679       RemoveMovingField(x, y + 1);
6680       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6681       Tile[x][y + 2] = EL_ROCK;
6682       TEST_DrawLevelField(x, y + 2);
6683
6684       object_hit = TRUE;
6685     }
6686 #endif
6687
6688     if (object_hit)
6689       smashed = MovingOrBlocked2Element(x, y + 1);
6690
6691     impact = (last_line || object_hit);
6692   }
6693
6694   if (!last_line && smashed == EL_ACID) // element falls into acid
6695   {
6696     SplashAcid(x, y + 1);
6697     return;
6698   }
6699
6700   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6701   // only reset graphic animation if graphic really changes after impact
6702   if (impact &&
6703       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6704   {
6705     ResetGfxAnimation(x, y);
6706     TEST_DrawLevelField(x, y);
6707   }
6708
6709   if (impact && CAN_EXPLODE_IMPACT(element))
6710   {
6711     Bang(x, y);
6712     return;
6713   }
6714   else if (impact && element == EL_PEARL &&
6715            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6716   {
6717     ResetGfxAnimation(x, y);
6718
6719     Tile[x][y] = EL_PEARL_BREAKING;
6720     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6721     return;
6722   }
6723   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6724   {
6725     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6726
6727     return;
6728   }
6729
6730   if (impact && element == EL_AMOEBA_DROP)
6731   {
6732     if (object_hit && IS_PLAYER(x, y + 1))
6733       KillPlayerUnlessEnemyProtected(x, y + 1);
6734     else if (object_hit && smashed == EL_PENGUIN)
6735       Bang(x, y + 1);
6736     else
6737     {
6738       Tile[x][y] = EL_AMOEBA_GROWING;
6739       Store[x][y] = EL_AMOEBA_WET;
6740
6741       ResetRandomAnimationValue(x, y);
6742     }
6743     return;
6744   }
6745
6746   if (object_hit)               // check which object was hit
6747   {
6748     if ((CAN_PASS_MAGIC_WALL(element) && 
6749          (smashed == EL_MAGIC_WALL ||
6750           smashed == EL_BD_MAGIC_WALL)) ||
6751         (CAN_PASS_DC_MAGIC_WALL(element) &&
6752          smashed == EL_DC_MAGIC_WALL))
6753     {
6754       int xx, yy;
6755       int activated_magic_wall =
6756         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6757          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6758          EL_DC_MAGIC_WALL_ACTIVE);
6759
6760       // activate magic wall / mill
6761       SCAN_PLAYFIELD(xx, yy)
6762       {
6763         if (Tile[xx][yy] == smashed)
6764           Tile[xx][yy] = activated_magic_wall;
6765       }
6766
6767       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6768       game.magic_wall_active = TRUE;
6769
6770       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6771                             SND_MAGIC_WALL_ACTIVATING :
6772                             smashed == EL_BD_MAGIC_WALL ?
6773                             SND_BD_MAGIC_WALL_ACTIVATING :
6774                             SND_DC_MAGIC_WALL_ACTIVATING));
6775     }
6776
6777     if (IS_PLAYER(x, y + 1))
6778     {
6779       if (CAN_SMASH_PLAYER(element))
6780       {
6781         KillPlayerUnlessEnemyProtected(x, y + 1);
6782         return;
6783       }
6784     }
6785     else if (smashed == EL_PENGUIN)
6786     {
6787       if (CAN_SMASH_PLAYER(element))
6788       {
6789         Bang(x, y + 1);
6790         return;
6791       }
6792     }
6793     else if (element == EL_BD_DIAMOND)
6794     {
6795       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6796       {
6797         Bang(x, y + 1);
6798         return;
6799       }
6800     }
6801     else if (((element == EL_SP_INFOTRON ||
6802                element == EL_SP_ZONK) &&
6803               (smashed == EL_SP_SNIKSNAK ||
6804                smashed == EL_SP_ELECTRON ||
6805                smashed == EL_SP_DISK_ORANGE)) ||
6806              (element == EL_SP_INFOTRON &&
6807               smashed == EL_SP_DISK_YELLOW))
6808     {
6809       Bang(x, y + 1);
6810       return;
6811     }
6812     else if (CAN_SMASH_EVERYTHING(element))
6813     {
6814       if (IS_CLASSIC_ENEMY(smashed) ||
6815           CAN_EXPLODE_SMASHED(smashed))
6816       {
6817         Bang(x, y + 1);
6818         return;
6819       }
6820       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6821       {
6822         if (smashed == EL_LAMP ||
6823             smashed == EL_LAMP_ACTIVE)
6824         {
6825           Bang(x, y + 1);
6826           return;
6827         }
6828         else if (smashed == EL_NUT)
6829         {
6830           Tile[x][y + 1] = EL_NUT_BREAKING;
6831           PlayLevelSound(x, y, SND_NUT_BREAKING);
6832           RaiseScoreElement(EL_NUT);
6833           return;
6834         }
6835         else if (smashed == EL_PEARL)
6836         {
6837           ResetGfxAnimation(x, y);
6838
6839           Tile[x][y + 1] = EL_PEARL_BREAKING;
6840           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6841           return;
6842         }
6843         else if (smashed == EL_DIAMOND)
6844         {
6845           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6846           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6847           return;
6848         }
6849         else if (IS_BELT_SWITCH(smashed))
6850         {
6851           ToggleBeltSwitch(x, y + 1);
6852         }
6853         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6854                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6855                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6856                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6857         {
6858           ToggleSwitchgateSwitch(x, y + 1);
6859         }
6860         else if (smashed == EL_LIGHT_SWITCH ||
6861                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6862         {
6863           ToggleLightSwitch(x, y + 1);
6864         }
6865         else
6866         {
6867           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6868
6869           CheckElementChangeBySide(x, y + 1, smashed, element,
6870                                    CE_SWITCHED, CH_SIDE_TOP);
6871           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6872                                             CH_SIDE_TOP);
6873         }
6874       }
6875       else
6876       {
6877         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6878       }
6879     }
6880   }
6881
6882   // play sound of magic wall / mill
6883   if (!last_line &&
6884       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6885        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6886        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6887   {
6888     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6889       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6890     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6891       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6892     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6893       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6894
6895     return;
6896   }
6897
6898   // play sound of object that hits the ground
6899   if (last_line || object_hit)
6900     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6901 }
6902
6903 static void TurnRoundExt(int x, int y)
6904 {
6905   static struct
6906   {
6907     int dx, dy;
6908   } move_xy[] =
6909   {
6910     {  0,  0 },
6911     { -1,  0 },
6912     { +1,  0 },
6913     {  0,  0 },
6914     {  0, -1 },
6915     {  0,  0 }, { 0, 0 }, { 0, 0 },
6916     {  0, +1 }
6917   };
6918   static struct
6919   {
6920     int left, right, back;
6921   } turn[] =
6922   {
6923     { 0,        0,              0        },
6924     { MV_DOWN,  MV_UP,          MV_RIGHT },
6925     { MV_UP,    MV_DOWN,        MV_LEFT  },
6926     { 0,        0,              0        },
6927     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6928     { 0,        0,              0        },
6929     { 0,        0,              0        },
6930     { 0,        0,              0        },
6931     { MV_RIGHT, MV_LEFT,        MV_UP    }
6932   };
6933
6934   int element = Tile[x][y];
6935   int move_pattern = element_info[element].move_pattern;
6936
6937   int old_move_dir = MovDir[x][y];
6938   int left_dir  = turn[old_move_dir].left;
6939   int right_dir = turn[old_move_dir].right;
6940   int back_dir  = turn[old_move_dir].back;
6941
6942   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6943   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6944   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6945   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6946
6947   int left_x  = x + left_dx,  left_y  = y + left_dy;
6948   int right_x = x + right_dx, right_y = y + right_dy;
6949   int move_x  = x + move_dx,  move_y  = y + move_dy;
6950
6951   int xx, yy;
6952
6953   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6954   {
6955     TestIfBadThingTouchesOtherBadThing(x, y);
6956
6957     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6958       MovDir[x][y] = right_dir;
6959     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6960       MovDir[x][y] = left_dir;
6961
6962     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6963       MovDelay[x][y] = 9;
6964     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6965       MovDelay[x][y] = 1;
6966   }
6967   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6968   {
6969     TestIfBadThingTouchesOtherBadThing(x, y);
6970
6971     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6972       MovDir[x][y] = left_dir;
6973     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6974       MovDir[x][y] = right_dir;
6975
6976     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6977       MovDelay[x][y] = 9;
6978     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6979       MovDelay[x][y] = 1;
6980   }
6981   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6982   {
6983     TestIfBadThingTouchesOtherBadThing(x, y);
6984
6985     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6986       MovDir[x][y] = left_dir;
6987     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6988       MovDir[x][y] = right_dir;
6989
6990     if (MovDir[x][y] != old_move_dir)
6991       MovDelay[x][y] = 9;
6992   }
6993   else if (element == EL_YAMYAM)
6994   {
6995     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6996     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6997
6998     if (can_turn_left && can_turn_right)
6999       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7000     else if (can_turn_left)
7001       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7002     else if (can_turn_right)
7003       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7004     else
7005       MovDir[x][y] = back_dir;
7006
7007     MovDelay[x][y] = 16 + 16 * RND(3);
7008   }
7009   else if (element == EL_DARK_YAMYAM)
7010   {
7011     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7012                                                          left_x, left_y);
7013     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7014                                                          right_x, right_y);
7015
7016     if (can_turn_left && can_turn_right)
7017       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7018     else if (can_turn_left)
7019       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7020     else if (can_turn_right)
7021       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7022     else
7023       MovDir[x][y] = back_dir;
7024
7025     MovDelay[x][y] = 16 + 16 * RND(3);
7026   }
7027   else if (element == EL_PACMAN)
7028   {
7029     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7030     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7031
7032     if (can_turn_left && can_turn_right)
7033       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7034     else if (can_turn_left)
7035       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7036     else if (can_turn_right)
7037       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7038     else
7039       MovDir[x][y] = back_dir;
7040
7041     MovDelay[x][y] = 6 + RND(40);
7042   }
7043   else if (element == EL_PIG)
7044   {
7045     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7046     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7047     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7048     boolean should_turn_left, should_turn_right, should_move_on;
7049     int rnd_value = 24;
7050     int rnd = RND(rnd_value);
7051
7052     should_turn_left = (can_turn_left &&
7053                         (!can_move_on ||
7054                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7055                                                    y + back_dy + left_dy)));
7056     should_turn_right = (can_turn_right &&
7057                          (!can_move_on ||
7058                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7059                                                     y + back_dy + right_dy)));
7060     should_move_on = (can_move_on &&
7061                       (!can_turn_left ||
7062                        !can_turn_right ||
7063                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7064                                                  y + move_dy + left_dy) ||
7065                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7066                                                  y + move_dy + right_dy)));
7067
7068     if (should_turn_left || should_turn_right || should_move_on)
7069     {
7070       if (should_turn_left && should_turn_right && should_move_on)
7071         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7072                         rnd < 2 * rnd_value / 3 ? right_dir :
7073                         old_move_dir);
7074       else if (should_turn_left && should_turn_right)
7075         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7076       else if (should_turn_left && should_move_on)
7077         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7078       else if (should_turn_right && should_move_on)
7079         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7080       else if (should_turn_left)
7081         MovDir[x][y] = left_dir;
7082       else if (should_turn_right)
7083         MovDir[x][y] = right_dir;
7084       else if (should_move_on)
7085         MovDir[x][y] = old_move_dir;
7086     }
7087     else if (can_move_on && rnd > rnd_value / 8)
7088       MovDir[x][y] = old_move_dir;
7089     else if (can_turn_left && can_turn_right)
7090       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7091     else if (can_turn_left && rnd > rnd_value / 8)
7092       MovDir[x][y] = left_dir;
7093     else if (can_turn_right && rnd > rnd_value/8)
7094       MovDir[x][y] = right_dir;
7095     else
7096       MovDir[x][y] = back_dir;
7097
7098     xx = x + move_xy[MovDir[x][y]].dx;
7099     yy = y + move_xy[MovDir[x][y]].dy;
7100
7101     if (!IN_LEV_FIELD(xx, yy) ||
7102         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7103       MovDir[x][y] = old_move_dir;
7104
7105     MovDelay[x][y] = 0;
7106   }
7107   else if (element == EL_DRAGON)
7108   {
7109     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7110     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7111     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7112     int rnd_value = 24;
7113     int rnd = RND(rnd_value);
7114
7115     if (can_move_on && rnd > rnd_value / 8)
7116       MovDir[x][y] = old_move_dir;
7117     else if (can_turn_left && can_turn_right)
7118       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7119     else if (can_turn_left && rnd > rnd_value / 8)
7120       MovDir[x][y] = left_dir;
7121     else if (can_turn_right && rnd > rnd_value / 8)
7122       MovDir[x][y] = right_dir;
7123     else
7124       MovDir[x][y] = back_dir;
7125
7126     xx = x + move_xy[MovDir[x][y]].dx;
7127     yy = y + move_xy[MovDir[x][y]].dy;
7128
7129     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7130       MovDir[x][y] = old_move_dir;
7131
7132     MovDelay[x][y] = 0;
7133   }
7134   else if (element == EL_MOLE)
7135   {
7136     boolean can_move_on =
7137       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7138                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7139                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7140     if (!can_move_on)
7141     {
7142       boolean can_turn_left =
7143         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7144                               IS_AMOEBOID(Tile[left_x][left_y])));
7145
7146       boolean can_turn_right =
7147         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7148                               IS_AMOEBOID(Tile[right_x][right_y])));
7149
7150       if (can_turn_left && can_turn_right)
7151         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7152       else if (can_turn_left)
7153         MovDir[x][y] = left_dir;
7154       else
7155         MovDir[x][y] = right_dir;
7156     }
7157
7158     if (MovDir[x][y] != old_move_dir)
7159       MovDelay[x][y] = 9;
7160   }
7161   else if (element == EL_BALLOON)
7162   {
7163     MovDir[x][y] = game.wind_direction;
7164     MovDelay[x][y] = 0;
7165   }
7166   else if (element == EL_SPRING)
7167   {
7168     if (MovDir[x][y] & MV_HORIZONTAL)
7169     {
7170       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7171           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7172       {
7173         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7174         ResetGfxAnimation(move_x, move_y);
7175         TEST_DrawLevelField(move_x, move_y);
7176
7177         MovDir[x][y] = back_dir;
7178       }
7179       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7180                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7181         MovDir[x][y] = MV_NONE;
7182     }
7183
7184     MovDelay[x][y] = 0;
7185   }
7186   else if (element == EL_ROBOT ||
7187            element == EL_SATELLITE ||
7188            element == EL_PENGUIN ||
7189            element == EL_EMC_ANDROID)
7190   {
7191     int attr_x = -1, attr_y = -1;
7192
7193     if (game.all_players_gone)
7194     {
7195       attr_x = game.exit_x;
7196       attr_y = game.exit_y;
7197     }
7198     else
7199     {
7200       int i;
7201
7202       for (i = 0; i < MAX_PLAYERS; i++)
7203       {
7204         struct PlayerInfo *player = &stored_player[i];
7205         int jx = player->jx, jy = player->jy;
7206
7207         if (!player->active)
7208           continue;
7209
7210         if (attr_x == -1 ||
7211             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7212         {
7213           attr_x = jx;
7214           attr_y = jy;
7215         }
7216       }
7217     }
7218
7219     if (element == EL_ROBOT &&
7220         game.robot_wheel_x >= 0 &&
7221         game.robot_wheel_y >= 0 &&
7222         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7223          game.engine_version < VERSION_IDENT(3,1,0,0)))
7224     {
7225       attr_x = game.robot_wheel_x;
7226       attr_y = game.robot_wheel_y;
7227     }
7228
7229     if (element == EL_PENGUIN)
7230     {
7231       int i;
7232       static int xy[4][2] =
7233       {
7234         { 0, -1 },
7235         { -1, 0 },
7236         { +1, 0 },
7237         { 0, +1 }
7238       };
7239
7240       for (i = 0; i < NUM_DIRECTIONS; i++)
7241       {
7242         int ex = x + xy[i][0];
7243         int ey = y + xy[i][1];
7244
7245         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7246                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7247                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7248                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7249         {
7250           attr_x = ex;
7251           attr_y = ey;
7252           break;
7253         }
7254       }
7255     }
7256
7257     MovDir[x][y] = MV_NONE;
7258     if (attr_x < x)
7259       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7260     else if (attr_x > x)
7261       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7262     if (attr_y < y)
7263       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7264     else if (attr_y > y)
7265       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7266
7267     if (element == EL_ROBOT)
7268     {
7269       int newx, newy;
7270
7271       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7272         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7273       Moving2Blocked(x, y, &newx, &newy);
7274
7275       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7276         MovDelay[x][y] = 8 + 8 * !RND(3);
7277       else
7278         MovDelay[x][y] = 16;
7279     }
7280     else if (element == EL_PENGUIN)
7281     {
7282       int newx, newy;
7283
7284       MovDelay[x][y] = 1;
7285
7286       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7287       {
7288         boolean first_horiz = RND(2);
7289         int new_move_dir = MovDir[x][y];
7290
7291         MovDir[x][y] =
7292           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7293         Moving2Blocked(x, y, &newx, &newy);
7294
7295         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7296           return;
7297
7298         MovDir[x][y] =
7299           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7300         Moving2Blocked(x, y, &newx, &newy);
7301
7302         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7303           return;
7304
7305         MovDir[x][y] = old_move_dir;
7306         return;
7307       }
7308     }
7309     else if (element == EL_SATELLITE)
7310     {
7311       int newx, newy;
7312
7313       MovDelay[x][y] = 1;
7314
7315       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7316       {
7317         boolean first_horiz = RND(2);
7318         int new_move_dir = MovDir[x][y];
7319
7320         MovDir[x][y] =
7321           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7322         Moving2Blocked(x, y, &newx, &newy);
7323
7324         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7325           return;
7326
7327         MovDir[x][y] =
7328           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7329         Moving2Blocked(x, y, &newx, &newy);
7330
7331         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7332           return;
7333
7334         MovDir[x][y] = old_move_dir;
7335         return;
7336       }
7337     }
7338     else if (element == EL_EMC_ANDROID)
7339     {
7340       static int check_pos[16] =
7341       {
7342         -1,             //  0 => (invalid)
7343         7,              //  1 => MV_LEFT
7344         3,              //  2 => MV_RIGHT
7345         -1,             //  3 => (invalid)
7346         1,              //  4 =>            MV_UP
7347         0,              //  5 => MV_LEFT  | MV_UP
7348         2,              //  6 => MV_RIGHT | MV_UP
7349         -1,             //  7 => (invalid)
7350         5,              //  8 =>            MV_DOWN
7351         6,              //  9 => MV_LEFT  | MV_DOWN
7352         4,              // 10 => MV_RIGHT | MV_DOWN
7353         -1,             // 11 => (invalid)
7354         -1,             // 12 => (invalid)
7355         -1,             // 13 => (invalid)
7356         -1,             // 14 => (invalid)
7357         -1,             // 15 => (invalid)
7358       };
7359       static struct
7360       {
7361         int dx, dy;
7362         int dir;
7363       } check_xy[8] =
7364       {
7365         { -1, -1,       MV_LEFT  | MV_UP   },
7366         {  0, -1,                  MV_UP   },
7367         { +1, -1,       MV_RIGHT | MV_UP   },
7368         { +1,  0,       MV_RIGHT           },
7369         { +1, +1,       MV_RIGHT | MV_DOWN },
7370         {  0, +1,                  MV_DOWN },
7371         { -1, +1,       MV_LEFT  | MV_DOWN },
7372         { -1,  0,       MV_LEFT            },
7373       };
7374       int start_pos, check_order;
7375       boolean can_clone = FALSE;
7376       int i;
7377
7378       // check if there is any free field around current position
7379       for (i = 0; i < 8; i++)
7380       {
7381         int newx = x + check_xy[i].dx;
7382         int newy = y + check_xy[i].dy;
7383
7384         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7385         {
7386           can_clone = TRUE;
7387
7388           break;
7389         }
7390       }
7391
7392       if (can_clone)            // randomly find an element to clone
7393       {
7394         can_clone = FALSE;
7395
7396         start_pos = check_pos[RND(8)];
7397         check_order = (RND(2) ? -1 : +1);
7398
7399         for (i = 0; i < 8; i++)
7400         {
7401           int pos_raw = start_pos + i * check_order;
7402           int pos = (pos_raw + 8) % 8;
7403           int newx = x + check_xy[pos].dx;
7404           int newy = y + check_xy[pos].dy;
7405
7406           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7407           {
7408             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7409             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7410
7411             Store[x][y] = Tile[newx][newy];
7412
7413             can_clone = TRUE;
7414
7415             break;
7416           }
7417         }
7418       }
7419
7420       if (can_clone)            // randomly find a direction to move
7421       {
7422         can_clone = FALSE;
7423
7424         start_pos = check_pos[RND(8)];
7425         check_order = (RND(2) ? -1 : +1);
7426
7427         for (i = 0; i < 8; i++)
7428         {
7429           int pos_raw = start_pos + i * check_order;
7430           int pos = (pos_raw + 8) % 8;
7431           int newx = x + check_xy[pos].dx;
7432           int newy = y + check_xy[pos].dy;
7433           int new_move_dir = check_xy[pos].dir;
7434
7435           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7436           {
7437             MovDir[x][y] = new_move_dir;
7438             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7439
7440             can_clone = TRUE;
7441
7442             break;
7443           }
7444         }
7445       }
7446
7447       if (can_clone)            // cloning and moving successful
7448         return;
7449
7450       // cannot clone -- try to move towards player
7451
7452       start_pos = check_pos[MovDir[x][y] & 0x0f];
7453       check_order = (RND(2) ? -1 : +1);
7454
7455       for (i = 0; i < 3; i++)
7456       {
7457         // first check start_pos, then previous/next or (next/previous) pos
7458         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7459         int pos = (pos_raw + 8) % 8;
7460         int newx = x + check_xy[pos].dx;
7461         int newy = y + check_xy[pos].dy;
7462         int new_move_dir = check_xy[pos].dir;
7463
7464         if (IS_PLAYER(newx, newy))
7465           break;
7466
7467         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7468         {
7469           MovDir[x][y] = new_move_dir;
7470           MovDelay[x][y] = level.android_move_time * 8 + 1;
7471
7472           break;
7473         }
7474       }
7475     }
7476   }
7477   else if (move_pattern == MV_TURNING_LEFT ||
7478            move_pattern == MV_TURNING_RIGHT ||
7479            move_pattern == MV_TURNING_LEFT_RIGHT ||
7480            move_pattern == MV_TURNING_RIGHT_LEFT ||
7481            move_pattern == MV_TURNING_RANDOM ||
7482            move_pattern == MV_ALL_DIRECTIONS)
7483   {
7484     boolean can_turn_left =
7485       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7486     boolean can_turn_right =
7487       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7488
7489     if (element_info[element].move_stepsize == 0)       // "not moving"
7490       return;
7491
7492     if (move_pattern == MV_TURNING_LEFT)
7493       MovDir[x][y] = left_dir;
7494     else if (move_pattern == MV_TURNING_RIGHT)
7495       MovDir[x][y] = right_dir;
7496     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7497       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7498     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7499       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7500     else if (move_pattern == MV_TURNING_RANDOM)
7501       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7502                       can_turn_right && !can_turn_left ? right_dir :
7503                       RND(2) ? left_dir : right_dir);
7504     else if (can_turn_left && can_turn_right)
7505       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7506     else if (can_turn_left)
7507       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7508     else if (can_turn_right)
7509       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7510     else
7511       MovDir[x][y] = back_dir;
7512
7513     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7514   }
7515   else if (move_pattern == MV_HORIZONTAL ||
7516            move_pattern == MV_VERTICAL)
7517   {
7518     if (move_pattern & old_move_dir)
7519       MovDir[x][y] = back_dir;
7520     else if (move_pattern == MV_HORIZONTAL)
7521       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7522     else if (move_pattern == MV_VERTICAL)
7523       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7524
7525     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7526   }
7527   else if (move_pattern & MV_ANY_DIRECTION)
7528   {
7529     MovDir[x][y] = move_pattern;
7530     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7531   }
7532   else if (move_pattern & MV_WIND_DIRECTION)
7533   {
7534     MovDir[x][y] = game.wind_direction;
7535     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7536   }
7537   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7538   {
7539     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7540       MovDir[x][y] = left_dir;
7541     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7542       MovDir[x][y] = right_dir;
7543
7544     if (MovDir[x][y] != old_move_dir)
7545       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7546   }
7547   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7548   {
7549     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7550       MovDir[x][y] = right_dir;
7551     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7552       MovDir[x][y] = left_dir;
7553
7554     if (MovDir[x][y] != old_move_dir)
7555       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7556   }
7557   else if (move_pattern == MV_TOWARDS_PLAYER ||
7558            move_pattern == MV_AWAY_FROM_PLAYER)
7559   {
7560     int attr_x = -1, attr_y = -1;
7561     int newx, newy;
7562     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7563
7564     if (game.all_players_gone)
7565     {
7566       attr_x = game.exit_x;
7567       attr_y = game.exit_y;
7568     }
7569     else
7570     {
7571       int i;
7572
7573       for (i = 0; i < MAX_PLAYERS; i++)
7574       {
7575         struct PlayerInfo *player = &stored_player[i];
7576         int jx = player->jx, jy = player->jy;
7577
7578         if (!player->active)
7579           continue;
7580
7581         if (attr_x == -1 ||
7582             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7583         {
7584           attr_x = jx;
7585           attr_y = jy;
7586         }
7587       }
7588     }
7589
7590     MovDir[x][y] = MV_NONE;
7591     if (attr_x < x)
7592       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7593     else if (attr_x > x)
7594       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7595     if (attr_y < y)
7596       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7597     else if (attr_y > y)
7598       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7599
7600     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7601
7602     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7603     {
7604       boolean first_horiz = RND(2);
7605       int new_move_dir = MovDir[x][y];
7606
7607       if (element_info[element].move_stepsize == 0)     // "not moving"
7608       {
7609         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7610         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7611
7612         return;
7613       }
7614
7615       MovDir[x][y] =
7616         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7617       Moving2Blocked(x, y, &newx, &newy);
7618
7619       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7620         return;
7621
7622       MovDir[x][y] =
7623         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7624       Moving2Blocked(x, y, &newx, &newy);
7625
7626       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7627         return;
7628
7629       MovDir[x][y] = old_move_dir;
7630     }
7631   }
7632   else if (move_pattern == MV_WHEN_PUSHED ||
7633            move_pattern == MV_WHEN_DROPPED)
7634   {
7635     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7636       MovDir[x][y] = MV_NONE;
7637
7638     MovDelay[x][y] = 0;
7639   }
7640   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7641   {
7642     static int test_xy[7][2] =
7643     {
7644       { 0, -1 },
7645       { -1, 0 },
7646       { +1, 0 },
7647       { 0, +1 },
7648       { 0, -1 },
7649       { -1, 0 },
7650       { +1, 0 },
7651     };
7652     static int test_dir[7] =
7653     {
7654       MV_UP,
7655       MV_LEFT,
7656       MV_RIGHT,
7657       MV_DOWN,
7658       MV_UP,
7659       MV_LEFT,
7660       MV_RIGHT,
7661     };
7662     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7663     int move_preference = -1000000;     // start with very low preference
7664     int new_move_dir = MV_NONE;
7665     int start_test = RND(4);
7666     int i;
7667
7668     for (i = 0; i < NUM_DIRECTIONS; i++)
7669     {
7670       int move_dir = test_dir[start_test + i];
7671       int move_dir_preference;
7672
7673       xx = x + test_xy[start_test + i][0];
7674       yy = y + test_xy[start_test + i][1];
7675
7676       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7677           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7678       {
7679         new_move_dir = move_dir;
7680
7681         break;
7682       }
7683
7684       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7685         continue;
7686
7687       move_dir_preference = -1 * RunnerVisit[xx][yy];
7688       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7689         move_dir_preference = PlayerVisit[xx][yy];
7690
7691       if (move_dir_preference > move_preference)
7692       {
7693         // prefer field that has not been visited for the longest time
7694         move_preference = move_dir_preference;
7695         new_move_dir = move_dir;
7696       }
7697       else if (move_dir_preference == move_preference &&
7698                move_dir == old_move_dir)
7699       {
7700         // prefer last direction when all directions are preferred equally
7701         move_preference = move_dir_preference;
7702         new_move_dir = move_dir;
7703       }
7704     }
7705
7706     MovDir[x][y] = new_move_dir;
7707     if (old_move_dir != new_move_dir)
7708       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7709   }
7710 }
7711
7712 static void TurnRound(int x, int y)
7713 {
7714   int direction = MovDir[x][y];
7715
7716   TurnRoundExt(x, y);
7717
7718   GfxDir[x][y] = MovDir[x][y];
7719
7720   if (direction != MovDir[x][y])
7721     GfxFrame[x][y] = 0;
7722
7723   if (MovDelay[x][y])
7724     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7725
7726   ResetGfxFrame(x, y);
7727 }
7728
7729 static boolean JustBeingPushed(int x, int y)
7730 {
7731   int i;
7732
7733   for (i = 0; i < MAX_PLAYERS; i++)
7734   {
7735     struct PlayerInfo *player = &stored_player[i];
7736
7737     if (player->active && player->is_pushing && player->MovPos)
7738     {
7739       int next_jx = player->jx + (player->jx - player->last_jx);
7740       int next_jy = player->jy + (player->jy - player->last_jy);
7741
7742       if (x == next_jx && y == next_jy)
7743         return TRUE;
7744     }
7745   }
7746
7747   return FALSE;
7748 }
7749
7750 static void StartMoving(int x, int y)
7751 {
7752   boolean started_moving = FALSE;       // some elements can fall _and_ move
7753   int element = Tile[x][y];
7754
7755   if (Stop[x][y])
7756     return;
7757
7758   if (MovDelay[x][y] == 0)
7759     GfxAction[x][y] = ACTION_DEFAULT;
7760
7761   if (CAN_FALL(element) && y < lev_fieldy - 1)
7762   {
7763     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7764         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7765       if (JustBeingPushed(x, y))
7766         return;
7767
7768     if (element == EL_QUICKSAND_FULL)
7769     {
7770       if (IS_FREE(x, y + 1))
7771       {
7772         InitMovingField(x, y, MV_DOWN);
7773         started_moving = TRUE;
7774
7775         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7776 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7777         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7778           Store[x][y] = EL_ROCK;
7779 #else
7780         Store[x][y] = EL_ROCK;
7781 #endif
7782
7783         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7784       }
7785       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7786       {
7787         if (!MovDelay[x][y])
7788         {
7789           MovDelay[x][y] = TILEY + 1;
7790
7791           ResetGfxAnimation(x, y);
7792           ResetGfxAnimation(x, y + 1);
7793         }
7794
7795         if (MovDelay[x][y])
7796         {
7797           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7798           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7799
7800           MovDelay[x][y]--;
7801           if (MovDelay[x][y])
7802             return;
7803         }
7804
7805         Tile[x][y] = EL_QUICKSAND_EMPTY;
7806         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7807         Store[x][y + 1] = Store[x][y];
7808         Store[x][y] = 0;
7809
7810         PlayLevelSoundAction(x, y, ACTION_FILLING);
7811       }
7812       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7813       {
7814         if (!MovDelay[x][y])
7815         {
7816           MovDelay[x][y] = TILEY + 1;
7817
7818           ResetGfxAnimation(x, y);
7819           ResetGfxAnimation(x, y + 1);
7820         }
7821
7822         if (MovDelay[x][y])
7823         {
7824           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7825           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7826
7827           MovDelay[x][y]--;
7828           if (MovDelay[x][y])
7829             return;
7830         }
7831
7832         Tile[x][y] = EL_QUICKSAND_EMPTY;
7833         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7834         Store[x][y + 1] = Store[x][y];
7835         Store[x][y] = 0;
7836
7837         PlayLevelSoundAction(x, y, ACTION_FILLING);
7838       }
7839     }
7840     else if (element == EL_QUICKSAND_FAST_FULL)
7841     {
7842       if (IS_FREE(x, y + 1))
7843       {
7844         InitMovingField(x, y, MV_DOWN);
7845         started_moving = TRUE;
7846
7847         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7848 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7849         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7850           Store[x][y] = EL_ROCK;
7851 #else
7852         Store[x][y] = EL_ROCK;
7853 #endif
7854
7855         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7856       }
7857       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7858       {
7859         if (!MovDelay[x][y])
7860         {
7861           MovDelay[x][y] = TILEY + 1;
7862
7863           ResetGfxAnimation(x, y);
7864           ResetGfxAnimation(x, y + 1);
7865         }
7866
7867         if (MovDelay[x][y])
7868         {
7869           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7870           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7871
7872           MovDelay[x][y]--;
7873           if (MovDelay[x][y])
7874             return;
7875         }
7876
7877         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7878         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7879         Store[x][y + 1] = Store[x][y];
7880         Store[x][y] = 0;
7881
7882         PlayLevelSoundAction(x, y, ACTION_FILLING);
7883       }
7884       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7885       {
7886         if (!MovDelay[x][y])
7887         {
7888           MovDelay[x][y] = TILEY + 1;
7889
7890           ResetGfxAnimation(x, y);
7891           ResetGfxAnimation(x, y + 1);
7892         }
7893
7894         if (MovDelay[x][y])
7895         {
7896           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7897           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7898
7899           MovDelay[x][y]--;
7900           if (MovDelay[x][y])
7901             return;
7902         }
7903
7904         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7905         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7906         Store[x][y + 1] = Store[x][y];
7907         Store[x][y] = 0;
7908
7909         PlayLevelSoundAction(x, y, ACTION_FILLING);
7910       }
7911     }
7912     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7913              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7914     {
7915       InitMovingField(x, y, MV_DOWN);
7916       started_moving = TRUE;
7917
7918       Tile[x][y] = EL_QUICKSAND_FILLING;
7919       Store[x][y] = element;
7920
7921       PlayLevelSoundAction(x, y, ACTION_FILLING);
7922     }
7923     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7924              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7925     {
7926       InitMovingField(x, y, MV_DOWN);
7927       started_moving = TRUE;
7928
7929       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7930       Store[x][y] = element;
7931
7932       PlayLevelSoundAction(x, y, ACTION_FILLING);
7933     }
7934     else if (element == EL_MAGIC_WALL_FULL)
7935     {
7936       if (IS_FREE(x, y + 1))
7937       {
7938         InitMovingField(x, y, MV_DOWN);
7939         started_moving = TRUE;
7940
7941         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7942         Store[x][y] = EL_CHANGED(Store[x][y]);
7943       }
7944       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7945       {
7946         if (!MovDelay[x][y])
7947           MovDelay[x][y] = TILEY / 4 + 1;
7948
7949         if (MovDelay[x][y])
7950         {
7951           MovDelay[x][y]--;
7952           if (MovDelay[x][y])
7953             return;
7954         }
7955
7956         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7957         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7958         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7959         Store[x][y] = 0;
7960       }
7961     }
7962     else if (element == EL_BD_MAGIC_WALL_FULL)
7963     {
7964       if (IS_FREE(x, y + 1))
7965       {
7966         InitMovingField(x, y, MV_DOWN);
7967         started_moving = TRUE;
7968
7969         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7970         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7971       }
7972       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7973       {
7974         if (!MovDelay[x][y])
7975           MovDelay[x][y] = TILEY / 4 + 1;
7976
7977         if (MovDelay[x][y])
7978         {
7979           MovDelay[x][y]--;
7980           if (MovDelay[x][y])
7981             return;
7982         }
7983
7984         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7985         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7986         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7987         Store[x][y] = 0;
7988       }
7989     }
7990     else if (element == EL_DC_MAGIC_WALL_FULL)
7991     {
7992       if (IS_FREE(x, y + 1))
7993       {
7994         InitMovingField(x, y, MV_DOWN);
7995         started_moving = TRUE;
7996
7997         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7998         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7999       }
8000       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8001       {
8002         if (!MovDelay[x][y])
8003           MovDelay[x][y] = TILEY / 4 + 1;
8004
8005         if (MovDelay[x][y])
8006         {
8007           MovDelay[x][y]--;
8008           if (MovDelay[x][y])
8009             return;
8010         }
8011
8012         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8013         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8014         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8015         Store[x][y] = 0;
8016       }
8017     }
8018     else if ((CAN_PASS_MAGIC_WALL(element) &&
8019               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8020                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8021              (CAN_PASS_DC_MAGIC_WALL(element) &&
8022               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8023
8024     {
8025       InitMovingField(x, y, MV_DOWN);
8026       started_moving = TRUE;
8027
8028       Tile[x][y] =
8029         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8030          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8031          EL_DC_MAGIC_WALL_FILLING);
8032       Store[x][y] = element;
8033     }
8034     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8035     {
8036       SplashAcid(x, y + 1);
8037
8038       InitMovingField(x, y, MV_DOWN);
8039       started_moving = TRUE;
8040
8041       Store[x][y] = EL_ACID;
8042     }
8043     else if (
8044              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8045               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8046              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8047               CAN_FALL(element) && WasJustFalling[x][y] &&
8048               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8049
8050              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8051               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8052               (Tile[x][y + 1] == EL_BLOCKED)))
8053     {
8054       /* this is needed for a special case not covered by calling "Impact()"
8055          from "ContinueMoving()": if an element moves to a tile directly below
8056          another element which was just falling on that tile (which was empty
8057          in the previous frame), the falling element above would just stop
8058          instead of smashing the element below (in previous version, the above
8059          element was just checked for "moving" instead of "falling", resulting
8060          in incorrect smashes caused by horizontal movement of the above
8061          element; also, the case of the player being the element to smash was
8062          simply not covered here... :-/ ) */
8063
8064       CheckCollision[x][y] = 0;
8065       CheckImpact[x][y] = 0;
8066
8067       Impact(x, y);
8068     }
8069     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8070     {
8071       if (MovDir[x][y] == MV_NONE)
8072       {
8073         InitMovingField(x, y, MV_DOWN);
8074         started_moving = TRUE;
8075       }
8076     }
8077     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8078     {
8079       if (WasJustFalling[x][y]) // prevent animation from being restarted
8080         MovDir[x][y] = MV_DOWN;
8081
8082       InitMovingField(x, y, MV_DOWN);
8083       started_moving = TRUE;
8084     }
8085     else if (element == EL_AMOEBA_DROP)
8086     {
8087       Tile[x][y] = EL_AMOEBA_GROWING;
8088       Store[x][y] = EL_AMOEBA_WET;
8089     }
8090     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8091               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8092              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8093              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8094     {
8095       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8096                                 (IS_FREE(x - 1, y + 1) ||
8097                                  Tile[x - 1][y + 1] == EL_ACID));
8098       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8099                                 (IS_FREE(x + 1, y + 1) ||
8100                                  Tile[x + 1][y + 1] == EL_ACID));
8101       boolean can_fall_any  = (can_fall_left || can_fall_right);
8102       boolean can_fall_both = (can_fall_left && can_fall_right);
8103       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8104
8105       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8106       {
8107         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8108           can_fall_right = FALSE;
8109         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8110           can_fall_left = FALSE;
8111         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8112           can_fall_right = FALSE;
8113         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8114           can_fall_left = FALSE;
8115
8116         can_fall_any  = (can_fall_left || can_fall_right);
8117         can_fall_both = FALSE;
8118       }
8119
8120       if (can_fall_both)
8121       {
8122         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8123           can_fall_right = FALSE;       // slip down on left side
8124         else
8125           can_fall_left = !(can_fall_right = RND(2));
8126
8127         can_fall_both = FALSE;
8128       }
8129
8130       if (can_fall_any)
8131       {
8132         // if not determined otherwise, prefer left side for slipping down
8133         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8134         started_moving = TRUE;
8135       }
8136     }
8137     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8138     {
8139       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8140       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8141       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8142       int belt_dir = game.belt_dir[belt_nr];
8143
8144       if ((belt_dir == MV_LEFT  && left_is_free) ||
8145           (belt_dir == MV_RIGHT && right_is_free))
8146       {
8147         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8148
8149         InitMovingField(x, y, belt_dir);
8150         started_moving = TRUE;
8151
8152         Pushed[x][y] = TRUE;
8153         Pushed[nextx][y] = TRUE;
8154
8155         GfxAction[x][y] = ACTION_DEFAULT;
8156       }
8157       else
8158       {
8159         MovDir[x][y] = 0;       // if element was moving, stop it
8160       }
8161     }
8162   }
8163
8164   // not "else if" because of elements that can fall and move (EL_SPRING)
8165   if (CAN_MOVE(element) && !started_moving)
8166   {
8167     int move_pattern = element_info[element].move_pattern;
8168     int newx, newy;
8169
8170     Moving2Blocked(x, y, &newx, &newy);
8171
8172     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8173       return;
8174
8175     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8176         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8177     {
8178       WasJustMoving[x][y] = 0;
8179       CheckCollision[x][y] = 0;
8180
8181       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8182
8183       if (Tile[x][y] != element)        // element has changed
8184         return;
8185     }
8186
8187     if (!MovDelay[x][y])        // start new movement phase
8188     {
8189       // all objects that can change their move direction after each step
8190       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8191
8192       if (element != EL_YAMYAM &&
8193           element != EL_DARK_YAMYAM &&
8194           element != EL_PACMAN &&
8195           !(move_pattern & MV_ANY_DIRECTION) &&
8196           move_pattern != MV_TURNING_LEFT &&
8197           move_pattern != MV_TURNING_RIGHT &&
8198           move_pattern != MV_TURNING_LEFT_RIGHT &&
8199           move_pattern != MV_TURNING_RIGHT_LEFT &&
8200           move_pattern != MV_TURNING_RANDOM)
8201       {
8202         TurnRound(x, y);
8203
8204         if (MovDelay[x][y] && (element == EL_BUG ||
8205                                element == EL_SPACESHIP ||
8206                                element == EL_SP_SNIKSNAK ||
8207                                element == EL_SP_ELECTRON ||
8208                                element == EL_MOLE))
8209           TEST_DrawLevelField(x, y);
8210       }
8211     }
8212
8213     if (MovDelay[x][y])         // wait some time before next movement
8214     {
8215       MovDelay[x][y]--;
8216
8217       if (element == EL_ROBOT ||
8218           element == EL_YAMYAM ||
8219           element == EL_DARK_YAMYAM)
8220       {
8221         DrawLevelElementAnimationIfNeeded(x, y, element);
8222         PlayLevelSoundAction(x, y, ACTION_WAITING);
8223       }
8224       else if (element == EL_SP_ELECTRON)
8225         DrawLevelElementAnimationIfNeeded(x, y, element);
8226       else if (element == EL_DRAGON)
8227       {
8228         int i;
8229         int dir = MovDir[x][y];
8230         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8231         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8232         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8233                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8234                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8235                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8236         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8237
8238         GfxAction[x][y] = ACTION_ATTACKING;
8239
8240         if (IS_PLAYER(x, y))
8241           DrawPlayerField(x, y);
8242         else
8243           TEST_DrawLevelField(x, y);
8244
8245         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8246
8247         for (i = 1; i <= 3; i++)
8248         {
8249           int xx = x + i * dx;
8250           int yy = y + i * dy;
8251           int sx = SCREENX(xx);
8252           int sy = SCREENY(yy);
8253           int flame_graphic = graphic + (i - 1);
8254
8255           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8256             break;
8257
8258           if (MovDelay[x][y])
8259           {
8260             int flamed = MovingOrBlocked2Element(xx, yy);
8261
8262             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8263               Bang(xx, yy);
8264             else
8265               RemoveMovingField(xx, yy);
8266
8267             ChangeDelay[xx][yy] = 0;
8268
8269             Tile[xx][yy] = EL_FLAMES;
8270
8271             if (IN_SCR_FIELD(sx, sy))
8272             {
8273               TEST_DrawLevelFieldCrumbled(xx, yy);
8274               DrawGraphic(sx, sy, flame_graphic, frame);
8275             }
8276           }
8277           else
8278           {
8279             if (Tile[xx][yy] == EL_FLAMES)
8280               Tile[xx][yy] = EL_EMPTY;
8281             TEST_DrawLevelField(xx, yy);
8282           }
8283         }
8284       }
8285
8286       if (MovDelay[x][y])       // element still has to wait some time
8287       {
8288         PlayLevelSoundAction(x, y, ACTION_WAITING);
8289
8290         return;
8291       }
8292     }
8293
8294     // now make next step
8295
8296     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8297
8298     if (DONT_COLLIDE_WITH(element) &&
8299         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8300         !PLAYER_ENEMY_PROTECTED(newx, newy))
8301     {
8302       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8303
8304       return;
8305     }
8306
8307     else if (CAN_MOVE_INTO_ACID(element) &&
8308              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8309              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8310              (MovDir[x][y] == MV_DOWN ||
8311               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8312     {
8313       SplashAcid(newx, newy);
8314       Store[x][y] = EL_ACID;
8315     }
8316     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8317     {
8318       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8319           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8320           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8321           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8322       {
8323         RemoveField(x, y);
8324         TEST_DrawLevelField(x, y);
8325
8326         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8327         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8328           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8329
8330         game.friends_still_needed--;
8331         if (!game.friends_still_needed &&
8332             !game.GameOver &&
8333             game.all_players_gone)
8334           LevelSolved();
8335
8336         return;
8337       }
8338       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8339       {
8340         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8341           TEST_DrawLevelField(newx, newy);
8342         else
8343           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8344       }
8345       else if (!IS_FREE(newx, newy))
8346       {
8347         GfxAction[x][y] = ACTION_WAITING;
8348
8349         if (IS_PLAYER(x, y))
8350           DrawPlayerField(x, y);
8351         else
8352           TEST_DrawLevelField(x, y);
8353
8354         return;
8355       }
8356     }
8357     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8358     {
8359       if (IS_FOOD_PIG(Tile[newx][newy]))
8360       {
8361         if (IS_MOVING(newx, newy))
8362           RemoveMovingField(newx, newy);
8363         else
8364         {
8365           Tile[newx][newy] = EL_EMPTY;
8366           TEST_DrawLevelField(newx, newy);
8367         }
8368
8369         PlayLevelSound(x, y, SND_PIG_DIGGING);
8370       }
8371       else if (!IS_FREE(newx, newy))
8372       {
8373         if (IS_PLAYER(x, y))
8374           DrawPlayerField(x, y);
8375         else
8376           TEST_DrawLevelField(x, y);
8377
8378         return;
8379       }
8380     }
8381     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8382     {
8383       if (Store[x][y] != EL_EMPTY)
8384       {
8385         boolean can_clone = FALSE;
8386         int xx, yy;
8387
8388         // check if element to clone is still there
8389         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8390         {
8391           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8392           {
8393             can_clone = TRUE;
8394
8395             break;
8396           }
8397         }
8398
8399         // cannot clone or target field not free anymore -- do not clone
8400         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8401           Store[x][y] = EL_EMPTY;
8402       }
8403
8404       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8405       {
8406         if (IS_MV_DIAGONAL(MovDir[x][y]))
8407         {
8408           int diagonal_move_dir = MovDir[x][y];
8409           int stored = Store[x][y];
8410           int change_delay = 8;
8411           int graphic;
8412
8413           // android is moving diagonally
8414
8415           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8416
8417           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8418           GfxElement[x][y] = EL_EMC_ANDROID;
8419           GfxAction[x][y] = ACTION_SHRINKING;
8420           GfxDir[x][y] = diagonal_move_dir;
8421           ChangeDelay[x][y] = change_delay;
8422
8423           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8424                                    GfxDir[x][y]);
8425
8426           DrawLevelGraphicAnimation(x, y, graphic);
8427           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8428
8429           if (Tile[newx][newy] == EL_ACID)
8430           {
8431             SplashAcid(newx, newy);
8432
8433             return;
8434           }
8435
8436           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8437
8438           Store[newx][newy] = EL_EMC_ANDROID;
8439           GfxElement[newx][newy] = EL_EMC_ANDROID;
8440           GfxAction[newx][newy] = ACTION_GROWING;
8441           GfxDir[newx][newy] = diagonal_move_dir;
8442           ChangeDelay[newx][newy] = change_delay;
8443
8444           graphic = el_act_dir2img(GfxElement[newx][newy],
8445                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8446
8447           DrawLevelGraphicAnimation(newx, newy, graphic);
8448           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8449
8450           return;
8451         }
8452         else
8453         {
8454           Tile[newx][newy] = EL_EMPTY;
8455           TEST_DrawLevelField(newx, newy);
8456
8457           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8458         }
8459       }
8460       else if (!IS_FREE(newx, newy))
8461       {
8462         return;
8463       }
8464     }
8465     else if (IS_CUSTOM_ELEMENT(element) &&
8466              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8467     {
8468       if (!DigFieldByCE(newx, newy, element))
8469         return;
8470
8471       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8472       {
8473         RunnerVisit[x][y] = FrameCounter;
8474         PlayerVisit[x][y] /= 8;         // expire player visit path
8475       }
8476     }
8477     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8478     {
8479       if (!IS_FREE(newx, newy))
8480       {
8481         if (IS_PLAYER(x, y))
8482           DrawPlayerField(x, y);
8483         else
8484           TEST_DrawLevelField(x, y);
8485
8486         return;
8487       }
8488       else
8489       {
8490         boolean wanna_flame = !RND(10);
8491         int dx = newx - x, dy = newy - y;
8492         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8493         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8494         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8495                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8496         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8497                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8498
8499         if ((wanna_flame ||
8500              IS_CLASSIC_ENEMY(element1) ||
8501              IS_CLASSIC_ENEMY(element2)) &&
8502             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8503             element1 != EL_FLAMES && element2 != EL_FLAMES)
8504         {
8505           ResetGfxAnimation(x, y);
8506           GfxAction[x][y] = ACTION_ATTACKING;
8507
8508           if (IS_PLAYER(x, y))
8509             DrawPlayerField(x, y);
8510           else
8511             TEST_DrawLevelField(x, y);
8512
8513           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8514
8515           MovDelay[x][y] = 50;
8516
8517           Tile[newx][newy] = EL_FLAMES;
8518           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8519             Tile[newx1][newy1] = EL_FLAMES;
8520           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8521             Tile[newx2][newy2] = EL_FLAMES;
8522
8523           return;
8524         }
8525       }
8526     }
8527     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8528              Tile[newx][newy] == EL_DIAMOND)
8529     {
8530       if (IS_MOVING(newx, newy))
8531         RemoveMovingField(newx, newy);
8532       else
8533       {
8534         Tile[newx][newy] = EL_EMPTY;
8535         TEST_DrawLevelField(newx, newy);
8536       }
8537
8538       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8539     }
8540     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8541              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8542     {
8543       if (AmoebaNr[newx][newy])
8544       {
8545         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8546         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8547             Tile[newx][newy] == EL_BD_AMOEBA)
8548           AmoebaCnt[AmoebaNr[newx][newy]]--;
8549       }
8550
8551       if (IS_MOVING(newx, newy))
8552       {
8553         RemoveMovingField(newx, newy);
8554       }
8555       else
8556       {
8557         Tile[newx][newy] = EL_EMPTY;
8558         TEST_DrawLevelField(newx, newy);
8559       }
8560
8561       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8562     }
8563     else if ((element == EL_PACMAN || element == EL_MOLE)
8564              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8565     {
8566       if (AmoebaNr[newx][newy])
8567       {
8568         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8569         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8570             Tile[newx][newy] == EL_BD_AMOEBA)
8571           AmoebaCnt[AmoebaNr[newx][newy]]--;
8572       }
8573
8574       if (element == EL_MOLE)
8575       {
8576         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8577         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8578
8579         ResetGfxAnimation(x, y);
8580         GfxAction[x][y] = ACTION_DIGGING;
8581         TEST_DrawLevelField(x, y);
8582
8583         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8584
8585         return;                         // wait for shrinking amoeba
8586       }
8587       else      // element == EL_PACMAN
8588       {
8589         Tile[newx][newy] = EL_EMPTY;
8590         TEST_DrawLevelField(newx, newy);
8591         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8592       }
8593     }
8594     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8595              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8596               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8597     {
8598       // wait for shrinking amoeba to completely disappear
8599       return;
8600     }
8601     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8602     {
8603       // object was running against a wall
8604
8605       TurnRound(x, y);
8606
8607       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8608         DrawLevelElementAnimation(x, y, element);
8609
8610       if (DONT_TOUCH(element))
8611         TestIfBadThingTouchesPlayer(x, y);
8612
8613       return;
8614     }
8615
8616     InitMovingField(x, y, MovDir[x][y]);
8617
8618     PlayLevelSoundAction(x, y, ACTION_MOVING);
8619   }
8620
8621   if (MovDir[x][y])
8622     ContinueMoving(x, y);
8623 }
8624
8625 void ContinueMoving(int x, int y)
8626 {
8627   int element = Tile[x][y];
8628   struct ElementInfo *ei = &element_info[element];
8629   int direction = MovDir[x][y];
8630   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8631   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8632   int newx = x + dx, newy = y + dy;
8633   int stored = Store[x][y];
8634   int stored_new = Store[newx][newy];
8635   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8636   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8637   boolean last_line = (newy == lev_fieldy - 1);
8638   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8639
8640   if (pushed_by_player)         // special case: moving object pushed by player
8641   {
8642     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8643   }
8644   else if (use_step_delay)      // special case: moving object has step delay
8645   {
8646     if (!MovDelay[x][y])
8647       MovPos[x][y] += getElementMoveStepsize(x, y);
8648
8649     if (MovDelay[x][y])
8650       MovDelay[x][y]--;
8651     else
8652       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8653
8654     if (MovDelay[x][y])
8655     {
8656       TEST_DrawLevelField(x, y);
8657
8658       return;   // element is still waiting
8659     }
8660   }
8661   else                          // normal case: generically moving object
8662   {
8663     MovPos[x][y] += getElementMoveStepsize(x, y);
8664   }
8665
8666   if (ABS(MovPos[x][y]) < TILEX)
8667   {
8668     TEST_DrawLevelField(x, y);
8669
8670     return;     // element is still moving
8671   }
8672
8673   // element reached destination field
8674
8675   Tile[x][y] = EL_EMPTY;
8676   Tile[newx][newy] = element;
8677   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8678
8679   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8680   {
8681     element = Tile[newx][newy] = EL_ACID;
8682   }
8683   else if (element == EL_MOLE)
8684   {
8685     Tile[x][y] = EL_SAND;
8686
8687     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8688   }
8689   else if (element == EL_QUICKSAND_FILLING)
8690   {
8691     element = Tile[newx][newy] = get_next_element(element);
8692     Store[newx][newy] = Store[x][y];
8693   }
8694   else if (element == EL_QUICKSAND_EMPTYING)
8695   {
8696     Tile[x][y] = get_next_element(element);
8697     element = Tile[newx][newy] = Store[x][y];
8698   }
8699   else if (element == EL_QUICKSAND_FAST_FILLING)
8700   {
8701     element = Tile[newx][newy] = get_next_element(element);
8702     Store[newx][newy] = Store[x][y];
8703   }
8704   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8705   {
8706     Tile[x][y] = get_next_element(element);
8707     element = Tile[newx][newy] = Store[x][y];
8708   }
8709   else if (element == EL_MAGIC_WALL_FILLING)
8710   {
8711     element = Tile[newx][newy] = get_next_element(element);
8712     if (!game.magic_wall_active)
8713       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8714     Store[newx][newy] = Store[x][y];
8715   }
8716   else if (element == EL_MAGIC_WALL_EMPTYING)
8717   {
8718     Tile[x][y] = get_next_element(element);
8719     if (!game.magic_wall_active)
8720       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8721     element = Tile[newx][newy] = Store[x][y];
8722
8723     InitField(newx, newy, FALSE);
8724   }
8725   else if (element == EL_BD_MAGIC_WALL_FILLING)
8726   {
8727     element = Tile[newx][newy] = get_next_element(element);
8728     if (!game.magic_wall_active)
8729       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8730     Store[newx][newy] = Store[x][y];
8731   }
8732   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8733   {
8734     Tile[x][y] = get_next_element(element);
8735     if (!game.magic_wall_active)
8736       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8737     element = Tile[newx][newy] = Store[x][y];
8738
8739     InitField(newx, newy, FALSE);
8740   }
8741   else if (element == EL_DC_MAGIC_WALL_FILLING)
8742   {
8743     element = Tile[newx][newy] = get_next_element(element);
8744     if (!game.magic_wall_active)
8745       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8746     Store[newx][newy] = Store[x][y];
8747   }
8748   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8749   {
8750     Tile[x][y] = get_next_element(element);
8751     if (!game.magic_wall_active)
8752       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8753     element = Tile[newx][newy] = Store[x][y];
8754
8755     InitField(newx, newy, FALSE);
8756   }
8757   else if (element == EL_AMOEBA_DROPPING)
8758   {
8759     Tile[x][y] = get_next_element(element);
8760     element = Tile[newx][newy] = Store[x][y];
8761   }
8762   else if (element == EL_SOKOBAN_OBJECT)
8763   {
8764     if (Back[x][y])
8765       Tile[x][y] = Back[x][y];
8766
8767     if (Back[newx][newy])
8768       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8769
8770     Back[x][y] = Back[newx][newy] = 0;
8771   }
8772
8773   Store[x][y] = EL_EMPTY;
8774   MovPos[x][y] = 0;
8775   MovDir[x][y] = 0;
8776   MovDelay[x][y] = 0;
8777
8778   MovDelay[newx][newy] = 0;
8779
8780   if (CAN_CHANGE_OR_HAS_ACTION(element))
8781   {
8782     // copy element change control values to new field
8783     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8784     ChangePage[newx][newy]  = ChangePage[x][y];
8785     ChangeCount[newx][newy] = ChangeCount[x][y];
8786     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8787   }
8788
8789   CustomValue[newx][newy] = CustomValue[x][y];
8790
8791   ChangeDelay[x][y] = 0;
8792   ChangePage[x][y] = -1;
8793   ChangeCount[x][y] = 0;
8794   ChangeEvent[x][y] = -1;
8795
8796   CustomValue[x][y] = 0;
8797
8798   // copy animation control values to new field
8799   GfxFrame[newx][newy]  = GfxFrame[x][y];
8800   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8801   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8802   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8803
8804   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8805
8806   // some elements can leave other elements behind after moving
8807   if (ei->move_leave_element != EL_EMPTY &&
8808       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8809       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8810   {
8811     int move_leave_element = ei->move_leave_element;
8812
8813     // this makes it possible to leave the removed element again
8814     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8815       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8816
8817     Tile[x][y] = move_leave_element;
8818
8819     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8820       MovDir[x][y] = direction;
8821
8822     InitField(x, y, FALSE);
8823
8824     if (GFX_CRUMBLED(Tile[x][y]))
8825       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8826
8827     if (IS_PLAYER_ELEMENT(move_leave_element))
8828       RelocatePlayer(x, y, move_leave_element);
8829   }
8830
8831   // do this after checking for left-behind element
8832   ResetGfxAnimation(x, y);      // reset animation values for old field
8833
8834   if (!CAN_MOVE(element) ||
8835       (CAN_FALL(element) && direction == MV_DOWN &&
8836        (element == EL_SPRING ||
8837         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8838         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8839     GfxDir[x][y] = MovDir[newx][newy] = 0;
8840
8841   TEST_DrawLevelField(x, y);
8842   TEST_DrawLevelField(newx, newy);
8843
8844   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8845
8846   // prevent pushed element from moving on in pushed direction
8847   if (pushed_by_player && CAN_MOVE(element) &&
8848       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8849       !(element_info[element].move_pattern & direction))
8850     TurnRound(newx, newy);
8851
8852   // prevent elements on conveyor belt from moving on in last direction
8853   if (pushed_by_conveyor && CAN_FALL(element) &&
8854       direction & MV_HORIZONTAL)
8855     MovDir[newx][newy] = 0;
8856
8857   if (!pushed_by_player)
8858   {
8859     int nextx = newx + dx, nexty = newy + dy;
8860     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8861
8862     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8863
8864     if (CAN_FALL(element) && direction == MV_DOWN)
8865       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8866
8867     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8868       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8869
8870     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8871       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8872   }
8873
8874   if (DONT_TOUCH(element))      // object may be nasty to player or others
8875   {
8876     TestIfBadThingTouchesPlayer(newx, newy);
8877     TestIfBadThingTouchesFriend(newx, newy);
8878
8879     if (!IS_CUSTOM_ELEMENT(element))
8880       TestIfBadThingTouchesOtherBadThing(newx, newy);
8881   }
8882   else if (element == EL_PENGUIN)
8883     TestIfFriendTouchesBadThing(newx, newy);
8884
8885   if (DONT_GET_HIT_BY(element))
8886   {
8887     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8888   }
8889
8890   // give the player one last chance (one more frame) to move away
8891   if (CAN_FALL(element) && direction == MV_DOWN &&
8892       (last_line || (!IS_FREE(x, newy + 1) &&
8893                      (!IS_PLAYER(x, newy + 1) ||
8894                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8895     Impact(x, newy);
8896
8897   if (pushed_by_player && !game.use_change_when_pushing_bug)
8898   {
8899     int push_side = MV_DIR_OPPOSITE(direction);
8900     struct PlayerInfo *player = PLAYERINFO(x, y);
8901
8902     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8903                                player->index_bit, push_side);
8904     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8905                                         player->index_bit, push_side);
8906   }
8907
8908   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8909     MovDelay[newx][newy] = 1;
8910
8911   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8912
8913   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8914   TestIfElementHitsCustomElement(newx, newy, direction);
8915   TestIfPlayerTouchesCustomElement(newx, newy);
8916   TestIfElementTouchesCustomElement(newx, newy);
8917
8918   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8919       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8920     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8921                              MV_DIR_OPPOSITE(direction));
8922 }
8923
8924 int AmoebaNeighbourNr(int ax, int ay)
8925 {
8926   int i;
8927   int element = Tile[ax][ay];
8928   int group_nr = 0;
8929   static int xy[4][2] =
8930   {
8931     { 0, -1 },
8932     { -1, 0 },
8933     { +1, 0 },
8934     { 0, +1 }
8935   };
8936
8937   for (i = 0; i < NUM_DIRECTIONS; i++)
8938   {
8939     int x = ax + xy[i][0];
8940     int y = ay + xy[i][1];
8941
8942     if (!IN_LEV_FIELD(x, y))
8943       continue;
8944
8945     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8946       group_nr = AmoebaNr[x][y];
8947   }
8948
8949   return group_nr;
8950 }
8951
8952 static void AmoebaMerge(int ax, int ay)
8953 {
8954   int i, x, y, xx, yy;
8955   int new_group_nr = AmoebaNr[ax][ay];
8956   static int xy[4][2] =
8957   {
8958     { 0, -1 },
8959     { -1, 0 },
8960     { +1, 0 },
8961     { 0, +1 }
8962   };
8963
8964   if (new_group_nr == 0)
8965     return;
8966
8967   for (i = 0; i < NUM_DIRECTIONS; i++)
8968   {
8969     x = ax + xy[i][0];
8970     y = ay + xy[i][1];
8971
8972     if (!IN_LEV_FIELD(x, y))
8973       continue;
8974
8975     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8976          Tile[x][y] == EL_BD_AMOEBA ||
8977          Tile[x][y] == EL_AMOEBA_DEAD) &&
8978         AmoebaNr[x][y] != new_group_nr)
8979     {
8980       int old_group_nr = AmoebaNr[x][y];
8981
8982       if (old_group_nr == 0)
8983         return;
8984
8985       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8986       AmoebaCnt[old_group_nr] = 0;
8987       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8988       AmoebaCnt2[old_group_nr] = 0;
8989
8990       SCAN_PLAYFIELD(xx, yy)
8991       {
8992         if (AmoebaNr[xx][yy] == old_group_nr)
8993           AmoebaNr[xx][yy] = new_group_nr;
8994       }
8995     }
8996   }
8997 }
8998
8999 void AmoebaToDiamond(int ax, int ay)
9000 {
9001   int i, x, y;
9002
9003   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9004   {
9005     int group_nr = AmoebaNr[ax][ay];
9006
9007 #ifdef DEBUG
9008     if (group_nr == 0)
9009     {
9010       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9011       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9012
9013       return;
9014     }
9015 #endif
9016
9017     SCAN_PLAYFIELD(x, y)
9018     {
9019       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9020       {
9021         AmoebaNr[x][y] = 0;
9022         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9023       }
9024     }
9025
9026     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9027                             SND_AMOEBA_TURNING_TO_GEM :
9028                             SND_AMOEBA_TURNING_TO_ROCK));
9029     Bang(ax, ay);
9030   }
9031   else
9032   {
9033     static int xy[4][2] =
9034     {
9035       { 0, -1 },
9036       { -1, 0 },
9037       { +1, 0 },
9038       { 0, +1 }
9039     };
9040
9041     for (i = 0; i < NUM_DIRECTIONS; i++)
9042     {
9043       x = ax + xy[i][0];
9044       y = ay + xy[i][1];
9045
9046       if (!IN_LEV_FIELD(x, y))
9047         continue;
9048
9049       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9050       {
9051         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9052                               SND_AMOEBA_TURNING_TO_GEM :
9053                               SND_AMOEBA_TURNING_TO_ROCK));
9054         Bang(x, y);
9055       }
9056     }
9057   }
9058 }
9059
9060 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9061 {
9062   int x, y;
9063   int group_nr = AmoebaNr[ax][ay];
9064   boolean done = FALSE;
9065
9066 #ifdef DEBUG
9067   if (group_nr == 0)
9068   {
9069     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9070     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9071
9072     return;
9073   }
9074 #endif
9075
9076   SCAN_PLAYFIELD(x, y)
9077   {
9078     if (AmoebaNr[x][y] == group_nr &&
9079         (Tile[x][y] == EL_AMOEBA_DEAD ||
9080          Tile[x][y] == EL_BD_AMOEBA ||
9081          Tile[x][y] == EL_AMOEBA_GROWING))
9082     {
9083       AmoebaNr[x][y] = 0;
9084       Tile[x][y] = new_element;
9085       InitField(x, y, FALSE);
9086       TEST_DrawLevelField(x, y);
9087       done = TRUE;
9088     }
9089   }
9090
9091   if (done)
9092     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9093                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9094                             SND_BD_AMOEBA_TURNING_TO_GEM));
9095 }
9096
9097 static void AmoebaGrowing(int x, int y)
9098 {
9099   static unsigned int sound_delay = 0;
9100   static unsigned int sound_delay_value = 0;
9101
9102   if (!MovDelay[x][y])          // start new growing cycle
9103   {
9104     MovDelay[x][y] = 7;
9105
9106     if (DelayReached(&sound_delay, sound_delay_value))
9107     {
9108       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9109       sound_delay_value = 30;
9110     }
9111   }
9112
9113   if (MovDelay[x][y])           // wait some time before growing bigger
9114   {
9115     MovDelay[x][y]--;
9116     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9117     {
9118       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9119                                            6 - MovDelay[x][y]);
9120
9121       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9122     }
9123
9124     if (!MovDelay[x][y])
9125     {
9126       Tile[x][y] = Store[x][y];
9127       Store[x][y] = 0;
9128       TEST_DrawLevelField(x, y);
9129     }
9130   }
9131 }
9132
9133 static void AmoebaShrinking(int x, int y)
9134 {
9135   static unsigned int sound_delay = 0;
9136   static unsigned int sound_delay_value = 0;
9137
9138   if (!MovDelay[x][y])          // start new shrinking cycle
9139   {
9140     MovDelay[x][y] = 7;
9141
9142     if (DelayReached(&sound_delay, sound_delay_value))
9143       sound_delay_value = 30;
9144   }
9145
9146   if (MovDelay[x][y])           // wait some time before shrinking
9147   {
9148     MovDelay[x][y]--;
9149     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9150     {
9151       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9152                                            6 - MovDelay[x][y]);
9153
9154       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9155     }
9156
9157     if (!MovDelay[x][y])
9158     {
9159       Tile[x][y] = EL_EMPTY;
9160       TEST_DrawLevelField(x, y);
9161
9162       // don't let mole enter this field in this cycle;
9163       // (give priority to objects falling to this field from above)
9164       Stop[x][y] = TRUE;
9165     }
9166   }
9167 }
9168
9169 static void AmoebaReproduce(int ax, int ay)
9170 {
9171   int i;
9172   int element = Tile[ax][ay];
9173   int graphic = el2img(element);
9174   int newax = ax, neway = ay;
9175   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9176   static int xy[4][2] =
9177   {
9178     { 0, -1 },
9179     { -1, 0 },
9180     { +1, 0 },
9181     { 0, +1 }
9182   };
9183
9184   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9185   {
9186     Tile[ax][ay] = EL_AMOEBA_DEAD;
9187     TEST_DrawLevelField(ax, ay);
9188     return;
9189   }
9190
9191   if (IS_ANIMATED(graphic))
9192     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9193
9194   if (!MovDelay[ax][ay])        // start making new amoeba field
9195     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9196
9197   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9198   {
9199     MovDelay[ax][ay]--;
9200     if (MovDelay[ax][ay])
9201       return;
9202   }
9203
9204   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9205   {
9206     int start = RND(4);
9207     int x = ax + xy[start][0];
9208     int y = ay + xy[start][1];
9209
9210     if (!IN_LEV_FIELD(x, y))
9211       return;
9212
9213     if (IS_FREE(x, y) ||
9214         CAN_GROW_INTO(Tile[x][y]) ||
9215         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9216         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9217     {
9218       newax = x;
9219       neway = y;
9220     }
9221
9222     if (newax == ax && neway == ay)
9223       return;
9224   }
9225   else                          // normal or "filled" (BD style) amoeba
9226   {
9227     int start = RND(4);
9228     boolean waiting_for_player = FALSE;
9229
9230     for (i = 0; i < NUM_DIRECTIONS; i++)
9231     {
9232       int j = (start + i) % 4;
9233       int x = ax + xy[j][0];
9234       int y = ay + xy[j][1];
9235
9236       if (!IN_LEV_FIELD(x, y))
9237         continue;
9238
9239       if (IS_FREE(x, y) ||
9240           CAN_GROW_INTO(Tile[x][y]) ||
9241           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9242           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9243       {
9244         newax = x;
9245         neway = y;
9246         break;
9247       }
9248       else if (IS_PLAYER(x, y))
9249         waiting_for_player = TRUE;
9250     }
9251
9252     if (newax == ax && neway == ay)             // amoeba cannot grow
9253     {
9254       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9255       {
9256         Tile[ax][ay] = EL_AMOEBA_DEAD;
9257         TEST_DrawLevelField(ax, ay);
9258         AmoebaCnt[AmoebaNr[ax][ay]]--;
9259
9260         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9261         {
9262           if (element == EL_AMOEBA_FULL)
9263             AmoebaToDiamond(ax, ay);
9264           else if (element == EL_BD_AMOEBA)
9265             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9266         }
9267       }
9268       return;
9269     }
9270     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9271     {
9272       // amoeba gets larger by growing in some direction
9273
9274       int new_group_nr = AmoebaNr[ax][ay];
9275
9276 #ifdef DEBUG
9277   if (new_group_nr == 0)
9278   {
9279     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9280           newax, neway);
9281     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9282
9283     return;
9284   }
9285 #endif
9286
9287       AmoebaNr[newax][neway] = new_group_nr;
9288       AmoebaCnt[new_group_nr]++;
9289       AmoebaCnt2[new_group_nr]++;
9290
9291       // if amoeba touches other amoeba(s) after growing, unify them
9292       AmoebaMerge(newax, neway);
9293
9294       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9295       {
9296         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9297         return;
9298       }
9299     }
9300   }
9301
9302   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9303       (neway == lev_fieldy - 1 && newax != ax))
9304   {
9305     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9306     Store[newax][neway] = element;
9307   }
9308   else if (neway == ay || element == EL_EMC_DRIPPER)
9309   {
9310     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9311
9312     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9313   }
9314   else
9315   {
9316     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9317     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9318     Store[ax][ay] = EL_AMOEBA_DROP;
9319     ContinueMoving(ax, ay);
9320     return;
9321   }
9322
9323   TEST_DrawLevelField(newax, neway);
9324 }
9325
9326 static void Life(int ax, int ay)
9327 {
9328   int x1, y1, x2, y2;
9329   int life_time = 40;
9330   int element = Tile[ax][ay];
9331   int graphic = el2img(element);
9332   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9333                          level.biomaze);
9334   boolean changed = FALSE;
9335
9336   if (IS_ANIMATED(graphic))
9337     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9338
9339   if (Stop[ax][ay])
9340     return;
9341
9342   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9343     MovDelay[ax][ay] = life_time;
9344
9345   if (MovDelay[ax][ay])         // wait some time before next cycle
9346   {
9347     MovDelay[ax][ay]--;
9348     if (MovDelay[ax][ay])
9349       return;
9350   }
9351
9352   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9353   {
9354     int xx = ax+x1, yy = ay+y1;
9355     int old_element = Tile[xx][yy];
9356     int num_neighbours = 0;
9357
9358     if (!IN_LEV_FIELD(xx, yy))
9359       continue;
9360
9361     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9362     {
9363       int x = xx+x2, y = yy+y2;
9364
9365       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9366         continue;
9367
9368       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9369       boolean is_neighbour = FALSE;
9370
9371       if (level.use_life_bugs)
9372         is_neighbour =
9373           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9374            (IS_FREE(x, y)                             &&  Stop[x][y]));
9375       else
9376         is_neighbour =
9377           (Last[x][y] == element || is_player_cell);
9378
9379       if (is_neighbour)
9380         num_neighbours++;
9381     }
9382
9383     boolean is_free = FALSE;
9384
9385     if (level.use_life_bugs)
9386       is_free = (IS_FREE(xx, yy));
9387     else
9388       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9389
9390     if (xx == ax && yy == ay)           // field in the middle
9391     {
9392       if (num_neighbours < life_parameter[0] ||
9393           num_neighbours > life_parameter[1])
9394       {
9395         Tile[xx][yy] = EL_EMPTY;
9396         if (Tile[xx][yy] != old_element)
9397           TEST_DrawLevelField(xx, yy);
9398         Stop[xx][yy] = TRUE;
9399         changed = TRUE;
9400       }
9401     }
9402     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9403     {                                   // free border field
9404       if (num_neighbours >= life_parameter[2] &&
9405           num_neighbours <= life_parameter[3])
9406       {
9407         Tile[xx][yy] = element;
9408         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9409         if (Tile[xx][yy] != old_element)
9410           TEST_DrawLevelField(xx, yy);
9411         Stop[xx][yy] = TRUE;
9412         changed = TRUE;
9413       }
9414     }
9415   }
9416
9417   if (changed)
9418     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9419                    SND_GAME_OF_LIFE_GROWING);
9420 }
9421
9422 static void InitRobotWheel(int x, int y)
9423 {
9424   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9425 }
9426
9427 static void RunRobotWheel(int x, int y)
9428 {
9429   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9430 }
9431
9432 static void StopRobotWheel(int x, int y)
9433 {
9434   if (game.robot_wheel_x == x &&
9435       game.robot_wheel_y == y)
9436   {
9437     game.robot_wheel_x = -1;
9438     game.robot_wheel_y = -1;
9439     game.robot_wheel_active = FALSE;
9440   }
9441 }
9442
9443 static void InitTimegateWheel(int x, int y)
9444 {
9445   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9446 }
9447
9448 static void RunTimegateWheel(int x, int y)
9449 {
9450   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9451 }
9452
9453 static void InitMagicBallDelay(int x, int y)
9454 {
9455   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9456 }
9457
9458 static void ActivateMagicBall(int bx, int by)
9459 {
9460   int x, y;
9461
9462   if (level.ball_random)
9463   {
9464     int pos_border = RND(8);    // select one of the eight border elements
9465     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9466     int xx = pos_content % 3;
9467     int yy = pos_content / 3;
9468
9469     x = bx - 1 + xx;
9470     y = by - 1 + yy;
9471
9472     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9473       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9474   }
9475   else
9476   {
9477     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9478     {
9479       int xx = x - bx + 1;
9480       int yy = y - by + 1;
9481
9482       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9483         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9484     }
9485   }
9486
9487   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9488 }
9489
9490 static void CheckExit(int x, int y)
9491 {
9492   if (game.gems_still_needed > 0 ||
9493       game.sokoban_fields_still_needed > 0 ||
9494       game.sokoban_objects_still_needed > 0 ||
9495       game.lights_still_needed > 0)
9496   {
9497     int element = Tile[x][y];
9498     int graphic = el2img(element);
9499
9500     if (IS_ANIMATED(graphic))
9501       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9502
9503     return;
9504   }
9505
9506   // do not re-open exit door closed after last player
9507   if (game.all_players_gone)
9508     return;
9509
9510   Tile[x][y] = EL_EXIT_OPENING;
9511
9512   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9513 }
9514
9515 static void CheckExitEM(int x, int y)
9516 {
9517   if (game.gems_still_needed > 0 ||
9518       game.sokoban_fields_still_needed > 0 ||
9519       game.sokoban_objects_still_needed > 0 ||
9520       game.lights_still_needed > 0)
9521   {
9522     int element = Tile[x][y];
9523     int graphic = el2img(element);
9524
9525     if (IS_ANIMATED(graphic))
9526       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9527
9528     return;
9529   }
9530
9531   // do not re-open exit door closed after last player
9532   if (game.all_players_gone)
9533     return;
9534
9535   Tile[x][y] = EL_EM_EXIT_OPENING;
9536
9537   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9538 }
9539
9540 static void CheckExitSteel(int x, int y)
9541 {
9542   if (game.gems_still_needed > 0 ||
9543       game.sokoban_fields_still_needed > 0 ||
9544       game.sokoban_objects_still_needed > 0 ||
9545       game.lights_still_needed > 0)
9546   {
9547     int element = Tile[x][y];
9548     int graphic = el2img(element);
9549
9550     if (IS_ANIMATED(graphic))
9551       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9552
9553     return;
9554   }
9555
9556   // do not re-open exit door closed after last player
9557   if (game.all_players_gone)
9558     return;
9559
9560   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9561
9562   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9563 }
9564
9565 static void CheckExitSteelEM(int x, int y)
9566 {
9567   if (game.gems_still_needed > 0 ||
9568       game.sokoban_fields_still_needed > 0 ||
9569       game.sokoban_objects_still_needed > 0 ||
9570       game.lights_still_needed > 0)
9571   {
9572     int element = Tile[x][y];
9573     int graphic = el2img(element);
9574
9575     if (IS_ANIMATED(graphic))
9576       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9577
9578     return;
9579   }
9580
9581   // do not re-open exit door closed after last player
9582   if (game.all_players_gone)
9583     return;
9584
9585   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9586
9587   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9588 }
9589
9590 static void CheckExitSP(int x, int y)
9591 {
9592   if (game.gems_still_needed > 0)
9593   {
9594     int element = Tile[x][y];
9595     int graphic = el2img(element);
9596
9597     if (IS_ANIMATED(graphic))
9598       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9599
9600     return;
9601   }
9602
9603   // do not re-open exit door closed after last player
9604   if (game.all_players_gone)
9605     return;
9606
9607   Tile[x][y] = EL_SP_EXIT_OPENING;
9608
9609   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9610 }
9611
9612 static void CloseAllOpenTimegates(void)
9613 {
9614   int x, y;
9615
9616   SCAN_PLAYFIELD(x, y)
9617   {
9618     int element = Tile[x][y];
9619
9620     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9621     {
9622       Tile[x][y] = EL_TIMEGATE_CLOSING;
9623
9624       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9625     }
9626   }
9627 }
9628
9629 static void DrawTwinkleOnField(int x, int y)
9630 {
9631   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9632     return;
9633
9634   if (Tile[x][y] == EL_BD_DIAMOND)
9635     return;
9636
9637   if (MovDelay[x][y] == 0)      // next animation frame
9638     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9639
9640   if (MovDelay[x][y] != 0)      // wait some time before next frame
9641   {
9642     MovDelay[x][y]--;
9643
9644     DrawLevelElementAnimation(x, y, Tile[x][y]);
9645
9646     if (MovDelay[x][y] != 0)
9647     {
9648       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9649                                            10 - MovDelay[x][y]);
9650
9651       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9652     }
9653   }
9654 }
9655
9656 static void MauerWaechst(int x, int y)
9657 {
9658   int delay = 6;
9659
9660   if (!MovDelay[x][y])          // next animation frame
9661     MovDelay[x][y] = 3 * delay;
9662
9663   if (MovDelay[x][y])           // wait some time before next frame
9664   {
9665     MovDelay[x][y]--;
9666
9667     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9668     {
9669       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9670       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9671
9672       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9673     }
9674
9675     if (!MovDelay[x][y])
9676     {
9677       if (MovDir[x][y] == MV_LEFT)
9678       {
9679         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9680           TEST_DrawLevelField(x - 1, y);
9681       }
9682       else if (MovDir[x][y] == MV_RIGHT)
9683       {
9684         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9685           TEST_DrawLevelField(x + 1, y);
9686       }
9687       else if (MovDir[x][y] == MV_UP)
9688       {
9689         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9690           TEST_DrawLevelField(x, y - 1);
9691       }
9692       else
9693       {
9694         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9695           TEST_DrawLevelField(x, y + 1);
9696       }
9697
9698       Tile[x][y] = Store[x][y];
9699       Store[x][y] = 0;
9700       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9701       TEST_DrawLevelField(x, y);
9702     }
9703   }
9704 }
9705
9706 static void MauerAbleger(int ax, int ay)
9707 {
9708   int element = Tile[ax][ay];
9709   int graphic = el2img(element);
9710   boolean oben_frei = FALSE, unten_frei = FALSE;
9711   boolean links_frei = FALSE, rechts_frei = FALSE;
9712   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9713   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9714   boolean new_wall = FALSE;
9715
9716   if (IS_ANIMATED(graphic))
9717     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9718
9719   if (!MovDelay[ax][ay])        // start building new wall
9720     MovDelay[ax][ay] = 6;
9721
9722   if (MovDelay[ax][ay])         // wait some time before building new wall
9723   {
9724     MovDelay[ax][ay]--;
9725     if (MovDelay[ax][ay])
9726       return;
9727   }
9728
9729   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9730     oben_frei = TRUE;
9731   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9732     unten_frei = TRUE;
9733   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9734     links_frei = TRUE;
9735   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9736     rechts_frei = TRUE;
9737
9738   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9739       element == EL_EXPANDABLE_WALL_ANY)
9740   {
9741     if (oben_frei)
9742     {
9743       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9744       Store[ax][ay-1] = element;
9745       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9746       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9747         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9748                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9749       new_wall = TRUE;
9750     }
9751     if (unten_frei)
9752     {
9753       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9754       Store[ax][ay+1] = element;
9755       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9756       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9757         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9758                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9759       new_wall = TRUE;
9760     }
9761   }
9762
9763   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9764       element == EL_EXPANDABLE_WALL_ANY ||
9765       element == EL_EXPANDABLE_WALL ||
9766       element == EL_BD_EXPANDABLE_WALL)
9767   {
9768     if (links_frei)
9769     {
9770       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9771       Store[ax-1][ay] = element;
9772       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9773       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9774         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9775                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9776       new_wall = TRUE;
9777     }
9778
9779     if (rechts_frei)
9780     {
9781       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9782       Store[ax+1][ay] = element;
9783       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9784       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9785         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9786                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9787       new_wall = TRUE;
9788     }
9789   }
9790
9791   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9792     TEST_DrawLevelField(ax, ay);
9793
9794   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9795     oben_massiv = TRUE;
9796   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9797     unten_massiv = TRUE;
9798   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9799     links_massiv = TRUE;
9800   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9801     rechts_massiv = TRUE;
9802
9803   if (((oben_massiv && unten_massiv) ||
9804        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9805        element == EL_EXPANDABLE_WALL) &&
9806       ((links_massiv && rechts_massiv) ||
9807        element == EL_EXPANDABLE_WALL_VERTICAL))
9808     Tile[ax][ay] = EL_WALL;
9809
9810   if (new_wall)
9811     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9812 }
9813
9814 static void MauerAblegerStahl(int ax, int ay)
9815 {
9816   int element = Tile[ax][ay];
9817   int graphic = el2img(element);
9818   boolean oben_frei = FALSE, unten_frei = FALSE;
9819   boolean links_frei = FALSE, rechts_frei = FALSE;
9820   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9821   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9822   boolean new_wall = FALSE;
9823
9824   if (IS_ANIMATED(graphic))
9825     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9826
9827   if (!MovDelay[ax][ay])        // start building new wall
9828     MovDelay[ax][ay] = 6;
9829
9830   if (MovDelay[ax][ay])         // wait some time before building new wall
9831   {
9832     MovDelay[ax][ay]--;
9833     if (MovDelay[ax][ay])
9834       return;
9835   }
9836
9837   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9838     oben_frei = TRUE;
9839   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9840     unten_frei = TRUE;
9841   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9842     links_frei = TRUE;
9843   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9844     rechts_frei = TRUE;
9845
9846   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9847       element == EL_EXPANDABLE_STEELWALL_ANY)
9848   {
9849     if (oben_frei)
9850     {
9851       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9852       Store[ax][ay-1] = element;
9853       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9854       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9855         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9856                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9857       new_wall = TRUE;
9858     }
9859     if (unten_frei)
9860     {
9861       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9862       Store[ax][ay+1] = element;
9863       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9864       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9865         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9866                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9867       new_wall = TRUE;
9868     }
9869   }
9870
9871   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9872       element == EL_EXPANDABLE_STEELWALL_ANY)
9873   {
9874     if (links_frei)
9875     {
9876       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9877       Store[ax-1][ay] = element;
9878       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9879       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9880         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9881                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9882       new_wall = TRUE;
9883     }
9884
9885     if (rechts_frei)
9886     {
9887       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9888       Store[ax+1][ay] = element;
9889       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9890       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9891         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9892                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9893       new_wall = TRUE;
9894     }
9895   }
9896
9897   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9898     oben_massiv = TRUE;
9899   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9900     unten_massiv = TRUE;
9901   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9902     links_massiv = TRUE;
9903   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9904     rechts_massiv = TRUE;
9905
9906   if (((oben_massiv && unten_massiv) ||
9907        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9908       ((links_massiv && rechts_massiv) ||
9909        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9910     Tile[ax][ay] = EL_STEELWALL;
9911
9912   if (new_wall)
9913     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9914 }
9915
9916 static void CheckForDragon(int x, int y)
9917 {
9918   int i, j;
9919   boolean dragon_found = FALSE;
9920   static int xy[4][2] =
9921   {
9922     { 0, -1 },
9923     { -1, 0 },
9924     { +1, 0 },
9925     { 0, +1 }
9926   };
9927
9928   for (i = 0; i < NUM_DIRECTIONS; i++)
9929   {
9930     for (j = 0; j < 4; j++)
9931     {
9932       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9933
9934       if (IN_LEV_FIELD(xx, yy) &&
9935           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9936       {
9937         if (Tile[xx][yy] == EL_DRAGON)
9938           dragon_found = TRUE;
9939       }
9940       else
9941         break;
9942     }
9943   }
9944
9945   if (!dragon_found)
9946   {
9947     for (i = 0; i < NUM_DIRECTIONS; i++)
9948     {
9949       for (j = 0; j < 3; j++)
9950       {
9951         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9952   
9953         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9954         {
9955           Tile[xx][yy] = EL_EMPTY;
9956           TEST_DrawLevelField(xx, yy);
9957         }
9958         else
9959           break;
9960       }
9961     }
9962   }
9963 }
9964
9965 static void InitBuggyBase(int x, int y)
9966 {
9967   int element = Tile[x][y];
9968   int activating_delay = FRAMES_PER_SECOND / 4;
9969
9970   ChangeDelay[x][y] =
9971     (element == EL_SP_BUGGY_BASE ?
9972      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9973      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9974      activating_delay :
9975      element == EL_SP_BUGGY_BASE_ACTIVE ?
9976      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9977 }
9978
9979 static void WarnBuggyBase(int x, int y)
9980 {
9981   int i;
9982   static int xy[4][2] =
9983   {
9984     { 0, -1 },
9985     { -1, 0 },
9986     { +1, 0 },
9987     { 0, +1 }
9988   };
9989
9990   for (i = 0; i < NUM_DIRECTIONS; i++)
9991   {
9992     int xx = x + xy[i][0];
9993     int yy = y + xy[i][1];
9994
9995     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9996     {
9997       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9998
9999       break;
10000     }
10001   }
10002 }
10003
10004 static void InitTrap(int x, int y)
10005 {
10006   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10007 }
10008
10009 static void ActivateTrap(int x, int y)
10010 {
10011   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10012 }
10013
10014 static void ChangeActiveTrap(int x, int y)
10015 {
10016   int graphic = IMG_TRAP_ACTIVE;
10017
10018   // if new animation frame was drawn, correct crumbled sand border
10019   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10020     TEST_DrawLevelFieldCrumbled(x, y);
10021 }
10022
10023 static int getSpecialActionElement(int element, int number, int base_element)
10024 {
10025   return (element != EL_EMPTY ? element :
10026           number != -1 ? base_element + number - 1 :
10027           EL_EMPTY);
10028 }
10029
10030 static int getModifiedActionNumber(int value_old, int operator, int operand,
10031                                    int value_min, int value_max)
10032 {
10033   int value_new = (operator == CA_MODE_SET      ? operand :
10034                    operator == CA_MODE_ADD      ? value_old + operand :
10035                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10036                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10037                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10038                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10039                    value_old);
10040
10041   return (value_new < value_min ? value_min :
10042           value_new > value_max ? value_max :
10043           value_new);
10044 }
10045
10046 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10047 {
10048   struct ElementInfo *ei = &element_info[element];
10049   struct ElementChangeInfo *change = &ei->change_page[page];
10050   int target_element = change->target_element;
10051   int action_type = change->action_type;
10052   int action_mode = change->action_mode;
10053   int action_arg = change->action_arg;
10054   int action_element = change->action_element;
10055   int i;
10056
10057   if (!change->has_action)
10058     return;
10059
10060   // ---------- determine action paramater values -----------------------------
10061
10062   int level_time_value =
10063     (level.time > 0 ? TimeLeft :
10064      TimePlayed);
10065
10066   int action_arg_element_raw =
10067     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10068      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10069      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10070      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10071      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10072      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10073      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10074      EL_EMPTY);
10075   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10076
10077   int action_arg_direction =
10078     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10079      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10080      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10081      change->actual_trigger_side :
10082      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10083      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10084      MV_NONE);
10085
10086   int action_arg_number_min =
10087     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10088      CA_ARG_MIN);
10089
10090   int action_arg_number_max =
10091     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10092      action_type == CA_SET_LEVEL_GEMS ? 999 :
10093      action_type == CA_SET_LEVEL_TIME ? 9999 :
10094      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10095      action_type == CA_SET_CE_VALUE ? 9999 :
10096      action_type == CA_SET_CE_SCORE ? 9999 :
10097      CA_ARG_MAX);
10098
10099   int action_arg_number_reset =
10100     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10101      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10102      action_type == CA_SET_LEVEL_TIME ? level.time :
10103      action_type == CA_SET_LEVEL_SCORE ? 0 :
10104      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10105      action_type == CA_SET_CE_SCORE ? 0 :
10106      0);
10107
10108   int action_arg_number =
10109     (action_arg <= CA_ARG_MAX ? action_arg :
10110      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10111      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10112      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10113      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10114      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10115      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10116      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10117      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10118      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10119      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10120      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10121      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10122      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10123      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10124      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10125      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10126      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10127      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10128      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10129      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10130      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10131      -1);
10132
10133   int action_arg_number_old =
10134     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10135      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10136      action_type == CA_SET_LEVEL_SCORE ? game.score :
10137      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10138      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10139      0);
10140
10141   int action_arg_number_new =
10142     getModifiedActionNumber(action_arg_number_old,
10143                             action_mode, action_arg_number,
10144                             action_arg_number_min, action_arg_number_max);
10145
10146   int trigger_player_bits =
10147     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10148      change->actual_trigger_player_bits : change->trigger_player);
10149
10150   int action_arg_player_bits =
10151     (action_arg >= CA_ARG_PLAYER_1 &&
10152      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10153      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10154      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10155      PLAYER_BITS_ANY);
10156
10157   // ---------- execute action  -----------------------------------------------
10158
10159   switch (action_type)
10160   {
10161     case CA_NO_ACTION:
10162     {
10163       return;
10164     }
10165
10166     // ---------- level actions  ----------------------------------------------
10167
10168     case CA_RESTART_LEVEL:
10169     {
10170       game.restart_level = TRUE;
10171
10172       break;
10173     }
10174
10175     case CA_SHOW_ENVELOPE:
10176     {
10177       int element = getSpecialActionElement(action_arg_element,
10178                                             action_arg_number, EL_ENVELOPE_1);
10179
10180       if (IS_ENVELOPE(element))
10181         local_player->show_envelope = element;
10182
10183       break;
10184     }
10185
10186     case CA_SET_LEVEL_TIME:
10187     {
10188       if (level.time > 0)       // only modify limited time value
10189       {
10190         TimeLeft = action_arg_number_new;
10191
10192         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10193
10194         DisplayGameControlValues();
10195
10196         if (!TimeLeft && setup.time_limit)
10197           for (i = 0; i < MAX_PLAYERS; i++)
10198             KillPlayer(&stored_player[i]);
10199       }
10200
10201       break;
10202     }
10203
10204     case CA_SET_LEVEL_SCORE:
10205     {
10206       game.score = action_arg_number_new;
10207
10208       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10209
10210       DisplayGameControlValues();
10211
10212       break;
10213     }
10214
10215     case CA_SET_LEVEL_GEMS:
10216     {
10217       game.gems_still_needed = action_arg_number_new;
10218
10219       game.snapshot.collected_item = TRUE;
10220
10221       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10222
10223       DisplayGameControlValues();
10224
10225       break;
10226     }
10227
10228     case CA_SET_LEVEL_WIND:
10229     {
10230       game.wind_direction = action_arg_direction;
10231
10232       break;
10233     }
10234
10235     case CA_SET_LEVEL_RANDOM_SEED:
10236     {
10237       // ensure that setting a new random seed while playing is predictable
10238       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10239
10240       break;
10241     }
10242
10243     // ---------- player actions  ---------------------------------------------
10244
10245     case CA_MOVE_PLAYER:
10246     case CA_MOVE_PLAYER_NEW:
10247     {
10248       // automatically move to the next field in specified direction
10249       for (i = 0; i < MAX_PLAYERS; i++)
10250         if (trigger_player_bits & (1 << i))
10251           if (action_type == CA_MOVE_PLAYER ||
10252               stored_player[i].MovPos == 0)
10253             stored_player[i].programmed_action = action_arg_direction;
10254
10255       break;
10256     }
10257
10258     case CA_EXIT_PLAYER:
10259     {
10260       for (i = 0; i < MAX_PLAYERS; i++)
10261         if (action_arg_player_bits & (1 << i))
10262           ExitPlayer(&stored_player[i]);
10263
10264       if (game.players_still_needed == 0)
10265         LevelSolved();
10266
10267       break;
10268     }
10269
10270     case CA_KILL_PLAYER:
10271     {
10272       for (i = 0; i < MAX_PLAYERS; i++)
10273         if (action_arg_player_bits & (1 << i))
10274           KillPlayer(&stored_player[i]);
10275
10276       break;
10277     }
10278
10279     case CA_SET_PLAYER_KEYS:
10280     {
10281       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10282       int element = getSpecialActionElement(action_arg_element,
10283                                             action_arg_number, EL_KEY_1);
10284
10285       if (IS_KEY(element))
10286       {
10287         for (i = 0; i < MAX_PLAYERS; i++)
10288         {
10289           if (trigger_player_bits & (1 << i))
10290           {
10291             stored_player[i].key[KEY_NR(element)] = key_state;
10292
10293             DrawGameDoorValues();
10294           }
10295         }
10296       }
10297
10298       break;
10299     }
10300
10301     case CA_SET_PLAYER_SPEED:
10302     {
10303       for (i = 0; i < MAX_PLAYERS; i++)
10304       {
10305         if (trigger_player_bits & (1 << i))
10306         {
10307           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10308
10309           if (action_arg == CA_ARG_SPEED_FASTER &&
10310               stored_player[i].cannot_move)
10311           {
10312             action_arg_number = STEPSIZE_VERY_SLOW;
10313           }
10314           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10315                    action_arg == CA_ARG_SPEED_FASTER)
10316           {
10317             action_arg_number = 2;
10318             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10319                            CA_MODE_MULTIPLY);
10320           }
10321           else if (action_arg == CA_ARG_NUMBER_RESET)
10322           {
10323             action_arg_number = level.initial_player_stepsize[i];
10324           }
10325
10326           move_stepsize =
10327             getModifiedActionNumber(move_stepsize,
10328                                     action_mode,
10329                                     action_arg_number,
10330                                     action_arg_number_min,
10331                                     action_arg_number_max);
10332
10333           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10334         }
10335       }
10336
10337       break;
10338     }
10339
10340     case CA_SET_PLAYER_SHIELD:
10341     {
10342       for (i = 0; i < MAX_PLAYERS; i++)
10343       {
10344         if (trigger_player_bits & (1 << i))
10345         {
10346           if (action_arg == CA_ARG_SHIELD_OFF)
10347           {
10348             stored_player[i].shield_normal_time_left = 0;
10349             stored_player[i].shield_deadly_time_left = 0;
10350           }
10351           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10352           {
10353             stored_player[i].shield_normal_time_left = 999999;
10354           }
10355           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10356           {
10357             stored_player[i].shield_normal_time_left = 999999;
10358             stored_player[i].shield_deadly_time_left = 999999;
10359           }
10360         }
10361       }
10362
10363       break;
10364     }
10365
10366     case CA_SET_PLAYER_GRAVITY:
10367     {
10368       for (i = 0; i < MAX_PLAYERS; i++)
10369       {
10370         if (trigger_player_bits & (1 << i))
10371         {
10372           stored_player[i].gravity =
10373             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10374              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10375              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10376              stored_player[i].gravity);
10377         }
10378       }
10379
10380       break;
10381     }
10382
10383     case CA_SET_PLAYER_ARTWORK:
10384     {
10385       for (i = 0; i < MAX_PLAYERS; i++)
10386       {
10387         if (trigger_player_bits & (1 << i))
10388         {
10389           int artwork_element = action_arg_element;
10390
10391           if (action_arg == CA_ARG_ELEMENT_RESET)
10392             artwork_element =
10393               (level.use_artwork_element[i] ? level.artwork_element[i] :
10394                stored_player[i].element_nr);
10395
10396           if (stored_player[i].artwork_element != artwork_element)
10397             stored_player[i].Frame = 0;
10398
10399           stored_player[i].artwork_element = artwork_element;
10400
10401           SetPlayerWaiting(&stored_player[i], FALSE);
10402
10403           // set number of special actions for bored and sleeping animation
10404           stored_player[i].num_special_action_bored =
10405             get_num_special_action(artwork_element,
10406                                    ACTION_BORING_1, ACTION_BORING_LAST);
10407           stored_player[i].num_special_action_sleeping =
10408             get_num_special_action(artwork_element,
10409                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10410         }
10411       }
10412
10413       break;
10414     }
10415
10416     case CA_SET_PLAYER_INVENTORY:
10417     {
10418       for (i = 0; i < MAX_PLAYERS; i++)
10419       {
10420         struct PlayerInfo *player = &stored_player[i];
10421         int j, k;
10422
10423         if (trigger_player_bits & (1 << i))
10424         {
10425           int inventory_element = action_arg_element;
10426
10427           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10428               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10429               action_arg == CA_ARG_ELEMENT_ACTION)
10430           {
10431             int element = inventory_element;
10432             int collect_count = element_info[element].collect_count_initial;
10433
10434             if (!IS_CUSTOM_ELEMENT(element))
10435               collect_count = 1;
10436
10437             if (collect_count == 0)
10438               player->inventory_infinite_element = element;
10439             else
10440               for (k = 0; k < collect_count; k++)
10441                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10442                   player->inventory_element[player->inventory_size++] =
10443                     element;
10444           }
10445           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10446                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10447                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10448           {
10449             if (player->inventory_infinite_element != EL_UNDEFINED &&
10450                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10451                                      action_arg_element_raw))
10452               player->inventory_infinite_element = EL_UNDEFINED;
10453
10454             for (k = 0, j = 0; j < player->inventory_size; j++)
10455             {
10456               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10457                                         action_arg_element_raw))
10458                 player->inventory_element[k++] = player->inventory_element[j];
10459             }
10460
10461             player->inventory_size = k;
10462           }
10463           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10464           {
10465             if (player->inventory_size > 0)
10466             {
10467               for (j = 0; j < player->inventory_size - 1; j++)
10468                 player->inventory_element[j] = player->inventory_element[j + 1];
10469
10470               player->inventory_size--;
10471             }
10472           }
10473           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10474           {
10475             if (player->inventory_size > 0)
10476               player->inventory_size--;
10477           }
10478           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10479           {
10480             player->inventory_infinite_element = EL_UNDEFINED;
10481             player->inventory_size = 0;
10482           }
10483           else if (action_arg == CA_ARG_INVENTORY_RESET)
10484           {
10485             player->inventory_infinite_element = EL_UNDEFINED;
10486             player->inventory_size = 0;
10487
10488             if (level.use_initial_inventory[i])
10489             {
10490               for (j = 0; j < level.initial_inventory_size[i]; j++)
10491               {
10492                 int element = level.initial_inventory_content[i][j];
10493                 int collect_count = element_info[element].collect_count_initial;
10494
10495                 if (!IS_CUSTOM_ELEMENT(element))
10496                   collect_count = 1;
10497
10498                 if (collect_count == 0)
10499                   player->inventory_infinite_element = element;
10500                 else
10501                   for (k = 0; k < collect_count; k++)
10502                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10503                       player->inventory_element[player->inventory_size++] =
10504                         element;
10505               }
10506             }
10507           }
10508         }
10509       }
10510
10511       break;
10512     }
10513
10514     // ---------- CE actions  -------------------------------------------------
10515
10516     case CA_SET_CE_VALUE:
10517     {
10518       int last_ce_value = CustomValue[x][y];
10519
10520       CustomValue[x][y] = action_arg_number_new;
10521
10522       if (CustomValue[x][y] != last_ce_value)
10523       {
10524         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10525         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10526
10527         if (CustomValue[x][y] == 0)
10528         {
10529           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10530           ChangeCount[x][y] = 0;        // allow at least one more change
10531
10532           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10533           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10534         }
10535       }
10536
10537       break;
10538     }
10539
10540     case CA_SET_CE_SCORE:
10541     {
10542       int last_ce_score = ei->collect_score;
10543
10544       ei->collect_score = action_arg_number_new;
10545
10546       if (ei->collect_score != last_ce_score)
10547       {
10548         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10549         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10550
10551         if (ei->collect_score == 0)
10552         {
10553           int xx, yy;
10554
10555           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10556           ChangeCount[x][y] = 0;        // allow at least one more change
10557
10558           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10559           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10560
10561           /*
10562             This is a very special case that seems to be a mixture between
10563             CheckElementChange() and CheckTriggeredElementChange(): while
10564             the first one only affects single elements that are triggered
10565             directly, the second one affects multiple elements in the playfield
10566             that are triggered indirectly by another element. This is a third
10567             case: Changing the CE score always affects multiple identical CEs,
10568             so every affected CE must be checked, not only the single CE for
10569             which the CE score was changed in the first place (as every instance
10570             of that CE shares the same CE score, and therefore also can change)!
10571           */
10572           SCAN_PLAYFIELD(xx, yy)
10573           {
10574             if (Tile[xx][yy] == element)
10575               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10576                                  CE_SCORE_GETS_ZERO);
10577           }
10578         }
10579       }
10580
10581       break;
10582     }
10583
10584     case CA_SET_CE_ARTWORK:
10585     {
10586       int artwork_element = action_arg_element;
10587       boolean reset_frame = FALSE;
10588       int xx, yy;
10589
10590       if (action_arg == CA_ARG_ELEMENT_RESET)
10591         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10592                            element);
10593
10594       if (ei->gfx_element != artwork_element)
10595         reset_frame = TRUE;
10596
10597       ei->gfx_element = artwork_element;
10598
10599       SCAN_PLAYFIELD(xx, yy)
10600       {
10601         if (Tile[xx][yy] == element)
10602         {
10603           if (reset_frame)
10604           {
10605             ResetGfxAnimation(xx, yy);
10606             ResetRandomAnimationValue(xx, yy);
10607           }
10608
10609           TEST_DrawLevelField(xx, yy);
10610         }
10611       }
10612
10613       break;
10614     }
10615
10616     // ---------- engine actions  ---------------------------------------------
10617
10618     case CA_SET_ENGINE_SCAN_MODE:
10619     {
10620       InitPlayfieldScanMode(action_arg);
10621
10622       break;
10623     }
10624
10625     default:
10626       break;
10627   }
10628 }
10629
10630 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10631 {
10632   int old_element = Tile[x][y];
10633   int new_element = GetElementFromGroupElement(element);
10634   int previous_move_direction = MovDir[x][y];
10635   int last_ce_value = CustomValue[x][y];
10636   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10637   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10638   boolean add_player_onto_element = (new_element_is_player &&
10639                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10640                                      IS_WALKABLE(old_element));
10641
10642   if (!add_player_onto_element)
10643   {
10644     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10645       RemoveMovingField(x, y);
10646     else
10647       RemoveField(x, y);
10648
10649     Tile[x][y] = new_element;
10650
10651     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10652       MovDir[x][y] = previous_move_direction;
10653
10654     if (element_info[new_element].use_last_ce_value)
10655       CustomValue[x][y] = last_ce_value;
10656
10657     InitField_WithBug1(x, y, FALSE);
10658
10659     new_element = Tile[x][y];   // element may have changed
10660
10661     ResetGfxAnimation(x, y);
10662     ResetRandomAnimationValue(x, y);
10663
10664     TEST_DrawLevelField(x, y);
10665
10666     if (GFX_CRUMBLED(new_element))
10667       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10668   }
10669
10670   // check if element under the player changes from accessible to unaccessible
10671   // (needed for special case of dropping element which then changes)
10672   // (must be checked after creating new element for walkable group elements)
10673   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10674       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10675   {
10676     Bang(x, y);
10677
10678     return;
10679   }
10680
10681   // "ChangeCount" not set yet to allow "entered by player" change one time
10682   if (new_element_is_player)
10683     RelocatePlayer(x, y, new_element);
10684
10685   if (is_change)
10686     ChangeCount[x][y]++;        // count number of changes in the same frame
10687
10688   TestIfBadThingTouchesPlayer(x, y);
10689   TestIfPlayerTouchesCustomElement(x, y);
10690   TestIfElementTouchesCustomElement(x, y);
10691 }
10692
10693 static void CreateField(int x, int y, int element)
10694 {
10695   CreateFieldExt(x, y, element, FALSE);
10696 }
10697
10698 static void CreateElementFromChange(int x, int y, int element)
10699 {
10700   element = GET_VALID_RUNTIME_ELEMENT(element);
10701
10702   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10703   {
10704     int old_element = Tile[x][y];
10705
10706     // prevent changed element from moving in same engine frame
10707     // unless both old and new element can either fall or move
10708     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10709         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10710       Stop[x][y] = TRUE;
10711   }
10712
10713   CreateFieldExt(x, y, element, TRUE);
10714 }
10715
10716 static boolean ChangeElement(int x, int y, int element, int page)
10717 {
10718   struct ElementInfo *ei = &element_info[element];
10719   struct ElementChangeInfo *change = &ei->change_page[page];
10720   int ce_value = CustomValue[x][y];
10721   int ce_score = ei->collect_score;
10722   int target_element;
10723   int old_element = Tile[x][y];
10724
10725   // always use default change event to prevent running into a loop
10726   if (ChangeEvent[x][y] == -1)
10727     ChangeEvent[x][y] = CE_DELAY;
10728
10729   if (ChangeEvent[x][y] == CE_DELAY)
10730   {
10731     // reset actual trigger element, trigger player and action element
10732     change->actual_trigger_element = EL_EMPTY;
10733     change->actual_trigger_player = EL_EMPTY;
10734     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10735     change->actual_trigger_side = CH_SIDE_NONE;
10736     change->actual_trigger_ce_value = 0;
10737     change->actual_trigger_ce_score = 0;
10738   }
10739
10740   // do not change elements more than a specified maximum number of changes
10741   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10742     return FALSE;
10743
10744   ChangeCount[x][y]++;          // count number of changes in the same frame
10745
10746   if (change->explode)
10747   {
10748     Bang(x, y);
10749
10750     return TRUE;
10751   }
10752
10753   if (change->use_target_content)
10754   {
10755     boolean complete_replace = TRUE;
10756     boolean can_replace[3][3];
10757     int xx, yy;
10758
10759     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10760     {
10761       boolean is_empty;
10762       boolean is_walkable;
10763       boolean is_diggable;
10764       boolean is_collectible;
10765       boolean is_removable;
10766       boolean is_destructible;
10767       int ex = x + xx - 1;
10768       int ey = y + yy - 1;
10769       int content_element = change->target_content.e[xx][yy];
10770       int e;
10771
10772       can_replace[xx][yy] = TRUE;
10773
10774       if (ex == x && ey == y)   // do not check changing element itself
10775         continue;
10776
10777       if (content_element == EL_EMPTY_SPACE)
10778       {
10779         can_replace[xx][yy] = FALSE;    // do not replace border with space
10780
10781         continue;
10782       }
10783
10784       if (!IN_LEV_FIELD(ex, ey))
10785       {
10786         can_replace[xx][yy] = FALSE;
10787         complete_replace = FALSE;
10788
10789         continue;
10790       }
10791
10792       e = Tile[ex][ey];
10793
10794       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10795         e = MovingOrBlocked2Element(ex, ey);
10796
10797       is_empty = (IS_FREE(ex, ey) ||
10798                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10799
10800       is_walkable     = (is_empty || IS_WALKABLE(e));
10801       is_diggable     = (is_empty || IS_DIGGABLE(e));
10802       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10803       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10804       is_removable    = (is_diggable || is_collectible);
10805
10806       can_replace[xx][yy] =
10807         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10808           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10809           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10810           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10811           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10812           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10813          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10814
10815       if (!can_replace[xx][yy])
10816         complete_replace = FALSE;
10817     }
10818
10819     if (!change->only_if_complete || complete_replace)
10820     {
10821       boolean something_has_changed = FALSE;
10822
10823       if (change->only_if_complete && change->use_random_replace &&
10824           RND(100) < change->random_percentage)
10825         return FALSE;
10826
10827       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10828       {
10829         int ex = x + xx - 1;
10830         int ey = y + yy - 1;
10831         int content_element;
10832
10833         if (can_replace[xx][yy] && (!change->use_random_replace ||
10834                                     RND(100) < change->random_percentage))
10835         {
10836           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10837             RemoveMovingField(ex, ey);
10838
10839           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10840
10841           content_element = change->target_content.e[xx][yy];
10842           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10843                                               ce_value, ce_score);
10844
10845           CreateElementFromChange(ex, ey, target_element);
10846
10847           something_has_changed = TRUE;
10848
10849           // for symmetry reasons, freeze newly created border elements
10850           if (ex != x || ey != y)
10851             Stop[ex][ey] = TRUE;        // no more moving in this frame
10852         }
10853       }
10854
10855       if (something_has_changed)
10856       {
10857         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10858         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10859       }
10860     }
10861   }
10862   else
10863   {
10864     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10865                                         ce_value, ce_score);
10866
10867     if (element == EL_DIAGONAL_GROWING ||
10868         element == EL_DIAGONAL_SHRINKING)
10869     {
10870       target_element = Store[x][y];
10871
10872       Store[x][y] = EL_EMPTY;
10873     }
10874
10875     // special case: element changes to player (and may be kept if walkable)
10876     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10877       CreateElementFromChange(x, y, EL_EMPTY);
10878
10879     CreateElementFromChange(x, y, target_element);
10880
10881     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10882     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10883   }
10884
10885   // this uses direct change before indirect change
10886   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10887
10888   return TRUE;
10889 }
10890
10891 static void HandleElementChange(int x, int y, int page)
10892 {
10893   int element = MovingOrBlocked2Element(x, y);
10894   struct ElementInfo *ei = &element_info[element];
10895   struct ElementChangeInfo *change = &ei->change_page[page];
10896   boolean handle_action_before_change = FALSE;
10897
10898 #ifdef DEBUG
10899   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10900       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10901   {
10902     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10903           x, y, element, element_info[element].token_name);
10904     Debug("game:playing:HandleElementChange", "This should never happen!");
10905   }
10906 #endif
10907
10908   // this can happen with classic bombs on walkable, changing elements
10909   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10910   {
10911     return;
10912   }
10913
10914   if (ChangeDelay[x][y] == 0)           // initialize element change
10915   {
10916     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10917
10918     if (change->can_change)
10919     {
10920       // !!! not clear why graphic animation should be reset at all here !!!
10921       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10922       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10923
10924       /*
10925         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10926
10927         When using an animation frame delay of 1 (this only happens with
10928         "sp_zonk.moving.left/right" in the classic graphics), the default
10929         (non-moving) animation shows wrong animation frames (while the
10930         moving animation, like "sp_zonk.moving.left/right", is correct,
10931         so this graphical bug never shows up with the classic graphics).
10932         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10933         be drawn instead of the correct frames 0,1,2,3. This is caused by
10934         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10935         an element change: First when the change delay ("ChangeDelay[][]")
10936         counter has reached zero after decrementing, then a second time in
10937         the next frame (after "GfxFrame[][]" was already incremented) when
10938         "ChangeDelay[][]" is reset to the initial delay value again.
10939
10940         This causes frame 0 to be drawn twice, while the last frame won't
10941         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10942
10943         As some animations may already be cleverly designed around this bug
10944         (at least the "Snake Bite" snake tail animation does this), it cannot
10945         simply be fixed here without breaking such existing animations.
10946         Unfortunately, it cannot easily be detected if a graphics set was
10947         designed "before" or "after" the bug was fixed. As a workaround,
10948         a new graphics set option "game.graphics_engine_version" was added
10949         to be able to specify the game's major release version for which the
10950         graphics set was designed, which can then be used to decide if the
10951         bugfix should be used (version 4 and above) or not (version 3 or
10952         below, or if no version was specified at all, as with old sets).
10953
10954         (The wrong/fixed animation frames can be tested with the test level set
10955         "test_gfxframe" and level "000", which contains a specially prepared
10956         custom element at level position (x/y) == (11/9) which uses the zonk
10957         animation mentioned above. Using "game.graphics_engine_version: 4"
10958         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10959         This can also be seen from the debug output for this test element.)
10960       */
10961
10962       // when a custom element is about to change (for example by change delay),
10963       // do not reset graphic animation when the custom element is moving
10964       if (game.graphics_engine_version < 4 &&
10965           !IS_MOVING(x, y))
10966       {
10967         ResetGfxAnimation(x, y);
10968         ResetRandomAnimationValue(x, y);
10969       }
10970
10971       if (change->pre_change_function)
10972         change->pre_change_function(x, y);
10973     }
10974   }
10975
10976   ChangeDelay[x][y]--;
10977
10978   if (ChangeDelay[x][y] != 0)           // continue element change
10979   {
10980     if (change->can_change)
10981     {
10982       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10983
10984       if (IS_ANIMATED(graphic))
10985         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10986
10987       if (change->change_function)
10988         change->change_function(x, y);
10989     }
10990   }
10991   else                                  // finish element change
10992   {
10993     if (ChangePage[x][y] != -1)         // remember page from delayed change
10994     {
10995       page = ChangePage[x][y];
10996       ChangePage[x][y] = -1;
10997
10998       change = &ei->change_page[page];
10999     }
11000
11001     if (IS_MOVING(x, y))                // never change a running system ;-)
11002     {
11003       ChangeDelay[x][y] = 1;            // try change after next move step
11004       ChangePage[x][y] = page;          // remember page to use for change
11005
11006       return;
11007     }
11008
11009     // special case: set new level random seed before changing element
11010     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11011       handle_action_before_change = TRUE;
11012
11013     if (change->has_action && handle_action_before_change)
11014       ExecuteCustomElementAction(x, y, element, page);
11015
11016     if (change->can_change)
11017     {
11018       if (ChangeElement(x, y, element, page))
11019       {
11020         if (change->post_change_function)
11021           change->post_change_function(x, y);
11022       }
11023     }
11024
11025     if (change->has_action && !handle_action_before_change)
11026       ExecuteCustomElementAction(x, y, element, page);
11027   }
11028 }
11029
11030 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11031                                               int trigger_element,
11032                                               int trigger_event,
11033                                               int trigger_player,
11034                                               int trigger_side,
11035                                               int trigger_page)
11036 {
11037   boolean change_done_any = FALSE;
11038   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11039   int i;
11040
11041   if (!(trigger_events[trigger_element][trigger_event]))
11042     return FALSE;
11043
11044   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11045
11046   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11047   {
11048     int element = EL_CUSTOM_START + i;
11049     boolean change_done = FALSE;
11050     int p;
11051
11052     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11053         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11054       continue;
11055
11056     for (p = 0; p < element_info[element].num_change_pages; p++)
11057     {
11058       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11059
11060       if (change->can_change_or_has_action &&
11061           change->has_event[trigger_event] &&
11062           change->trigger_side & trigger_side &&
11063           change->trigger_player & trigger_player &&
11064           change->trigger_page & trigger_page_bits &&
11065           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11066       {
11067         change->actual_trigger_element = trigger_element;
11068         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11069         change->actual_trigger_player_bits = trigger_player;
11070         change->actual_trigger_side = trigger_side;
11071         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11072         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11073
11074         if ((change->can_change && !change_done) || change->has_action)
11075         {
11076           int x, y;
11077
11078           SCAN_PLAYFIELD(x, y)
11079           {
11080             if (Tile[x][y] == element)
11081             {
11082               if (change->can_change && !change_done)
11083               {
11084                 // if element already changed in this frame, not only prevent
11085                 // another element change (checked in ChangeElement()), but
11086                 // also prevent additional element actions for this element
11087
11088                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11089                     !level.use_action_after_change_bug)
11090                   continue;
11091
11092                 ChangeDelay[x][y] = 1;
11093                 ChangeEvent[x][y] = trigger_event;
11094
11095                 HandleElementChange(x, y, p);
11096               }
11097               else if (change->has_action)
11098               {
11099                 // if element already changed in this frame, not only prevent
11100                 // another element change (checked in ChangeElement()), but
11101                 // also prevent additional element actions for this element
11102
11103                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11104                     !level.use_action_after_change_bug)
11105                   continue;
11106
11107                 ExecuteCustomElementAction(x, y, element, p);
11108                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11109               }
11110             }
11111           }
11112
11113           if (change->can_change)
11114           {
11115             change_done = TRUE;
11116             change_done_any = TRUE;
11117           }
11118         }
11119       }
11120     }
11121   }
11122
11123   RECURSION_LOOP_DETECTION_END();
11124
11125   return change_done_any;
11126 }
11127
11128 static boolean CheckElementChangeExt(int x, int y,
11129                                      int element,
11130                                      int trigger_element,
11131                                      int trigger_event,
11132                                      int trigger_player,
11133                                      int trigger_side)
11134 {
11135   boolean change_done = FALSE;
11136   int p;
11137
11138   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11139       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11140     return FALSE;
11141
11142   if (Tile[x][y] == EL_BLOCKED)
11143   {
11144     Blocked2Moving(x, y, &x, &y);
11145     element = Tile[x][y];
11146   }
11147
11148   // check if element has already changed or is about to change after moving
11149   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11150        Tile[x][y] != element) ||
11151
11152       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11153        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11154         ChangePage[x][y] != -1)))
11155     return FALSE;
11156
11157   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11158
11159   for (p = 0; p < element_info[element].num_change_pages; p++)
11160   {
11161     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11162
11163     /* check trigger element for all events where the element that is checked
11164        for changing interacts with a directly adjacent element -- this is
11165        different to element changes that affect other elements to change on the
11166        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11167     boolean check_trigger_element =
11168       (trigger_event == CE_NEXT_TO_X ||
11169        trigger_event == CE_TOUCHING_X ||
11170        trigger_event == CE_HITTING_X ||
11171        trigger_event == CE_HIT_BY_X ||
11172        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11173
11174     if (change->can_change_or_has_action &&
11175         change->has_event[trigger_event] &&
11176         change->trigger_side & trigger_side &&
11177         change->trigger_player & trigger_player &&
11178         (!check_trigger_element ||
11179          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11180     {
11181       change->actual_trigger_element = trigger_element;
11182       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11183       change->actual_trigger_player_bits = trigger_player;
11184       change->actual_trigger_side = trigger_side;
11185       change->actual_trigger_ce_value = CustomValue[x][y];
11186       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11187
11188       // special case: trigger element not at (x,y) position for some events
11189       if (check_trigger_element)
11190       {
11191         static struct
11192         {
11193           int dx, dy;
11194         } move_xy[] =
11195           {
11196             {  0,  0 },
11197             { -1,  0 },
11198             { +1,  0 },
11199             {  0,  0 },
11200             {  0, -1 },
11201             {  0,  0 }, { 0, 0 }, { 0, 0 },
11202             {  0, +1 }
11203           };
11204
11205         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11206         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11207
11208         change->actual_trigger_ce_value = CustomValue[xx][yy];
11209         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11210       }
11211
11212       if (change->can_change && !change_done)
11213       {
11214         ChangeDelay[x][y] = 1;
11215         ChangeEvent[x][y] = trigger_event;
11216
11217         HandleElementChange(x, y, p);
11218
11219         change_done = TRUE;
11220       }
11221       else if (change->has_action)
11222       {
11223         ExecuteCustomElementAction(x, y, element, p);
11224         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11225       }
11226     }
11227   }
11228
11229   RECURSION_LOOP_DETECTION_END();
11230
11231   return change_done;
11232 }
11233
11234 static void PlayPlayerSound(struct PlayerInfo *player)
11235 {
11236   int jx = player->jx, jy = player->jy;
11237   int sound_element = player->artwork_element;
11238   int last_action = player->last_action_waiting;
11239   int action = player->action_waiting;
11240
11241   if (player->is_waiting)
11242   {
11243     if (action != last_action)
11244       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11245     else
11246       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11247   }
11248   else
11249   {
11250     if (action != last_action)
11251       StopSound(element_info[sound_element].sound[last_action]);
11252
11253     if (last_action == ACTION_SLEEPING)
11254       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11255   }
11256 }
11257
11258 static void PlayAllPlayersSound(void)
11259 {
11260   int i;
11261
11262   for (i = 0; i < MAX_PLAYERS; i++)
11263     if (stored_player[i].active)
11264       PlayPlayerSound(&stored_player[i]);
11265 }
11266
11267 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11268 {
11269   boolean last_waiting = player->is_waiting;
11270   int move_dir = player->MovDir;
11271
11272   player->dir_waiting = move_dir;
11273   player->last_action_waiting = player->action_waiting;
11274
11275   if (is_waiting)
11276   {
11277     if (!last_waiting)          // not waiting -> waiting
11278     {
11279       player->is_waiting = TRUE;
11280
11281       player->frame_counter_bored =
11282         FrameCounter +
11283         game.player_boring_delay_fixed +
11284         GetSimpleRandom(game.player_boring_delay_random);
11285       player->frame_counter_sleeping =
11286         FrameCounter +
11287         game.player_sleeping_delay_fixed +
11288         GetSimpleRandom(game.player_sleeping_delay_random);
11289
11290       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11291     }
11292
11293     if (game.player_sleeping_delay_fixed +
11294         game.player_sleeping_delay_random > 0 &&
11295         player->anim_delay_counter == 0 &&
11296         player->post_delay_counter == 0 &&
11297         FrameCounter >= player->frame_counter_sleeping)
11298       player->is_sleeping = TRUE;
11299     else if (game.player_boring_delay_fixed +
11300              game.player_boring_delay_random > 0 &&
11301              FrameCounter >= player->frame_counter_bored)
11302       player->is_bored = TRUE;
11303
11304     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11305                               player->is_bored ? ACTION_BORING :
11306                               ACTION_WAITING);
11307
11308     if (player->is_sleeping && player->use_murphy)
11309     {
11310       // special case for sleeping Murphy when leaning against non-free tile
11311
11312       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11313           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11314            !IS_MOVING(player->jx - 1, player->jy)))
11315         move_dir = MV_LEFT;
11316       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11317                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11318                 !IS_MOVING(player->jx + 1, player->jy)))
11319         move_dir = MV_RIGHT;
11320       else
11321         player->is_sleeping = FALSE;
11322
11323       player->dir_waiting = move_dir;
11324     }
11325
11326     if (player->is_sleeping)
11327     {
11328       if (player->num_special_action_sleeping > 0)
11329       {
11330         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11331         {
11332           int last_special_action = player->special_action_sleeping;
11333           int num_special_action = player->num_special_action_sleeping;
11334           int special_action =
11335             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11336              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11337              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11338              last_special_action + 1 : ACTION_SLEEPING);
11339           int special_graphic =
11340             el_act_dir2img(player->artwork_element, special_action, move_dir);
11341
11342           player->anim_delay_counter =
11343             graphic_info[special_graphic].anim_delay_fixed +
11344             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11345           player->post_delay_counter =
11346             graphic_info[special_graphic].post_delay_fixed +
11347             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11348
11349           player->special_action_sleeping = special_action;
11350         }
11351
11352         if (player->anim_delay_counter > 0)
11353         {
11354           player->action_waiting = player->special_action_sleeping;
11355           player->anim_delay_counter--;
11356         }
11357         else if (player->post_delay_counter > 0)
11358         {
11359           player->post_delay_counter--;
11360         }
11361       }
11362     }
11363     else if (player->is_bored)
11364     {
11365       if (player->num_special_action_bored > 0)
11366       {
11367         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11368         {
11369           int special_action =
11370             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11371           int special_graphic =
11372             el_act_dir2img(player->artwork_element, special_action, move_dir);
11373
11374           player->anim_delay_counter =
11375             graphic_info[special_graphic].anim_delay_fixed +
11376             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11377           player->post_delay_counter =
11378             graphic_info[special_graphic].post_delay_fixed +
11379             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11380
11381           player->special_action_bored = special_action;
11382         }
11383
11384         if (player->anim_delay_counter > 0)
11385         {
11386           player->action_waiting = player->special_action_bored;
11387           player->anim_delay_counter--;
11388         }
11389         else if (player->post_delay_counter > 0)
11390         {
11391           player->post_delay_counter--;
11392         }
11393       }
11394     }
11395   }
11396   else if (last_waiting)        // waiting -> not waiting
11397   {
11398     player->is_waiting = FALSE;
11399     player->is_bored = FALSE;
11400     player->is_sleeping = FALSE;
11401
11402     player->frame_counter_bored = -1;
11403     player->frame_counter_sleeping = -1;
11404
11405     player->anim_delay_counter = 0;
11406     player->post_delay_counter = 0;
11407
11408     player->dir_waiting = player->MovDir;
11409     player->action_waiting = ACTION_DEFAULT;
11410
11411     player->special_action_bored = ACTION_DEFAULT;
11412     player->special_action_sleeping = ACTION_DEFAULT;
11413   }
11414 }
11415
11416 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11417 {
11418   if ((!player->is_moving  && player->was_moving) ||
11419       (player->MovPos == 0 && player->was_moving) ||
11420       (player->is_snapping && !player->was_snapping) ||
11421       (player->is_dropping && !player->was_dropping))
11422   {
11423     if (!CheckSaveEngineSnapshotToList())
11424       return;
11425
11426     player->was_moving = FALSE;
11427     player->was_snapping = TRUE;
11428     player->was_dropping = TRUE;
11429   }
11430   else
11431   {
11432     if (player->is_moving)
11433       player->was_moving = TRUE;
11434
11435     if (!player->is_snapping)
11436       player->was_snapping = FALSE;
11437
11438     if (!player->is_dropping)
11439       player->was_dropping = FALSE;
11440   }
11441
11442   static struct MouseActionInfo mouse_action_last = { 0 };
11443   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11444   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11445
11446   if (new_released)
11447     CheckSaveEngineSnapshotToList();
11448
11449   mouse_action_last = mouse_action;
11450 }
11451
11452 static void CheckSingleStepMode(struct PlayerInfo *player)
11453 {
11454   if (tape.single_step && tape.recording && !tape.pausing)
11455   {
11456     // as it is called "single step mode", just return to pause mode when the
11457     // player stopped moving after one tile (or never starts moving at all)
11458     // (reverse logic needed here in case single step mode used in team mode)
11459     if (player->is_moving ||
11460         player->is_pushing ||
11461         player->is_dropping_pressed ||
11462         player->effective_mouse_action.button)
11463       game.enter_single_step_mode = FALSE;
11464   }
11465
11466   CheckSaveEngineSnapshot(player);
11467 }
11468
11469 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11470 {
11471   int left      = player_action & JOY_LEFT;
11472   int right     = player_action & JOY_RIGHT;
11473   int up        = player_action & JOY_UP;
11474   int down      = player_action & JOY_DOWN;
11475   int button1   = player_action & JOY_BUTTON_1;
11476   int button2   = player_action & JOY_BUTTON_2;
11477   int dx        = (left ? -1 : right ? 1 : 0);
11478   int dy        = (up   ? -1 : down  ? 1 : 0);
11479
11480   if (!player->active || tape.pausing)
11481     return 0;
11482
11483   if (player_action)
11484   {
11485     if (button1)
11486       SnapField(player, dx, dy);
11487     else
11488     {
11489       if (button2)
11490         DropElement(player);
11491
11492       MovePlayer(player, dx, dy);
11493     }
11494
11495     CheckSingleStepMode(player);
11496
11497     SetPlayerWaiting(player, FALSE);
11498
11499     return player_action;
11500   }
11501   else
11502   {
11503     // no actions for this player (no input at player's configured device)
11504
11505     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11506     SnapField(player, 0, 0);
11507     CheckGravityMovementWhenNotMoving(player);
11508
11509     if (player->MovPos == 0)
11510       SetPlayerWaiting(player, TRUE);
11511
11512     if (player->MovPos == 0)    // needed for tape.playing
11513       player->is_moving = FALSE;
11514
11515     player->is_dropping = FALSE;
11516     player->is_dropping_pressed = FALSE;
11517     player->drop_pressed_delay = 0;
11518
11519     CheckSingleStepMode(player);
11520
11521     return 0;
11522   }
11523 }
11524
11525 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11526                                          byte *tape_action)
11527 {
11528   if (!tape.use_mouse_actions)
11529     return;
11530
11531   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11532   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11533   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11534 }
11535
11536 static void SetTapeActionFromMouseAction(byte *tape_action,
11537                                          struct MouseActionInfo *mouse_action)
11538 {
11539   if (!tape.use_mouse_actions)
11540     return;
11541
11542   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11543   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11544   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11545 }
11546
11547 static void CheckLevelSolved(void)
11548 {
11549   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11550   {
11551     if (game_em.level_solved &&
11552         !game_em.game_over)                             // game won
11553     {
11554       LevelSolved();
11555
11556       game_em.game_over = TRUE;
11557
11558       game.all_players_gone = TRUE;
11559     }
11560
11561     if (game_em.game_over)                              // game lost
11562       game.all_players_gone = TRUE;
11563   }
11564   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11565   {
11566     if (game_sp.level_solved &&
11567         !game_sp.game_over)                             // game won
11568     {
11569       LevelSolved();
11570
11571       game_sp.game_over = TRUE;
11572
11573       game.all_players_gone = TRUE;
11574     }
11575
11576     if (game_sp.game_over)                              // game lost
11577       game.all_players_gone = TRUE;
11578   }
11579   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11580   {
11581     if (game_mm.level_solved &&
11582         !game_mm.game_over)                             // game won
11583     {
11584       LevelSolved();
11585
11586       game_mm.game_over = TRUE;
11587
11588       game.all_players_gone = TRUE;
11589     }
11590
11591     if (game_mm.game_over)                              // game lost
11592       game.all_players_gone = TRUE;
11593   }
11594 }
11595
11596 static void CheckLevelTime(void)
11597 {
11598   int i;
11599
11600   if (TimeFrames >= FRAMES_PER_SECOND)
11601   {
11602     TimeFrames = 0;
11603     TapeTime++;
11604
11605     for (i = 0; i < MAX_PLAYERS; i++)
11606     {
11607       struct PlayerInfo *player = &stored_player[i];
11608
11609       if (SHIELD_ON(player))
11610       {
11611         player->shield_normal_time_left--;
11612
11613         if (player->shield_deadly_time_left > 0)
11614           player->shield_deadly_time_left--;
11615       }
11616     }
11617
11618     if (!game.LevelSolved && !level.use_step_counter)
11619     {
11620       TimePlayed++;
11621
11622       if (TimeLeft > 0)
11623       {
11624         TimeLeft--;
11625
11626         if (TimeLeft <= 10 && setup.time_limit)
11627           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11628
11629         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11630            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11631
11632         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11633
11634         if (!TimeLeft && setup.time_limit)
11635         {
11636           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11637             game_em.lev->killed_out_of_time = TRUE;
11638           else
11639             for (i = 0; i < MAX_PLAYERS; i++)
11640               KillPlayer(&stored_player[i]);
11641         }
11642       }
11643       else if (game.no_time_limit && !game.all_players_gone)
11644       {
11645         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11646       }
11647
11648       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11649     }
11650
11651     if (tape.recording || tape.playing)
11652       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11653   }
11654
11655   if (tape.recording || tape.playing)
11656     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11657
11658   UpdateAndDisplayGameControlValues();
11659 }
11660
11661 void AdvanceFrameAndPlayerCounters(int player_nr)
11662 {
11663   int i;
11664
11665   // advance frame counters (global frame counter and time frame counter)
11666   FrameCounter++;
11667   TimeFrames++;
11668
11669   // advance player counters (counters for move delay, move animation etc.)
11670   for (i = 0; i < MAX_PLAYERS; i++)
11671   {
11672     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11673     int move_delay_value = stored_player[i].move_delay_value;
11674     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11675
11676     if (!advance_player_counters)       // not all players may be affected
11677       continue;
11678
11679     if (move_frames == 0)       // less than one move per game frame
11680     {
11681       int stepsize = TILEX / move_delay_value;
11682       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11683       int count = (stored_player[i].is_moving ?
11684                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11685
11686       if (count % delay == 0)
11687         move_frames = 1;
11688     }
11689
11690     stored_player[i].Frame += move_frames;
11691
11692     if (stored_player[i].MovPos != 0)
11693       stored_player[i].StepFrame += move_frames;
11694
11695     if (stored_player[i].move_delay > 0)
11696       stored_player[i].move_delay--;
11697
11698     // due to bugs in previous versions, counter must count up, not down
11699     if (stored_player[i].push_delay != -1)
11700       stored_player[i].push_delay++;
11701
11702     if (stored_player[i].drop_delay > 0)
11703       stored_player[i].drop_delay--;
11704
11705     if (stored_player[i].is_dropping_pressed)
11706       stored_player[i].drop_pressed_delay++;
11707   }
11708 }
11709
11710 void StartGameActions(boolean init_network_game, boolean record_tape,
11711                       int random_seed)
11712 {
11713   unsigned int new_random_seed = InitRND(random_seed);
11714
11715   if (record_tape)
11716     TapeStartRecording(new_random_seed);
11717
11718   if (init_network_game)
11719   {
11720     SendToServer_LevelFile();
11721     SendToServer_StartPlaying();
11722
11723     return;
11724   }
11725
11726   InitGame();
11727 }
11728
11729 static void GameActionsExt(void)
11730 {
11731 #if 0
11732   static unsigned int game_frame_delay = 0;
11733 #endif
11734   unsigned int game_frame_delay_value;
11735   byte *recorded_player_action;
11736   byte summarized_player_action = 0;
11737   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11738   int i;
11739
11740   // detect endless loops, caused by custom element programming
11741   if (recursion_loop_detected && recursion_loop_depth == 0)
11742   {
11743     char *message = getStringCat3("Internal Error! Element ",
11744                                   EL_NAME(recursion_loop_element),
11745                                   " caused endless loop! Quit the game?");
11746
11747     Warn("element '%s' caused endless loop in game engine",
11748          EL_NAME(recursion_loop_element));
11749
11750     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11751
11752     recursion_loop_detected = FALSE;    // if game should be continued
11753
11754     free(message);
11755
11756     return;
11757   }
11758
11759   if (game.restart_level)
11760     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11761
11762   CheckLevelSolved();
11763
11764   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11765     GameWon();
11766
11767   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11768     TapeStop();
11769
11770   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11771     return;
11772
11773   game_frame_delay_value =
11774     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11775
11776   if (tape.playing && tape.warp_forward && !tape.pausing)
11777     game_frame_delay_value = 0;
11778
11779   SetVideoFrameDelay(game_frame_delay_value);
11780
11781   // (de)activate virtual buttons depending on current game status
11782   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11783   {
11784     if (game.all_players_gone)  // if no players there to be controlled anymore
11785       SetOverlayActive(FALSE);
11786     else if (!tape.playing)     // if game continues after tape stopped playing
11787       SetOverlayActive(TRUE);
11788   }
11789
11790 #if 0
11791 #if 0
11792   // ---------- main game synchronization point ----------
11793
11794   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11795
11796   Debug("game:playing:skip", "skip == %d", skip);
11797
11798 #else
11799   // ---------- main game synchronization point ----------
11800
11801   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11802 #endif
11803 #endif
11804
11805   if (network_playing && !network_player_action_received)
11806   {
11807     // try to get network player actions in time
11808
11809     // last chance to get network player actions without main loop delay
11810     HandleNetworking();
11811
11812     // game was quit by network peer
11813     if (game_status != GAME_MODE_PLAYING)
11814       return;
11815
11816     // check if network player actions still missing and game still running
11817     if (!network_player_action_received && !checkGameEnded())
11818       return;           // failed to get network player actions in time
11819
11820     // do not yet reset "network_player_action_received" (for tape.pausing)
11821   }
11822
11823   if (tape.pausing)
11824     return;
11825
11826   // at this point we know that we really continue executing the game
11827
11828   network_player_action_received = FALSE;
11829
11830   // when playing tape, read previously recorded player input from tape data
11831   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11832
11833   local_player->effective_mouse_action = local_player->mouse_action;
11834
11835   if (recorded_player_action != NULL)
11836     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11837                                  recorded_player_action);
11838
11839   // TapePlayAction() may return NULL when toggling to "pause before death"
11840   if (tape.pausing)
11841     return;
11842
11843   if (tape.set_centered_player)
11844   {
11845     game.centered_player_nr_next = tape.centered_player_nr_next;
11846     game.set_centered_player = TRUE;
11847   }
11848
11849   for (i = 0; i < MAX_PLAYERS; i++)
11850   {
11851     summarized_player_action |= stored_player[i].action;
11852
11853     if (!network_playing && (game.team_mode || tape.playing))
11854       stored_player[i].effective_action = stored_player[i].action;
11855   }
11856
11857   if (network_playing && !checkGameEnded())
11858     SendToServer_MovePlayer(summarized_player_action);
11859
11860   // summarize all actions at local players mapped input device position
11861   // (this allows using different input devices in single player mode)
11862   if (!network.enabled && !game.team_mode)
11863     stored_player[map_player_action[local_player->index_nr]].effective_action =
11864       summarized_player_action;
11865
11866   // summarize all actions at centered player in local team mode
11867   if (tape.recording &&
11868       setup.team_mode && !network.enabled &&
11869       setup.input_on_focus &&
11870       game.centered_player_nr != -1)
11871   {
11872     for (i = 0; i < MAX_PLAYERS; i++)
11873       stored_player[map_player_action[i]].effective_action =
11874         (i == game.centered_player_nr ? summarized_player_action : 0);
11875   }
11876
11877   if (recorded_player_action != NULL)
11878     for (i = 0; i < MAX_PLAYERS; i++)
11879       stored_player[i].effective_action = recorded_player_action[i];
11880
11881   for (i = 0; i < MAX_PLAYERS; i++)
11882   {
11883     tape_action[i] = stored_player[i].effective_action;
11884
11885     /* (this may happen in the RND game engine if a player was not present on
11886        the playfield on level start, but appeared later from a custom element */
11887     if (setup.team_mode &&
11888         tape.recording &&
11889         tape_action[i] &&
11890         !tape.player_participates[i])
11891       tape.player_participates[i] = TRUE;
11892   }
11893
11894   SetTapeActionFromMouseAction(tape_action,
11895                                &local_player->effective_mouse_action);
11896
11897   // only record actions from input devices, but not programmed actions
11898   if (tape.recording)
11899     TapeRecordAction(tape_action);
11900
11901   // remember if game was played (especially after tape stopped playing)
11902   if (!tape.playing && summarized_player_action)
11903     game.GamePlayed = TRUE;
11904
11905 #if USE_NEW_PLAYER_ASSIGNMENTS
11906   // !!! also map player actions in single player mode !!!
11907   // if (game.team_mode)
11908   if (1)
11909   {
11910     byte mapped_action[MAX_PLAYERS];
11911
11912 #if DEBUG_PLAYER_ACTIONS
11913     for (i = 0; i < MAX_PLAYERS; i++)
11914       DebugContinued("", "%d, ", stored_player[i].effective_action);
11915 #endif
11916
11917     for (i = 0; i < MAX_PLAYERS; i++)
11918       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11919
11920     for (i = 0; i < MAX_PLAYERS; i++)
11921       stored_player[i].effective_action = mapped_action[i];
11922
11923 #if DEBUG_PLAYER_ACTIONS
11924     DebugContinued("", "=> ");
11925     for (i = 0; i < MAX_PLAYERS; i++)
11926       DebugContinued("", "%d, ", stored_player[i].effective_action);
11927     DebugContinued("game:playing:player", "\n");
11928 #endif
11929   }
11930 #if DEBUG_PLAYER_ACTIONS
11931   else
11932   {
11933     for (i = 0; i < MAX_PLAYERS; i++)
11934       DebugContinued("", "%d, ", stored_player[i].effective_action);
11935     DebugContinued("game:playing:player", "\n");
11936   }
11937 #endif
11938 #endif
11939
11940   for (i = 0; i < MAX_PLAYERS; i++)
11941   {
11942     // allow engine snapshot in case of changed movement attempt
11943     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11944         (stored_player[i].effective_action & KEY_MOTION))
11945       game.snapshot.changed_action = TRUE;
11946
11947     // allow engine snapshot in case of snapping/dropping attempt
11948     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11949         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11950       game.snapshot.changed_action = TRUE;
11951
11952     game.snapshot.last_action[i] = stored_player[i].effective_action;
11953   }
11954
11955   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11956   {
11957     GameActions_EM_Main();
11958   }
11959   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11960   {
11961     GameActions_SP_Main();
11962   }
11963   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11964   {
11965     GameActions_MM_Main();
11966   }
11967   else
11968   {
11969     GameActions_RND_Main();
11970   }
11971
11972   BlitScreenToBitmap(backbuffer);
11973
11974   CheckLevelSolved();
11975   CheckLevelTime();
11976
11977   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11978
11979   if (global.show_frames_per_second)
11980   {
11981     static unsigned int fps_counter = 0;
11982     static int fps_frames = 0;
11983     unsigned int fps_delay_ms = Counter() - fps_counter;
11984
11985     fps_frames++;
11986
11987     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11988     {
11989       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11990
11991       fps_frames = 0;
11992       fps_counter = Counter();
11993
11994       // always draw FPS to screen after FPS value was updated
11995       redraw_mask |= REDRAW_FPS;
11996     }
11997
11998     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11999     if (GetDrawDeactivationMask() == REDRAW_NONE)
12000       redraw_mask |= REDRAW_FPS;
12001   }
12002 }
12003
12004 static void GameActions_CheckSaveEngineSnapshot(void)
12005 {
12006   if (!game.snapshot.save_snapshot)
12007     return;
12008
12009   // clear flag for saving snapshot _before_ saving snapshot
12010   game.snapshot.save_snapshot = FALSE;
12011
12012   SaveEngineSnapshotToList();
12013 }
12014
12015 void GameActions(void)
12016 {
12017   GameActionsExt();
12018
12019   GameActions_CheckSaveEngineSnapshot();
12020 }
12021
12022 void GameActions_EM_Main(void)
12023 {
12024   byte effective_action[MAX_PLAYERS];
12025   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12026   int i;
12027
12028   for (i = 0; i < MAX_PLAYERS; i++)
12029     effective_action[i] = stored_player[i].effective_action;
12030
12031   GameActions_EM(effective_action, warp_mode);
12032 }
12033
12034 void GameActions_SP_Main(void)
12035 {
12036   byte effective_action[MAX_PLAYERS];
12037   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12038   int i;
12039
12040   for (i = 0; i < MAX_PLAYERS; i++)
12041     effective_action[i] = stored_player[i].effective_action;
12042
12043   GameActions_SP(effective_action, warp_mode);
12044
12045   for (i = 0; i < MAX_PLAYERS; i++)
12046   {
12047     if (stored_player[i].force_dropping)
12048       stored_player[i].action |= KEY_BUTTON_DROP;
12049
12050     stored_player[i].force_dropping = FALSE;
12051   }
12052 }
12053
12054 void GameActions_MM_Main(void)
12055 {
12056   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12057
12058   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12059 }
12060
12061 void GameActions_RND_Main(void)
12062 {
12063   GameActions_RND();
12064 }
12065
12066 void GameActions_RND(void)
12067 {
12068   static struct MouseActionInfo mouse_action_last = { 0 };
12069   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12070   int magic_wall_x = 0, magic_wall_y = 0;
12071   int i, x, y, element, graphic, last_gfx_frame;
12072
12073   InitPlayfieldScanModeVars();
12074
12075   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12076   {
12077     SCAN_PLAYFIELD(x, y)
12078     {
12079       ChangeCount[x][y] = 0;
12080       ChangeEvent[x][y] = -1;
12081     }
12082   }
12083
12084   if (game.set_centered_player)
12085   {
12086     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12087
12088     // switching to "all players" only possible if all players fit to screen
12089     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12090     {
12091       game.centered_player_nr_next = game.centered_player_nr;
12092       game.set_centered_player = FALSE;
12093     }
12094
12095     // do not switch focus to non-existing (or non-active) player
12096     if (game.centered_player_nr_next >= 0 &&
12097         !stored_player[game.centered_player_nr_next].active)
12098     {
12099       game.centered_player_nr_next = game.centered_player_nr;
12100       game.set_centered_player = FALSE;
12101     }
12102   }
12103
12104   if (game.set_centered_player &&
12105       ScreenMovPos == 0)        // screen currently aligned at tile position
12106   {
12107     int sx, sy;
12108
12109     if (game.centered_player_nr_next == -1)
12110     {
12111       setScreenCenteredToAllPlayers(&sx, &sy);
12112     }
12113     else
12114     {
12115       sx = stored_player[game.centered_player_nr_next].jx;
12116       sy = stored_player[game.centered_player_nr_next].jy;
12117     }
12118
12119     game.centered_player_nr = game.centered_player_nr_next;
12120     game.set_centered_player = FALSE;
12121
12122     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12123     DrawGameDoorValues();
12124   }
12125
12126   // check single step mode (set flag and clear again if any player is active)
12127   game.enter_single_step_mode =
12128     (tape.single_step && tape.recording && !tape.pausing);
12129
12130   for (i = 0; i < MAX_PLAYERS; i++)
12131   {
12132     int actual_player_action = stored_player[i].effective_action;
12133
12134 #if 1
12135     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12136        - rnd_equinox_tetrachloride 048
12137        - rnd_equinox_tetrachloride_ii 096
12138        - rnd_emanuel_schmieg 002
12139        - doctor_sloan_ww 001, 020
12140     */
12141     if (stored_player[i].MovPos == 0)
12142       CheckGravityMovement(&stored_player[i]);
12143 #endif
12144
12145     // overwrite programmed action with tape action
12146     if (stored_player[i].programmed_action)
12147       actual_player_action = stored_player[i].programmed_action;
12148
12149     PlayerActions(&stored_player[i], actual_player_action);
12150
12151     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12152   }
12153
12154   // single step pause mode may already have been toggled by "ScrollPlayer()"
12155   if (game.enter_single_step_mode && !tape.pausing)
12156     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12157
12158   ScrollScreen(NULL, SCROLL_GO_ON);
12159
12160   /* for backwards compatibility, the following code emulates a fixed bug that
12161      occured when pushing elements (causing elements that just made their last
12162      pushing step to already (if possible) make their first falling step in the
12163      same game frame, which is bad); this code is also needed to use the famous
12164      "spring push bug" which is used in older levels and might be wanted to be
12165      used also in newer levels, but in this case the buggy pushing code is only
12166      affecting the "spring" element and no other elements */
12167
12168   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12169   {
12170     for (i = 0; i < MAX_PLAYERS; i++)
12171     {
12172       struct PlayerInfo *player = &stored_player[i];
12173       int x = player->jx;
12174       int y = player->jy;
12175
12176       if (player->active && player->is_pushing && player->is_moving &&
12177           IS_MOVING(x, y) &&
12178           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12179            Tile[x][y] == EL_SPRING))
12180       {
12181         ContinueMoving(x, y);
12182
12183         // continue moving after pushing (this is actually a bug)
12184         if (!IS_MOVING(x, y))
12185           Stop[x][y] = FALSE;
12186       }
12187     }
12188   }
12189
12190   SCAN_PLAYFIELD(x, y)
12191   {
12192     Last[x][y] = Tile[x][y];
12193
12194     ChangeCount[x][y] = 0;
12195     ChangeEvent[x][y] = -1;
12196
12197     // this must be handled before main playfield loop
12198     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12199     {
12200       MovDelay[x][y]--;
12201       if (MovDelay[x][y] <= 0)
12202         RemoveField(x, y);
12203     }
12204
12205     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12206     {
12207       MovDelay[x][y]--;
12208       if (MovDelay[x][y] <= 0)
12209       {
12210         int element = Store[x][y];
12211         int move_direction = MovDir[x][y];
12212         int player_index_bit = Store2[x][y];
12213
12214         Store[x][y] = 0;
12215         Store2[x][y] = 0;
12216
12217         RemoveField(x, y);
12218         TEST_DrawLevelField(x, y);
12219
12220         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12221
12222         if (IS_ENVELOPE(element))
12223           local_player->show_envelope = element;
12224       }
12225     }
12226
12227 #if DEBUG
12228     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12229     {
12230       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12231             x, y);
12232       Debug("game:playing:GameActions_RND", "This should never happen!");
12233
12234       ChangePage[x][y] = -1;
12235     }
12236 #endif
12237
12238     Stop[x][y] = FALSE;
12239     if (WasJustMoving[x][y] > 0)
12240       WasJustMoving[x][y]--;
12241     if (WasJustFalling[x][y] > 0)
12242       WasJustFalling[x][y]--;
12243     if (CheckCollision[x][y] > 0)
12244       CheckCollision[x][y]--;
12245     if (CheckImpact[x][y] > 0)
12246       CheckImpact[x][y]--;
12247
12248     GfxFrame[x][y]++;
12249
12250     /* reset finished pushing action (not done in ContinueMoving() to allow
12251        continuous pushing animation for elements with zero push delay) */
12252     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12253     {
12254       ResetGfxAnimation(x, y);
12255       TEST_DrawLevelField(x, y);
12256     }
12257
12258 #if DEBUG
12259     if (IS_BLOCKED(x, y))
12260     {
12261       int oldx, oldy;
12262
12263       Blocked2Moving(x, y, &oldx, &oldy);
12264       if (!IS_MOVING(oldx, oldy))
12265       {
12266         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12267         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12268         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12269         Debug("game:playing:GameActions_RND", "This should never happen!");
12270       }
12271     }
12272 #endif
12273   }
12274
12275   if (mouse_action.button)
12276   {
12277     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12278     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12279
12280     x = mouse_action.lx;
12281     y = mouse_action.ly;
12282     element = Tile[x][y];
12283
12284     if (new_button)
12285     {
12286       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12287       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12288                                          ch_button);
12289     }
12290
12291     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12292     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12293                                        ch_button);
12294   }
12295
12296   SCAN_PLAYFIELD(x, y)
12297   {
12298     element = Tile[x][y];
12299     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12300     last_gfx_frame = GfxFrame[x][y];
12301
12302     if (element == EL_EMPTY)
12303       graphic = el2img(GfxElementEmpty[x][y]);
12304
12305     ResetGfxFrame(x, y);
12306
12307     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12308       DrawLevelGraphicAnimation(x, y, graphic);
12309
12310     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12311         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12312       ResetRandomAnimationValue(x, y);
12313
12314     SetRandomAnimationValue(x, y);
12315
12316     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12317
12318     if (IS_INACTIVE(element))
12319     {
12320       if (IS_ANIMATED(graphic))
12321         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12322
12323       continue;
12324     }
12325
12326     // this may take place after moving, so 'element' may have changed
12327     if (IS_CHANGING(x, y) &&
12328         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12329     {
12330       int page = element_info[element].event_page_nr[CE_DELAY];
12331
12332       HandleElementChange(x, y, page);
12333
12334       element = Tile[x][y];
12335       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12336     }
12337
12338     CheckNextToConditions(x, y);
12339
12340     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12341     {
12342       StartMoving(x, y);
12343
12344       element = Tile[x][y];
12345       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12346
12347       if (IS_ANIMATED(graphic) &&
12348           !IS_MOVING(x, y) &&
12349           !Stop[x][y])
12350         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12351
12352       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12353         TEST_DrawTwinkleOnField(x, y);
12354     }
12355     else if (element == EL_ACID)
12356     {
12357       if (!Stop[x][y])
12358         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12359     }
12360     else if ((element == EL_EXIT_OPEN ||
12361               element == EL_EM_EXIT_OPEN ||
12362               element == EL_SP_EXIT_OPEN ||
12363               element == EL_STEEL_EXIT_OPEN ||
12364               element == EL_EM_STEEL_EXIT_OPEN ||
12365               element == EL_SP_TERMINAL ||
12366               element == EL_SP_TERMINAL_ACTIVE ||
12367               element == EL_EXTRA_TIME ||
12368               element == EL_SHIELD_NORMAL ||
12369               element == EL_SHIELD_DEADLY) &&
12370              IS_ANIMATED(graphic))
12371       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12372     else if (IS_MOVING(x, y))
12373       ContinueMoving(x, y);
12374     else if (IS_ACTIVE_BOMB(element))
12375       CheckDynamite(x, y);
12376     else if (element == EL_AMOEBA_GROWING)
12377       AmoebaGrowing(x, y);
12378     else if (element == EL_AMOEBA_SHRINKING)
12379       AmoebaShrinking(x, y);
12380
12381 #if !USE_NEW_AMOEBA_CODE
12382     else if (IS_AMOEBALIVE(element))
12383       AmoebaReproduce(x, y);
12384 #endif
12385
12386     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12387       Life(x, y);
12388     else if (element == EL_EXIT_CLOSED)
12389       CheckExit(x, y);
12390     else if (element == EL_EM_EXIT_CLOSED)
12391       CheckExitEM(x, y);
12392     else if (element == EL_STEEL_EXIT_CLOSED)
12393       CheckExitSteel(x, y);
12394     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12395       CheckExitSteelEM(x, y);
12396     else if (element == EL_SP_EXIT_CLOSED)
12397       CheckExitSP(x, y);
12398     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12399              element == EL_EXPANDABLE_STEELWALL_GROWING)
12400       MauerWaechst(x, y);
12401     else if (element == EL_EXPANDABLE_WALL ||
12402              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12403              element == EL_EXPANDABLE_WALL_VERTICAL ||
12404              element == EL_EXPANDABLE_WALL_ANY ||
12405              element == EL_BD_EXPANDABLE_WALL)
12406       MauerAbleger(x, y);
12407     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12408              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12409              element == EL_EXPANDABLE_STEELWALL_ANY)
12410       MauerAblegerStahl(x, y);
12411     else if (element == EL_FLAMES)
12412       CheckForDragon(x, y);
12413     else if (element == EL_EXPLOSION)
12414       ; // drawing of correct explosion animation is handled separately
12415     else if (element == EL_ELEMENT_SNAPPING ||
12416              element == EL_DIAGONAL_SHRINKING ||
12417              element == EL_DIAGONAL_GROWING)
12418     {
12419       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12420
12421       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12422     }
12423     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12424       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12425
12426     if (IS_BELT_ACTIVE(element))
12427       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12428
12429     if (game.magic_wall_active)
12430     {
12431       int jx = local_player->jx, jy = local_player->jy;
12432
12433       // play the element sound at the position nearest to the player
12434       if ((element == EL_MAGIC_WALL_FULL ||
12435            element == EL_MAGIC_WALL_ACTIVE ||
12436            element == EL_MAGIC_WALL_EMPTYING ||
12437            element == EL_BD_MAGIC_WALL_FULL ||
12438            element == EL_BD_MAGIC_WALL_ACTIVE ||
12439            element == EL_BD_MAGIC_WALL_EMPTYING ||
12440            element == EL_DC_MAGIC_WALL_FULL ||
12441            element == EL_DC_MAGIC_WALL_ACTIVE ||
12442            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12443           ABS(x - jx) + ABS(y - jy) <
12444           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12445       {
12446         magic_wall_x = x;
12447         magic_wall_y = y;
12448       }
12449     }
12450   }
12451
12452 #if USE_NEW_AMOEBA_CODE
12453   // new experimental amoeba growth stuff
12454   if (!(FrameCounter % 8))
12455   {
12456     static unsigned int random = 1684108901;
12457
12458     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12459     {
12460       x = RND(lev_fieldx);
12461       y = RND(lev_fieldy);
12462       element = Tile[x][y];
12463
12464       if (!IS_PLAYER(x,y) &&
12465           (element == EL_EMPTY ||
12466            CAN_GROW_INTO(element) ||
12467            element == EL_QUICKSAND_EMPTY ||
12468            element == EL_QUICKSAND_FAST_EMPTY ||
12469            element == EL_ACID_SPLASH_LEFT ||
12470            element == EL_ACID_SPLASH_RIGHT))
12471       {
12472         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12473             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12474             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12475             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12476           Tile[x][y] = EL_AMOEBA_DROP;
12477       }
12478
12479       random = random * 129 + 1;
12480     }
12481   }
12482 #endif
12483
12484   game.explosions_delayed = FALSE;
12485
12486   SCAN_PLAYFIELD(x, y)
12487   {
12488     element = Tile[x][y];
12489
12490     if (ExplodeField[x][y])
12491       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12492     else if (element == EL_EXPLOSION)
12493       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12494
12495     ExplodeField[x][y] = EX_TYPE_NONE;
12496   }
12497
12498   game.explosions_delayed = TRUE;
12499
12500   if (game.magic_wall_active)
12501   {
12502     if (!(game.magic_wall_time_left % 4))
12503     {
12504       int element = Tile[magic_wall_x][magic_wall_y];
12505
12506       if (element == EL_BD_MAGIC_WALL_FULL ||
12507           element == EL_BD_MAGIC_WALL_ACTIVE ||
12508           element == EL_BD_MAGIC_WALL_EMPTYING)
12509         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12510       else if (element == EL_DC_MAGIC_WALL_FULL ||
12511                element == EL_DC_MAGIC_WALL_ACTIVE ||
12512                element == EL_DC_MAGIC_WALL_EMPTYING)
12513         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12514       else
12515         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12516     }
12517
12518     if (game.magic_wall_time_left > 0)
12519     {
12520       game.magic_wall_time_left--;
12521
12522       if (!game.magic_wall_time_left)
12523       {
12524         SCAN_PLAYFIELD(x, y)
12525         {
12526           element = Tile[x][y];
12527
12528           if (element == EL_MAGIC_WALL_ACTIVE ||
12529               element == EL_MAGIC_WALL_FULL)
12530           {
12531             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12532             TEST_DrawLevelField(x, y);
12533           }
12534           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12535                    element == EL_BD_MAGIC_WALL_FULL)
12536           {
12537             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12538             TEST_DrawLevelField(x, y);
12539           }
12540           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12541                    element == EL_DC_MAGIC_WALL_FULL)
12542           {
12543             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12544             TEST_DrawLevelField(x, y);
12545           }
12546         }
12547
12548         game.magic_wall_active = FALSE;
12549       }
12550     }
12551   }
12552
12553   if (game.light_time_left > 0)
12554   {
12555     game.light_time_left--;
12556
12557     if (game.light_time_left == 0)
12558       RedrawAllLightSwitchesAndInvisibleElements();
12559   }
12560
12561   if (game.timegate_time_left > 0)
12562   {
12563     game.timegate_time_left--;
12564
12565     if (game.timegate_time_left == 0)
12566       CloseAllOpenTimegates();
12567   }
12568
12569   if (game.lenses_time_left > 0)
12570   {
12571     game.lenses_time_left--;
12572
12573     if (game.lenses_time_left == 0)
12574       RedrawAllInvisibleElementsForLenses();
12575   }
12576
12577   if (game.magnify_time_left > 0)
12578   {
12579     game.magnify_time_left--;
12580
12581     if (game.magnify_time_left == 0)
12582       RedrawAllInvisibleElementsForMagnifier();
12583   }
12584
12585   for (i = 0; i < MAX_PLAYERS; i++)
12586   {
12587     struct PlayerInfo *player = &stored_player[i];
12588
12589     if (SHIELD_ON(player))
12590     {
12591       if (player->shield_deadly_time_left)
12592         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12593       else if (player->shield_normal_time_left)
12594         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12595     }
12596   }
12597
12598 #if USE_DELAYED_GFX_REDRAW
12599   SCAN_PLAYFIELD(x, y)
12600   {
12601     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12602     {
12603       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12604          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12605
12606       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12607         DrawLevelField(x, y);
12608
12609       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12610         DrawLevelFieldCrumbled(x, y);
12611
12612       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12613         DrawLevelFieldCrumbledNeighbours(x, y);
12614
12615       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12616         DrawTwinkleOnField(x, y);
12617     }
12618
12619     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12620   }
12621 #endif
12622
12623   DrawAllPlayers();
12624   PlayAllPlayersSound();
12625
12626   for (i = 0; i < MAX_PLAYERS; i++)
12627   {
12628     struct PlayerInfo *player = &stored_player[i];
12629
12630     if (player->show_envelope != 0 && (!player->active ||
12631                                        player->MovPos == 0))
12632     {
12633       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12634
12635       player->show_envelope = 0;
12636     }
12637   }
12638
12639   // use random number generator in every frame to make it less predictable
12640   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12641     RND(1);
12642
12643   mouse_action_last = mouse_action;
12644 }
12645
12646 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12647 {
12648   int min_x = x, min_y = y, max_x = x, max_y = y;
12649   int scr_fieldx = getScreenFieldSizeX();
12650   int scr_fieldy = getScreenFieldSizeY();
12651   int i;
12652
12653   for (i = 0; i < MAX_PLAYERS; i++)
12654   {
12655     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12656
12657     if (!stored_player[i].active || &stored_player[i] == player)
12658       continue;
12659
12660     min_x = MIN(min_x, jx);
12661     min_y = MIN(min_y, jy);
12662     max_x = MAX(max_x, jx);
12663     max_y = MAX(max_y, jy);
12664   }
12665
12666   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12667 }
12668
12669 static boolean AllPlayersInVisibleScreen(void)
12670 {
12671   int i;
12672
12673   for (i = 0; i < MAX_PLAYERS; i++)
12674   {
12675     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12676
12677     if (!stored_player[i].active)
12678       continue;
12679
12680     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12681       return FALSE;
12682   }
12683
12684   return TRUE;
12685 }
12686
12687 void ScrollLevel(int dx, int dy)
12688 {
12689   int scroll_offset = 2 * TILEX_VAR;
12690   int x, y;
12691
12692   BlitBitmap(drawto_field, drawto_field,
12693              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12694              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12695              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12696              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12697              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12698              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12699
12700   if (dx != 0)
12701   {
12702     x = (dx == 1 ? BX1 : BX2);
12703     for (y = BY1; y <= BY2; y++)
12704       DrawScreenField(x, y);
12705   }
12706
12707   if (dy != 0)
12708   {
12709     y = (dy == 1 ? BY1 : BY2);
12710     for (x = BX1; x <= BX2; x++)
12711       DrawScreenField(x, y);
12712   }
12713
12714   redraw_mask |= REDRAW_FIELD;
12715 }
12716
12717 static boolean canFallDown(struct PlayerInfo *player)
12718 {
12719   int jx = player->jx, jy = player->jy;
12720
12721   return (IN_LEV_FIELD(jx, jy + 1) &&
12722           (IS_FREE(jx, jy + 1) ||
12723            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12724           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12725           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12726 }
12727
12728 static boolean canPassField(int x, int y, int move_dir)
12729 {
12730   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12731   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12732   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12733   int nextx = x + dx;
12734   int nexty = y + dy;
12735   int element = Tile[x][y];
12736
12737   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12738           !CAN_MOVE(element) &&
12739           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12740           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12741           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12742 }
12743
12744 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12745 {
12746   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12747   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12748   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12749   int newx = x + dx;
12750   int newy = y + dy;
12751
12752   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12753           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12754           (IS_DIGGABLE(Tile[newx][newy]) ||
12755            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12756            canPassField(newx, newy, move_dir)));
12757 }
12758
12759 static void CheckGravityMovement(struct PlayerInfo *player)
12760 {
12761   if (player->gravity && !player->programmed_action)
12762   {
12763     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12764     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12765     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12766     int jx = player->jx, jy = player->jy;
12767     boolean player_is_moving_to_valid_field =
12768       (!player_is_snapping &&
12769        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12770         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12771     boolean player_can_fall_down = canFallDown(player);
12772
12773     if (player_can_fall_down &&
12774         !player_is_moving_to_valid_field)
12775       player->programmed_action = MV_DOWN;
12776   }
12777 }
12778
12779 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12780 {
12781   return CheckGravityMovement(player);
12782
12783   if (player->gravity && !player->programmed_action)
12784   {
12785     int jx = player->jx, jy = player->jy;
12786     boolean field_under_player_is_free =
12787       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12788     boolean player_is_standing_on_valid_field =
12789       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12790        (IS_WALKABLE(Tile[jx][jy]) &&
12791         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12792
12793     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12794       player->programmed_action = MV_DOWN;
12795   }
12796 }
12797
12798 /*
12799   MovePlayerOneStep()
12800   -----------------------------------------------------------------------------
12801   dx, dy:               direction (non-diagonal) to try to move the player to
12802   real_dx, real_dy:     direction as read from input device (can be diagonal)
12803 */
12804
12805 boolean MovePlayerOneStep(struct PlayerInfo *player,
12806                           int dx, int dy, int real_dx, int real_dy)
12807 {
12808   int jx = player->jx, jy = player->jy;
12809   int new_jx = jx + dx, new_jy = jy + dy;
12810   int can_move;
12811   boolean player_can_move = !player->cannot_move;
12812
12813   if (!player->active || (!dx && !dy))
12814     return MP_NO_ACTION;
12815
12816   player->MovDir = (dx < 0 ? MV_LEFT :
12817                     dx > 0 ? MV_RIGHT :
12818                     dy < 0 ? MV_UP :
12819                     dy > 0 ? MV_DOWN :  MV_NONE);
12820
12821   if (!IN_LEV_FIELD(new_jx, new_jy))
12822     return MP_NO_ACTION;
12823
12824   if (!player_can_move)
12825   {
12826     if (player->MovPos == 0)
12827     {
12828       player->is_moving = FALSE;
12829       player->is_digging = FALSE;
12830       player->is_collecting = FALSE;
12831       player->is_snapping = FALSE;
12832       player->is_pushing = FALSE;
12833     }
12834   }
12835
12836   if (!network.enabled && game.centered_player_nr == -1 &&
12837       !AllPlayersInSight(player, new_jx, new_jy))
12838     return MP_NO_ACTION;
12839
12840   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12841   if (can_move != MP_MOVING)
12842     return can_move;
12843
12844   // check if DigField() has caused relocation of the player
12845   if (player->jx != jx || player->jy != jy)
12846     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12847
12848   StorePlayer[jx][jy] = 0;
12849   player->last_jx = jx;
12850   player->last_jy = jy;
12851   player->jx = new_jx;
12852   player->jy = new_jy;
12853   StorePlayer[new_jx][new_jy] = player->element_nr;
12854
12855   if (player->move_delay_value_next != -1)
12856   {
12857     player->move_delay_value = player->move_delay_value_next;
12858     player->move_delay_value_next = -1;
12859   }
12860
12861   player->MovPos =
12862     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12863
12864   player->step_counter++;
12865
12866   PlayerVisit[jx][jy] = FrameCounter;
12867
12868   player->is_moving = TRUE;
12869
12870 #if 1
12871   // should better be called in MovePlayer(), but this breaks some tapes
12872   ScrollPlayer(player, SCROLL_INIT);
12873 #endif
12874
12875   return MP_MOVING;
12876 }
12877
12878 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12879 {
12880   int jx = player->jx, jy = player->jy;
12881   int old_jx = jx, old_jy = jy;
12882   int moved = MP_NO_ACTION;
12883
12884   if (!player->active)
12885     return FALSE;
12886
12887   if (!dx && !dy)
12888   {
12889     if (player->MovPos == 0)
12890     {
12891       player->is_moving = FALSE;
12892       player->is_digging = FALSE;
12893       player->is_collecting = FALSE;
12894       player->is_snapping = FALSE;
12895       player->is_pushing = FALSE;
12896     }
12897
12898     return FALSE;
12899   }
12900
12901   if (player->move_delay > 0)
12902     return FALSE;
12903
12904   player->move_delay = -1;              // set to "uninitialized" value
12905
12906   // store if player is automatically moved to next field
12907   player->is_auto_moving = (player->programmed_action != MV_NONE);
12908
12909   // remove the last programmed player action
12910   player->programmed_action = 0;
12911
12912   if (player->MovPos)
12913   {
12914     // should only happen if pre-1.2 tape recordings are played
12915     // this is only for backward compatibility
12916
12917     int original_move_delay_value = player->move_delay_value;
12918
12919 #if DEBUG
12920     Debug("game:playing:MovePlayer",
12921           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12922           tape.counter);
12923 #endif
12924
12925     // scroll remaining steps with finest movement resolution
12926     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12927
12928     while (player->MovPos)
12929     {
12930       ScrollPlayer(player, SCROLL_GO_ON);
12931       ScrollScreen(NULL, SCROLL_GO_ON);
12932
12933       AdvanceFrameAndPlayerCounters(player->index_nr);
12934
12935       DrawAllPlayers();
12936       BackToFront_WithFrameDelay(0);
12937     }
12938
12939     player->move_delay_value = original_move_delay_value;
12940   }
12941
12942   player->is_active = FALSE;
12943
12944   if (player->last_move_dir & MV_HORIZONTAL)
12945   {
12946     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12947       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12948   }
12949   else
12950   {
12951     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12952       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12953   }
12954
12955   if (!moved && !player->is_active)
12956   {
12957     player->is_moving = FALSE;
12958     player->is_digging = FALSE;
12959     player->is_collecting = FALSE;
12960     player->is_snapping = FALSE;
12961     player->is_pushing = FALSE;
12962   }
12963
12964   jx = player->jx;
12965   jy = player->jy;
12966
12967   if (moved & MP_MOVING && !ScreenMovPos &&
12968       (player->index_nr == game.centered_player_nr ||
12969        game.centered_player_nr == -1))
12970   {
12971     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12972
12973     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12974     {
12975       // actual player has left the screen -- scroll in that direction
12976       if (jx != old_jx)         // player has moved horizontally
12977         scroll_x += (jx - old_jx);
12978       else                      // player has moved vertically
12979         scroll_y += (jy - old_jy);
12980     }
12981     else
12982     {
12983       int offset_raw = game.scroll_delay_value;
12984
12985       if (jx != old_jx)         // player has moved horizontally
12986       {
12987         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12988         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12989         int new_scroll_x = jx - MIDPOSX + offset_x;
12990
12991         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12992             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12993           scroll_x = new_scroll_x;
12994
12995         // don't scroll over playfield boundaries
12996         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12997
12998         // don't scroll more than one field at a time
12999         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13000
13001         // don't scroll against the player's moving direction
13002         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13003             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13004           scroll_x = old_scroll_x;
13005       }
13006       else                      // player has moved vertically
13007       {
13008         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13009         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13010         int new_scroll_y = jy - MIDPOSY + offset_y;
13011
13012         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13013             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13014           scroll_y = new_scroll_y;
13015
13016         // don't scroll over playfield boundaries
13017         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13018
13019         // don't scroll more than one field at a time
13020         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13021
13022         // don't scroll against the player's moving direction
13023         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13024             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13025           scroll_y = old_scroll_y;
13026       }
13027     }
13028
13029     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13030     {
13031       if (!network.enabled && game.centered_player_nr == -1 &&
13032           !AllPlayersInVisibleScreen())
13033       {
13034         scroll_x = old_scroll_x;
13035         scroll_y = old_scroll_y;
13036       }
13037       else
13038       {
13039         ScrollScreen(player, SCROLL_INIT);
13040         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13041       }
13042     }
13043   }
13044
13045   player->StepFrame = 0;
13046
13047   if (moved & MP_MOVING)
13048   {
13049     if (old_jx != jx && old_jy == jy)
13050       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13051     else if (old_jx == jx && old_jy != jy)
13052       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13053
13054     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13055
13056     player->last_move_dir = player->MovDir;
13057     player->is_moving = TRUE;
13058     player->is_snapping = FALSE;
13059     player->is_switching = FALSE;
13060     player->is_dropping = FALSE;
13061     player->is_dropping_pressed = FALSE;
13062     player->drop_pressed_delay = 0;
13063
13064 #if 0
13065     // should better be called here than above, but this breaks some tapes
13066     ScrollPlayer(player, SCROLL_INIT);
13067 #endif
13068   }
13069   else
13070   {
13071     CheckGravityMovementWhenNotMoving(player);
13072
13073     player->is_moving = FALSE;
13074
13075     /* at this point, the player is allowed to move, but cannot move right now
13076        (e.g. because of something blocking the way) -- ensure that the player
13077        is also allowed to move in the next frame (in old versions before 3.1.1,
13078        the player was forced to wait again for eight frames before next try) */
13079
13080     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13081       player->move_delay = 0;   // allow direct movement in the next frame
13082   }
13083
13084   if (player->move_delay == -1)         // not yet initialized by DigField()
13085     player->move_delay = player->move_delay_value;
13086
13087   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13088   {
13089     TestIfPlayerTouchesBadThing(jx, jy);
13090     TestIfPlayerTouchesCustomElement(jx, jy);
13091   }
13092
13093   if (!player->active)
13094     RemovePlayer(player);
13095
13096   return moved;
13097 }
13098
13099 void ScrollPlayer(struct PlayerInfo *player, int mode)
13100 {
13101   int jx = player->jx, jy = player->jy;
13102   int last_jx = player->last_jx, last_jy = player->last_jy;
13103   int move_stepsize = TILEX / player->move_delay_value;
13104
13105   if (!player->active)
13106     return;
13107
13108   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13109     return;
13110
13111   if (mode == SCROLL_INIT)
13112   {
13113     player->actual_frame_counter = FrameCounter;
13114     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13115
13116     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13117         Tile[last_jx][last_jy] == EL_EMPTY)
13118     {
13119       int last_field_block_delay = 0;   // start with no blocking at all
13120       int block_delay_adjustment = player->block_delay_adjustment;
13121
13122       // if player blocks last field, add delay for exactly one move
13123       if (player->block_last_field)
13124       {
13125         last_field_block_delay += player->move_delay_value;
13126
13127         // when blocking enabled, prevent moving up despite gravity
13128         if (player->gravity && player->MovDir == MV_UP)
13129           block_delay_adjustment = -1;
13130       }
13131
13132       // add block delay adjustment (also possible when not blocking)
13133       last_field_block_delay += block_delay_adjustment;
13134
13135       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13136       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13137     }
13138
13139     if (player->MovPos != 0)    // player has not yet reached destination
13140       return;
13141   }
13142   else if (!FrameReached(&player->actual_frame_counter, 1))
13143     return;
13144
13145   if (player->MovPos != 0)
13146   {
13147     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13148     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13149
13150     // before DrawPlayer() to draw correct player graphic for this case
13151     if (player->MovPos == 0)
13152       CheckGravityMovement(player);
13153   }
13154
13155   if (player->MovPos == 0)      // player reached destination field
13156   {
13157     if (player->move_delay_reset_counter > 0)
13158     {
13159       player->move_delay_reset_counter--;
13160
13161       if (player->move_delay_reset_counter == 0)
13162       {
13163         // continue with normal speed after quickly moving through gate
13164         HALVE_PLAYER_SPEED(player);
13165
13166         // be able to make the next move without delay
13167         player->move_delay = 0;
13168       }
13169     }
13170
13171     player->last_jx = jx;
13172     player->last_jy = jy;
13173
13174     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13175         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13176         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13177         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13178         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13179         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13180         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13181         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13182     {
13183       ExitPlayer(player);
13184
13185       if (game.players_still_needed == 0 &&
13186           (game.friends_still_needed == 0 ||
13187            IS_SP_ELEMENT(Tile[jx][jy])))
13188         LevelSolved();
13189     }
13190
13191     // this breaks one level: "machine", level 000
13192     {
13193       int move_direction = player->MovDir;
13194       int enter_side = MV_DIR_OPPOSITE(move_direction);
13195       int leave_side = move_direction;
13196       int old_jx = last_jx;
13197       int old_jy = last_jy;
13198       int old_element = Tile[old_jx][old_jy];
13199       int new_element = Tile[jx][jy];
13200
13201       if (IS_CUSTOM_ELEMENT(old_element))
13202         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13203                                    CE_LEFT_BY_PLAYER,
13204                                    player->index_bit, leave_side);
13205
13206       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13207                                           CE_PLAYER_LEAVES_X,
13208                                           player->index_bit, leave_side);
13209
13210       if (IS_CUSTOM_ELEMENT(new_element))
13211         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13212                                    player->index_bit, enter_side);
13213
13214       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13215                                           CE_PLAYER_ENTERS_X,
13216                                           player->index_bit, enter_side);
13217
13218       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13219                                         CE_MOVE_OF_X, move_direction);
13220     }
13221
13222     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13223     {
13224       TestIfPlayerTouchesBadThing(jx, jy);
13225       TestIfPlayerTouchesCustomElement(jx, jy);
13226
13227       /* needed because pushed element has not yet reached its destination,
13228          so it would trigger a change event at its previous field location */
13229       if (!player->is_pushing)
13230         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13231
13232       if (level.finish_dig_collect &&
13233           (player->is_digging || player->is_collecting))
13234       {
13235         int last_element = player->last_removed_element;
13236         int move_direction = player->MovDir;
13237         int enter_side = MV_DIR_OPPOSITE(move_direction);
13238         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13239                             CE_PLAYER_COLLECTS_X);
13240
13241         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13242                                             player->index_bit, enter_side);
13243
13244         player->last_removed_element = EL_UNDEFINED;
13245       }
13246
13247       if (!player->active)
13248         RemovePlayer(player);
13249     }
13250
13251     if (level.use_step_counter)
13252     {
13253       int i;
13254
13255       TimePlayed++;
13256
13257       if (TimeLeft > 0)
13258       {
13259         TimeLeft--;
13260
13261         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13262           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13263
13264         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13265
13266         DisplayGameControlValues();
13267
13268         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13269           for (i = 0; i < MAX_PLAYERS; i++)
13270             KillPlayer(&stored_player[i]);
13271       }
13272       else if (game.no_time_limit && !game.all_players_gone)
13273       {
13274         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13275
13276         DisplayGameControlValues();
13277       }
13278     }
13279
13280     if (tape.single_step && tape.recording && !tape.pausing &&
13281         !player->programmed_action)
13282       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13283
13284     if (!player->programmed_action)
13285       CheckSaveEngineSnapshot(player);
13286   }
13287 }
13288
13289 void ScrollScreen(struct PlayerInfo *player, int mode)
13290 {
13291   static unsigned int screen_frame_counter = 0;
13292
13293   if (mode == SCROLL_INIT)
13294   {
13295     // set scrolling step size according to actual player's moving speed
13296     ScrollStepSize = TILEX / player->move_delay_value;
13297
13298     screen_frame_counter = FrameCounter;
13299     ScreenMovDir = player->MovDir;
13300     ScreenMovPos = player->MovPos;
13301     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13302     return;
13303   }
13304   else if (!FrameReached(&screen_frame_counter, 1))
13305     return;
13306
13307   if (ScreenMovPos)
13308   {
13309     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13310     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13311     redraw_mask |= REDRAW_FIELD;
13312   }
13313   else
13314     ScreenMovDir = MV_NONE;
13315 }
13316
13317 void CheckNextToConditions(int x, int y)
13318 {
13319   int element = Tile[x][y];
13320
13321   if (IS_PLAYER(x, y))
13322     TestIfPlayerNextToCustomElement(x, y);
13323
13324   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13325       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13326     TestIfElementNextToCustomElement(x, y);
13327 }
13328
13329 void TestIfPlayerNextToCustomElement(int x, int y)
13330 {
13331   static int xy[4][2] =
13332   {
13333     { 0, -1 },
13334     { -1, 0 },
13335     { +1, 0 },
13336     { 0, +1 }
13337   };
13338   static int trigger_sides[4][2] =
13339   {
13340     // center side       border side
13341     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13342     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13343     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13344     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13345   };
13346   int i;
13347
13348   if (!IS_PLAYER(x, y))
13349     return;
13350
13351   struct PlayerInfo *player = PLAYERINFO(x, y);
13352
13353   if (player->is_moving)
13354     return;
13355
13356   for (i = 0; i < NUM_DIRECTIONS; i++)
13357   {
13358     int xx = x + xy[i][0];
13359     int yy = y + xy[i][1];
13360     int border_side = trigger_sides[i][1];
13361     int border_element;
13362
13363     if (!IN_LEV_FIELD(xx, yy))
13364       continue;
13365
13366     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13367       continue;         // center and border element not connected
13368
13369     border_element = Tile[xx][yy];
13370
13371     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13372                                player->index_bit, border_side);
13373     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13374                                         CE_PLAYER_NEXT_TO_X,
13375                                         player->index_bit, border_side);
13376
13377     /* use player element that is initially defined in the level playfield,
13378        not the player element that corresponds to the runtime player number
13379        (example: a level that contains EL_PLAYER_3 as the only player would
13380        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13381
13382     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13383                              CE_NEXT_TO_X, border_side);
13384   }
13385 }
13386
13387 void TestIfPlayerTouchesCustomElement(int x, int y)
13388 {
13389   static int xy[4][2] =
13390   {
13391     { 0, -1 },
13392     { -1, 0 },
13393     { +1, 0 },
13394     { 0, +1 }
13395   };
13396   static int trigger_sides[4][2] =
13397   {
13398     // center side       border side
13399     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13400     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13401     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13402     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13403   };
13404   static int touch_dir[4] =
13405   {
13406     MV_LEFT | MV_RIGHT,
13407     MV_UP   | MV_DOWN,
13408     MV_UP   | MV_DOWN,
13409     MV_LEFT | MV_RIGHT
13410   };
13411   int center_element = Tile[x][y];      // should always be non-moving!
13412   int i;
13413
13414   for (i = 0; i < NUM_DIRECTIONS; i++)
13415   {
13416     int xx = x + xy[i][0];
13417     int yy = y + xy[i][1];
13418     int center_side = trigger_sides[i][0];
13419     int border_side = trigger_sides[i][1];
13420     int border_element;
13421
13422     if (!IN_LEV_FIELD(xx, yy))
13423       continue;
13424
13425     if (IS_PLAYER(x, y))                // player found at center element
13426     {
13427       struct PlayerInfo *player = PLAYERINFO(x, y);
13428
13429       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13430         border_element = Tile[xx][yy];          // may be moving!
13431       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13432         border_element = Tile[xx][yy];
13433       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13434         border_element = MovingOrBlocked2Element(xx, yy);
13435       else
13436         continue;               // center and border element do not touch
13437
13438       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13439                                  player->index_bit, border_side);
13440       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13441                                           CE_PLAYER_TOUCHES_X,
13442                                           player->index_bit, border_side);
13443
13444       {
13445         /* use player element that is initially defined in the level playfield,
13446            not the player element that corresponds to the runtime player number
13447            (example: a level that contains EL_PLAYER_3 as the only player would
13448            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13449         int player_element = PLAYERINFO(x, y)->initial_element;
13450
13451         CheckElementChangeBySide(xx, yy, border_element, player_element,
13452                                  CE_TOUCHING_X, border_side);
13453       }
13454     }
13455     else if (IS_PLAYER(xx, yy))         // player found at border element
13456     {
13457       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13458
13459       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13460       {
13461         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13462           continue;             // center and border element do not touch
13463       }
13464
13465       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13466                                  player->index_bit, center_side);
13467       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13468                                           CE_PLAYER_TOUCHES_X,
13469                                           player->index_bit, center_side);
13470
13471       {
13472         /* use player element that is initially defined in the level playfield,
13473            not the player element that corresponds to the runtime player number
13474            (example: a level that contains EL_PLAYER_3 as the only player would
13475            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13476         int player_element = PLAYERINFO(xx, yy)->initial_element;
13477
13478         CheckElementChangeBySide(x, y, center_element, player_element,
13479                                  CE_TOUCHING_X, center_side);
13480       }
13481
13482       break;
13483     }
13484   }
13485 }
13486
13487 void TestIfElementNextToCustomElement(int x, int y)
13488 {
13489   static int xy[4][2] =
13490   {
13491     { 0, -1 },
13492     { -1, 0 },
13493     { +1, 0 },
13494     { 0, +1 }
13495   };
13496   static int trigger_sides[4][2] =
13497   {
13498     // center side      border side
13499     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13500     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13501     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13502     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13503   };
13504   int center_element = Tile[x][y];      // should always be non-moving!
13505   int i;
13506
13507   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13508     return;
13509
13510   for (i = 0; i < NUM_DIRECTIONS; i++)
13511   {
13512     int xx = x + xy[i][0];
13513     int yy = y + xy[i][1];
13514     int border_side = trigger_sides[i][1];
13515     int border_element;
13516
13517     if (!IN_LEV_FIELD(xx, yy))
13518       continue;
13519
13520     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13521       continue;                 // center and border element not connected
13522
13523     border_element = Tile[xx][yy];
13524
13525     // check for change of center element (but change it only once)
13526     if (CheckElementChangeBySide(x, y, center_element, border_element,
13527                                  CE_NEXT_TO_X, border_side))
13528       break;
13529   }
13530 }
13531
13532 void TestIfElementTouchesCustomElement(int x, int y)
13533 {
13534   static int xy[4][2] =
13535   {
13536     { 0, -1 },
13537     { -1, 0 },
13538     { +1, 0 },
13539     { 0, +1 }
13540   };
13541   static int trigger_sides[4][2] =
13542   {
13543     // center side      border side
13544     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13545     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13546     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13547     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13548   };
13549   static int touch_dir[4] =
13550   {
13551     MV_LEFT | MV_RIGHT,
13552     MV_UP   | MV_DOWN,
13553     MV_UP   | MV_DOWN,
13554     MV_LEFT | MV_RIGHT
13555   };
13556   boolean change_center_element = FALSE;
13557   int center_element = Tile[x][y];      // should always be non-moving!
13558   int border_element_old[NUM_DIRECTIONS];
13559   int i;
13560
13561   for (i = 0; i < NUM_DIRECTIONS; i++)
13562   {
13563     int xx = x + xy[i][0];
13564     int yy = y + xy[i][1];
13565     int border_element;
13566
13567     border_element_old[i] = -1;
13568
13569     if (!IN_LEV_FIELD(xx, yy))
13570       continue;
13571
13572     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13573       border_element = Tile[xx][yy];    // may be moving!
13574     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13575       border_element = Tile[xx][yy];
13576     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13577       border_element = MovingOrBlocked2Element(xx, yy);
13578     else
13579       continue;                 // center and border element do not touch
13580
13581     border_element_old[i] = border_element;
13582   }
13583
13584   for (i = 0; i < NUM_DIRECTIONS; i++)
13585   {
13586     int xx = x + xy[i][0];
13587     int yy = y + xy[i][1];
13588     int center_side = trigger_sides[i][0];
13589     int border_element = border_element_old[i];
13590
13591     if (border_element == -1)
13592       continue;
13593
13594     // check for change of border element
13595     CheckElementChangeBySide(xx, yy, border_element, center_element,
13596                              CE_TOUCHING_X, center_side);
13597
13598     // (center element cannot be player, so we dont have to check this here)
13599   }
13600
13601   for (i = 0; i < NUM_DIRECTIONS; i++)
13602   {
13603     int xx = x + xy[i][0];
13604     int yy = y + xy[i][1];
13605     int border_side = trigger_sides[i][1];
13606     int border_element = border_element_old[i];
13607
13608     if (border_element == -1)
13609       continue;
13610
13611     // check for change of center element (but change it only once)
13612     if (!change_center_element)
13613       change_center_element =
13614         CheckElementChangeBySide(x, y, center_element, border_element,
13615                                  CE_TOUCHING_X, border_side);
13616
13617     if (IS_PLAYER(xx, yy))
13618     {
13619       /* use player element that is initially defined in the level playfield,
13620          not the player element that corresponds to the runtime player number
13621          (example: a level that contains EL_PLAYER_3 as the only player would
13622          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13623       int player_element = PLAYERINFO(xx, yy)->initial_element;
13624
13625       CheckElementChangeBySide(x, y, center_element, player_element,
13626                                CE_TOUCHING_X, border_side);
13627     }
13628   }
13629 }
13630
13631 void TestIfElementHitsCustomElement(int x, int y, int direction)
13632 {
13633   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13634   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13635   int hitx = x + dx, hity = y + dy;
13636   int hitting_element = Tile[x][y];
13637   int touched_element;
13638
13639   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13640     return;
13641
13642   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13643                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13644
13645   if (IN_LEV_FIELD(hitx, hity))
13646   {
13647     int opposite_direction = MV_DIR_OPPOSITE(direction);
13648     int hitting_side = direction;
13649     int touched_side = opposite_direction;
13650     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13651                           MovDir[hitx][hity] != direction ||
13652                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13653
13654     object_hit = TRUE;
13655
13656     if (object_hit)
13657     {
13658       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13659                                CE_HITTING_X, touched_side);
13660
13661       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13662                                CE_HIT_BY_X, hitting_side);
13663
13664       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13665                                CE_HIT_BY_SOMETHING, opposite_direction);
13666
13667       if (IS_PLAYER(hitx, hity))
13668       {
13669         /* use player element that is initially defined in the level playfield,
13670            not the player element that corresponds to the runtime player number
13671            (example: a level that contains EL_PLAYER_3 as the only player would
13672            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13673         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13674
13675         CheckElementChangeBySide(x, y, hitting_element, player_element,
13676                                  CE_HITTING_X, touched_side);
13677       }
13678     }
13679   }
13680
13681   // "hitting something" is also true when hitting the playfield border
13682   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13683                            CE_HITTING_SOMETHING, direction);
13684 }
13685
13686 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13687 {
13688   int i, kill_x = -1, kill_y = -1;
13689
13690   int bad_element = -1;
13691   static int test_xy[4][2] =
13692   {
13693     { 0, -1 },
13694     { -1, 0 },
13695     { +1, 0 },
13696     { 0, +1 }
13697   };
13698   static int test_dir[4] =
13699   {
13700     MV_UP,
13701     MV_LEFT,
13702     MV_RIGHT,
13703     MV_DOWN
13704   };
13705
13706   for (i = 0; i < NUM_DIRECTIONS; i++)
13707   {
13708     int test_x, test_y, test_move_dir, test_element;
13709
13710     test_x = good_x + test_xy[i][0];
13711     test_y = good_y + test_xy[i][1];
13712
13713     if (!IN_LEV_FIELD(test_x, test_y))
13714       continue;
13715
13716     test_move_dir =
13717       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13718
13719     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13720
13721     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13722        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13723     */
13724     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13725         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13726     {
13727       kill_x = test_x;
13728       kill_y = test_y;
13729       bad_element = test_element;
13730
13731       break;
13732     }
13733   }
13734
13735   if (kill_x != -1 || kill_y != -1)
13736   {
13737     if (IS_PLAYER(good_x, good_y))
13738     {
13739       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13740
13741       if (player->shield_deadly_time_left > 0 &&
13742           !IS_INDESTRUCTIBLE(bad_element))
13743         Bang(kill_x, kill_y);
13744       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13745         KillPlayer(player);
13746     }
13747     else
13748       Bang(good_x, good_y);
13749   }
13750 }
13751
13752 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13753 {
13754   int i, kill_x = -1, kill_y = -1;
13755   int bad_element = Tile[bad_x][bad_y];
13756   static int test_xy[4][2] =
13757   {
13758     { 0, -1 },
13759     { -1, 0 },
13760     { +1, 0 },
13761     { 0, +1 }
13762   };
13763   static int touch_dir[4] =
13764   {
13765     MV_LEFT | MV_RIGHT,
13766     MV_UP   | MV_DOWN,
13767     MV_UP   | MV_DOWN,
13768     MV_LEFT | MV_RIGHT
13769   };
13770   static int test_dir[4] =
13771   {
13772     MV_UP,
13773     MV_LEFT,
13774     MV_RIGHT,
13775     MV_DOWN
13776   };
13777
13778   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13779     return;
13780
13781   for (i = 0; i < NUM_DIRECTIONS; i++)
13782   {
13783     int test_x, test_y, test_move_dir, test_element;
13784
13785     test_x = bad_x + test_xy[i][0];
13786     test_y = bad_y + test_xy[i][1];
13787
13788     if (!IN_LEV_FIELD(test_x, test_y))
13789       continue;
13790
13791     test_move_dir =
13792       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13793
13794     test_element = Tile[test_x][test_y];
13795
13796     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13797        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13798     */
13799     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13800         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13801     {
13802       // good thing is player or penguin that does not move away
13803       if (IS_PLAYER(test_x, test_y))
13804       {
13805         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13806
13807         if (bad_element == EL_ROBOT && player->is_moving)
13808           continue;     // robot does not kill player if he is moving
13809
13810         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13811         {
13812           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13813             continue;           // center and border element do not touch
13814         }
13815
13816         kill_x = test_x;
13817         kill_y = test_y;
13818
13819         break;
13820       }
13821       else if (test_element == EL_PENGUIN)
13822       {
13823         kill_x = test_x;
13824         kill_y = test_y;
13825
13826         break;
13827       }
13828     }
13829   }
13830
13831   if (kill_x != -1 || kill_y != -1)
13832   {
13833     if (IS_PLAYER(kill_x, kill_y))
13834     {
13835       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13836
13837       if (player->shield_deadly_time_left > 0 &&
13838           !IS_INDESTRUCTIBLE(bad_element))
13839         Bang(bad_x, bad_y);
13840       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13841         KillPlayer(player);
13842     }
13843     else
13844       Bang(kill_x, kill_y);
13845   }
13846 }
13847
13848 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13849 {
13850   int bad_element = Tile[bad_x][bad_y];
13851   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13852   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13853   int test_x = bad_x + dx, test_y = bad_y + dy;
13854   int test_move_dir, test_element;
13855   int kill_x = -1, kill_y = -1;
13856
13857   if (!IN_LEV_FIELD(test_x, test_y))
13858     return;
13859
13860   test_move_dir =
13861     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13862
13863   test_element = Tile[test_x][test_y];
13864
13865   if (test_move_dir != bad_move_dir)
13866   {
13867     // good thing can be player or penguin that does not move away
13868     if (IS_PLAYER(test_x, test_y))
13869     {
13870       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13871
13872       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13873          player as being hit when he is moving towards the bad thing, because
13874          the "get hit by" condition would be lost after the player stops) */
13875       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13876         return;         // player moves away from bad thing
13877
13878       kill_x = test_x;
13879       kill_y = test_y;
13880     }
13881     else if (test_element == EL_PENGUIN)
13882     {
13883       kill_x = test_x;
13884       kill_y = test_y;
13885     }
13886   }
13887
13888   if (kill_x != -1 || kill_y != -1)
13889   {
13890     if (IS_PLAYER(kill_x, kill_y))
13891     {
13892       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13893
13894       if (player->shield_deadly_time_left > 0 &&
13895           !IS_INDESTRUCTIBLE(bad_element))
13896         Bang(bad_x, bad_y);
13897       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13898         KillPlayer(player);
13899     }
13900     else
13901       Bang(kill_x, kill_y);
13902   }
13903 }
13904
13905 void TestIfPlayerTouchesBadThing(int x, int y)
13906 {
13907   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13908 }
13909
13910 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13911 {
13912   TestIfGoodThingHitsBadThing(x, y, move_dir);
13913 }
13914
13915 void TestIfBadThingTouchesPlayer(int x, int y)
13916 {
13917   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13918 }
13919
13920 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13921 {
13922   TestIfBadThingHitsGoodThing(x, y, move_dir);
13923 }
13924
13925 void TestIfFriendTouchesBadThing(int x, int y)
13926 {
13927   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13928 }
13929
13930 void TestIfBadThingTouchesFriend(int x, int y)
13931 {
13932   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13933 }
13934
13935 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13936 {
13937   int i, kill_x = bad_x, kill_y = bad_y;
13938   static int xy[4][2] =
13939   {
13940     { 0, -1 },
13941     { -1, 0 },
13942     { +1, 0 },
13943     { 0, +1 }
13944   };
13945
13946   for (i = 0; i < NUM_DIRECTIONS; i++)
13947   {
13948     int x, y, element;
13949
13950     x = bad_x + xy[i][0];
13951     y = bad_y + xy[i][1];
13952     if (!IN_LEV_FIELD(x, y))
13953       continue;
13954
13955     element = Tile[x][y];
13956     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13957         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13958     {
13959       kill_x = x;
13960       kill_y = y;
13961       break;
13962     }
13963   }
13964
13965   if (kill_x != bad_x || kill_y != bad_y)
13966     Bang(bad_x, bad_y);
13967 }
13968
13969 void KillPlayer(struct PlayerInfo *player)
13970 {
13971   int jx = player->jx, jy = player->jy;
13972
13973   if (!player->active)
13974     return;
13975
13976 #if 0
13977   Debug("game:playing:KillPlayer",
13978         "0: killed == %d, active == %d, reanimated == %d",
13979         player->killed, player->active, player->reanimated);
13980 #endif
13981
13982   /* the following code was introduced to prevent an infinite loop when calling
13983      -> Bang()
13984      -> CheckTriggeredElementChangeExt()
13985      -> ExecuteCustomElementAction()
13986      -> KillPlayer()
13987      -> (infinitely repeating the above sequence of function calls)
13988      which occurs when killing the player while having a CE with the setting
13989      "kill player X when explosion of <player X>"; the solution using a new
13990      field "player->killed" was chosen for backwards compatibility, although
13991      clever use of the fields "player->active" etc. would probably also work */
13992 #if 1
13993   if (player->killed)
13994     return;
13995 #endif
13996
13997   player->killed = TRUE;
13998
13999   // remove accessible field at the player's position
14000   Tile[jx][jy] = EL_EMPTY;
14001
14002   // deactivate shield (else Bang()/Explode() would not work right)
14003   player->shield_normal_time_left = 0;
14004   player->shield_deadly_time_left = 0;
14005
14006 #if 0
14007   Debug("game:playing:KillPlayer",
14008         "1: killed == %d, active == %d, reanimated == %d",
14009         player->killed, player->active, player->reanimated);
14010 #endif
14011
14012   Bang(jx, jy);
14013
14014 #if 0
14015   Debug("game:playing:KillPlayer",
14016         "2: killed == %d, active == %d, reanimated == %d",
14017         player->killed, player->active, player->reanimated);
14018 #endif
14019
14020   if (player->reanimated)       // killed player may have been reanimated
14021     player->killed = player->reanimated = FALSE;
14022   else
14023     BuryPlayer(player);
14024 }
14025
14026 static void KillPlayerUnlessEnemyProtected(int x, int y)
14027 {
14028   if (!PLAYER_ENEMY_PROTECTED(x, y))
14029     KillPlayer(PLAYERINFO(x, y));
14030 }
14031
14032 static void KillPlayerUnlessExplosionProtected(int x, int y)
14033 {
14034   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14035     KillPlayer(PLAYERINFO(x, y));
14036 }
14037
14038 void BuryPlayer(struct PlayerInfo *player)
14039 {
14040   int jx = player->jx, jy = player->jy;
14041
14042   if (!player->active)
14043     return;
14044
14045   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14046   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14047
14048   RemovePlayer(player);
14049
14050   player->buried = TRUE;
14051
14052   if (game.all_players_gone)
14053     game.GameOver = TRUE;
14054 }
14055
14056 void RemovePlayer(struct PlayerInfo *player)
14057 {
14058   int jx = player->jx, jy = player->jy;
14059   int i, found = FALSE;
14060
14061   player->present = FALSE;
14062   player->active = FALSE;
14063
14064   // required for some CE actions (even if the player is not active anymore)
14065   player->MovPos = 0;
14066
14067   if (!ExplodeField[jx][jy])
14068     StorePlayer[jx][jy] = 0;
14069
14070   if (player->is_moving)
14071     TEST_DrawLevelField(player->last_jx, player->last_jy);
14072
14073   for (i = 0; i < MAX_PLAYERS; i++)
14074     if (stored_player[i].active)
14075       found = TRUE;
14076
14077   if (!found)
14078   {
14079     game.all_players_gone = TRUE;
14080     game.GameOver = TRUE;
14081   }
14082
14083   game.exit_x = game.robot_wheel_x = jx;
14084   game.exit_y = game.robot_wheel_y = jy;
14085 }
14086
14087 void ExitPlayer(struct PlayerInfo *player)
14088 {
14089   DrawPlayer(player);   // needed here only to cleanup last field
14090   RemovePlayer(player);
14091
14092   if (game.players_still_needed > 0)
14093     game.players_still_needed--;
14094 }
14095
14096 static void SetFieldForSnapping(int x, int y, int element, int direction,
14097                                 int player_index_bit)
14098 {
14099   struct ElementInfo *ei = &element_info[element];
14100   int direction_bit = MV_DIR_TO_BIT(direction);
14101   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14102   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14103                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14104
14105   Tile[x][y] = EL_ELEMENT_SNAPPING;
14106   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14107   MovDir[x][y] = direction;
14108   Store[x][y] = element;
14109   Store2[x][y] = player_index_bit;
14110
14111   ResetGfxAnimation(x, y);
14112
14113   GfxElement[x][y] = element;
14114   GfxAction[x][y] = action;
14115   GfxDir[x][y] = direction;
14116   GfxFrame[x][y] = -1;
14117 }
14118
14119 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14120                                    int player_index_bit)
14121 {
14122   TestIfElementTouchesCustomElement(x, y);      // for empty space
14123
14124   if (level.finish_dig_collect)
14125   {
14126     int dig_side = MV_DIR_OPPOSITE(direction);
14127     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14128                         CE_PLAYER_COLLECTS_X);
14129
14130     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14131                                         player_index_bit, dig_side);
14132     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14133                                         player_index_bit, dig_side);
14134   }
14135 }
14136
14137 /*
14138   =============================================================================
14139   checkDiagonalPushing()
14140   -----------------------------------------------------------------------------
14141   check if diagonal input device direction results in pushing of object
14142   (by checking if the alternative direction is walkable, diggable, ...)
14143   =============================================================================
14144 */
14145
14146 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14147                                     int x, int y, int real_dx, int real_dy)
14148 {
14149   int jx, jy, dx, dy, xx, yy;
14150
14151   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14152     return TRUE;
14153
14154   // diagonal direction: check alternative direction
14155   jx = player->jx;
14156   jy = player->jy;
14157   dx = x - jx;
14158   dy = y - jy;
14159   xx = jx + (dx == 0 ? real_dx : 0);
14160   yy = jy + (dy == 0 ? real_dy : 0);
14161
14162   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14163 }
14164
14165 /*
14166   =============================================================================
14167   DigField()
14168   -----------------------------------------------------------------------------
14169   x, y:                 field next to player (non-diagonal) to try to dig to
14170   real_dx, real_dy:     direction as read from input device (can be diagonal)
14171   =============================================================================
14172 */
14173
14174 static int DigField(struct PlayerInfo *player,
14175                     int oldx, int oldy, int x, int y,
14176                     int real_dx, int real_dy, int mode)
14177 {
14178   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14179   boolean player_was_pushing = player->is_pushing;
14180   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14181   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14182   int jx = oldx, jy = oldy;
14183   int dx = x - jx, dy = y - jy;
14184   int nextx = x + dx, nexty = y + dy;
14185   int move_direction = (dx == -1 ? MV_LEFT  :
14186                         dx == +1 ? MV_RIGHT :
14187                         dy == -1 ? MV_UP    :
14188                         dy == +1 ? MV_DOWN  : MV_NONE);
14189   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14190   int dig_side = MV_DIR_OPPOSITE(move_direction);
14191   int old_element = Tile[jx][jy];
14192   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14193   int collect_count;
14194
14195   if (is_player)                // function can also be called by EL_PENGUIN
14196   {
14197     if (player->MovPos == 0)
14198     {
14199       player->is_digging = FALSE;
14200       player->is_collecting = FALSE;
14201     }
14202
14203     if (player->MovPos == 0)    // last pushing move finished
14204       player->is_pushing = FALSE;
14205
14206     if (mode == DF_NO_PUSH)     // player just stopped pushing
14207     {
14208       player->is_switching = FALSE;
14209       player->push_delay = -1;
14210
14211       return MP_NO_ACTION;
14212     }
14213   }
14214   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14215     old_element = Back[jx][jy];
14216
14217   // in case of element dropped at player position, check background
14218   else if (Back[jx][jy] != EL_EMPTY &&
14219            game.engine_version >= VERSION_IDENT(2,2,0,0))
14220     old_element = Back[jx][jy];
14221
14222   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14223     return MP_NO_ACTION;        // field has no opening in this direction
14224
14225   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14226     return MP_NO_ACTION;        // field has no opening in this direction
14227
14228   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14229   {
14230     SplashAcid(x, y);
14231
14232     Tile[jx][jy] = player->artwork_element;
14233     InitMovingField(jx, jy, MV_DOWN);
14234     Store[jx][jy] = EL_ACID;
14235     ContinueMoving(jx, jy);
14236     BuryPlayer(player);
14237
14238     return MP_DONT_RUN_INTO;
14239   }
14240
14241   if (player_can_move && DONT_RUN_INTO(element))
14242   {
14243     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14244
14245     return MP_DONT_RUN_INTO;
14246   }
14247
14248   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14249     return MP_NO_ACTION;
14250
14251   collect_count = element_info[element].collect_count_initial;
14252
14253   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14254     return MP_NO_ACTION;
14255
14256   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14257     player_can_move = player_can_move_or_snap;
14258
14259   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14260       game.engine_version >= VERSION_IDENT(2,2,0,0))
14261   {
14262     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14263                                player->index_bit, dig_side);
14264     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14265                                         player->index_bit, dig_side);
14266
14267     if (element == EL_DC_LANDMINE)
14268       Bang(x, y);
14269
14270     if (Tile[x][y] != element)          // field changed by snapping
14271       return MP_ACTION;
14272
14273     return MP_NO_ACTION;
14274   }
14275
14276   if (player->gravity && is_player && !player->is_auto_moving &&
14277       canFallDown(player) && move_direction != MV_DOWN &&
14278       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14279     return MP_NO_ACTION;        // player cannot walk here due to gravity
14280
14281   if (player_can_move &&
14282       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14283   {
14284     int sound_element = SND_ELEMENT(element);
14285     int sound_action = ACTION_WALKING;
14286
14287     if (IS_RND_GATE(element))
14288     {
14289       if (!player->key[RND_GATE_NR(element)])
14290         return MP_NO_ACTION;
14291     }
14292     else if (IS_RND_GATE_GRAY(element))
14293     {
14294       if (!player->key[RND_GATE_GRAY_NR(element)])
14295         return MP_NO_ACTION;
14296     }
14297     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14298     {
14299       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14300         return MP_NO_ACTION;
14301     }
14302     else if (element == EL_EXIT_OPEN ||
14303              element == EL_EM_EXIT_OPEN ||
14304              element == EL_EM_EXIT_OPENING ||
14305              element == EL_STEEL_EXIT_OPEN ||
14306              element == EL_EM_STEEL_EXIT_OPEN ||
14307              element == EL_EM_STEEL_EXIT_OPENING ||
14308              element == EL_SP_EXIT_OPEN ||
14309              element == EL_SP_EXIT_OPENING)
14310     {
14311       sound_action = ACTION_PASSING;    // player is passing exit
14312     }
14313     else if (element == EL_EMPTY)
14314     {
14315       sound_action = ACTION_MOVING;             // nothing to walk on
14316     }
14317
14318     // play sound from background or player, whatever is available
14319     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14320       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14321     else
14322       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14323   }
14324   else if (player_can_move &&
14325            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14326   {
14327     if (!ACCESS_FROM(element, opposite_direction))
14328       return MP_NO_ACTION;      // field not accessible from this direction
14329
14330     if (CAN_MOVE(element))      // only fixed elements can be passed!
14331       return MP_NO_ACTION;
14332
14333     if (IS_EM_GATE(element))
14334     {
14335       if (!player->key[EM_GATE_NR(element)])
14336         return MP_NO_ACTION;
14337     }
14338     else if (IS_EM_GATE_GRAY(element))
14339     {
14340       if (!player->key[EM_GATE_GRAY_NR(element)])
14341         return MP_NO_ACTION;
14342     }
14343     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14344     {
14345       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14346         return MP_NO_ACTION;
14347     }
14348     else if (IS_EMC_GATE(element))
14349     {
14350       if (!player->key[EMC_GATE_NR(element)])
14351         return MP_NO_ACTION;
14352     }
14353     else if (IS_EMC_GATE_GRAY(element))
14354     {
14355       if (!player->key[EMC_GATE_GRAY_NR(element)])
14356         return MP_NO_ACTION;
14357     }
14358     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14359     {
14360       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14361         return MP_NO_ACTION;
14362     }
14363     else if (element == EL_DC_GATE_WHITE ||
14364              element == EL_DC_GATE_WHITE_GRAY ||
14365              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14366     {
14367       if (player->num_white_keys == 0)
14368         return MP_NO_ACTION;
14369
14370       player->num_white_keys--;
14371     }
14372     else if (IS_SP_PORT(element))
14373     {
14374       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14375           element == EL_SP_GRAVITY_PORT_RIGHT ||
14376           element == EL_SP_GRAVITY_PORT_UP ||
14377           element == EL_SP_GRAVITY_PORT_DOWN)
14378         player->gravity = !player->gravity;
14379       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14380                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14381                element == EL_SP_GRAVITY_ON_PORT_UP ||
14382                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14383         player->gravity = TRUE;
14384       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14385                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14386                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14387                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14388         player->gravity = FALSE;
14389     }
14390
14391     // automatically move to the next field with double speed
14392     player->programmed_action = move_direction;
14393
14394     if (player->move_delay_reset_counter == 0)
14395     {
14396       player->move_delay_reset_counter = 2;     // two double speed steps
14397
14398       DOUBLE_PLAYER_SPEED(player);
14399     }
14400
14401     PlayLevelSoundAction(x, y, ACTION_PASSING);
14402   }
14403   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14404   {
14405     RemoveField(x, y);
14406
14407     if (mode != DF_SNAP)
14408     {
14409       GfxElement[x][y] = GFX_ELEMENT(element);
14410       player->is_digging = TRUE;
14411     }
14412
14413     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14414
14415     // use old behaviour for old levels (digging)
14416     if (!level.finish_dig_collect)
14417     {
14418       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14419                                           player->index_bit, dig_side);
14420
14421       // if digging triggered player relocation, finish digging tile
14422       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14423         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14424     }
14425
14426     if (mode == DF_SNAP)
14427     {
14428       if (level.block_snap_field)
14429         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14430       else
14431         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14432
14433       // use old behaviour for old levels (snapping)
14434       if (!level.finish_dig_collect)
14435         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14436                                             player->index_bit, dig_side);
14437     }
14438   }
14439   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14440   {
14441     RemoveField(x, y);
14442
14443     if (is_player && mode != DF_SNAP)
14444     {
14445       GfxElement[x][y] = element;
14446       player->is_collecting = TRUE;
14447     }
14448
14449     if (element == EL_SPEED_PILL)
14450     {
14451       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14452     }
14453     else if (element == EL_EXTRA_TIME && level.time > 0)
14454     {
14455       TimeLeft += level.extra_time;
14456
14457       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14458
14459       DisplayGameControlValues();
14460     }
14461     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14462     {
14463       player->shield_normal_time_left += level.shield_normal_time;
14464       if (element == EL_SHIELD_DEADLY)
14465         player->shield_deadly_time_left += level.shield_deadly_time;
14466     }
14467     else if (element == EL_DYNAMITE ||
14468              element == EL_EM_DYNAMITE ||
14469              element == EL_SP_DISK_RED)
14470     {
14471       if (player->inventory_size < MAX_INVENTORY_SIZE)
14472         player->inventory_element[player->inventory_size++] = element;
14473
14474       DrawGameDoorValues();
14475     }
14476     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14477     {
14478       player->dynabomb_count++;
14479       player->dynabombs_left++;
14480     }
14481     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14482     {
14483       player->dynabomb_size++;
14484     }
14485     else if (element == EL_DYNABOMB_INCREASE_POWER)
14486     {
14487       player->dynabomb_xl = TRUE;
14488     }
14489     else if (IS_KEY(element))
14490     {
14491       player->key[KEY_NR(element)] = TRUE;
14492
14493       DrawGameDoorValues();
14494     }
14495     else if (element == EL_DC_KEY_WHITE)
14496     {
14497       player->num_white_keys++;
14498
14499       // display white keys?
14500       // DrawGameDoorValues();
14501     }
14502     else if (IS_ENVELOPE(element))
14503     {
14504       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14505
14506       if (!wait_for_snapping)
14507         player->show_envelope = element;
14508     }
14509     else if (element == EL_EMC_LENSES)
14510     {
14511       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14512
14513       RedrawAllInvisibleElementsForLenses();
14514     }
14515     else if (element == EL_EMC_MAGNIFIER)
14516     {
14517       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14518
14519       RedrawAllInvisibleElementsForMagnifier();
14520     }
14521     else if (IS_DROPPABLE(element) ||
14522              IS_THROWABLE(element))     // can be collected and dropped
14523     {
14524       int i;
14525
14526       if (collect_count == 0)
14527         player->inventory_infinite_element = element;
14528       else
14529         for (i = 0; i < collect_count; i++)
14530           if (player->inventory_size < MAX_INVENTORY_SIZE)
14531             player->inventory_element[player->inventory_size++] = element;
14532
14533       DrawGameDoorValues();
14534     }
14535     else if (collect_count > 0)
14536     {
14537       game.gems_still_needed -= collect_count;
14538       if (game.gems_still_needed < 0)
14539         game.gems_still_needed = 0;
14540
14541       game.snapshot.collected_item = TRUE;
14542
14543       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14544
14545       DisplayGameControlValues();
14546     }
14547
14548     RaiseScoreElement(element);
14549     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14550
14551     // use old behaviour for old levels (collecting)
14552     if (!level.finish_dig_collect && is_player)
14553     {
14554       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14555                                           player->index_bit, dig_side);
14556
14557       // if collecting triggered player relocation, finish collecting tile
14558       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14559         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14560     }
14561
14562     if (mode == DF_SNAP)
14563     {
14564       if (level.block_snap_field)
14565         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14566       else
14567         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14568
14569       // use old behaviour for old levels (snapping)
14570       if (!level.finish_dig_collect)
14571         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14572                                             player->index_bit, dig_side);
14573     }
14574   }
14575   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14576   {
14577     if (mode == DF_SNAP && element != EL_BD_ROCK)
14578       return MP_NO_ACTION;
14579
14580     if (CAN_FALL(element) && dy)
14581       return MP_NO_ACTION;
14582
14583     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14584         !(element == EL_SPRING && level.use_spring_bug))
14585       return MP_NO_ACTION;
14586
14587     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14588         ((move_direction & MV_VERTICAL &&
14589           ((element_info[element].move_pattern & MV_LEFT &&
14590             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14591            (element_info[element].move_pattern & MV_RIGHT &&
14592             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14593          (move_direction & MV_HORIZONTAL &&
14594           ((element_info[element].move_pattern & MV_UP &&
14595             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14596            (element_info[element].move_pattern & MV_DOWN &&
14597             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14598       return MP_NO_ACTION;
14599
14600     // do not push elements already moving away faster than player
14601     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14602         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14603       return MP_NO_ACTION;
14604
14605     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14606     {
14607       if (player->push_delay_value == -1 || !player_was_pushing)
14608         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14609     }
14610     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14611     {
14612       if (player->push_delay_value == -1)
14613         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14614     }
14615     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14616     {
14617       if (!player->is_pushing)
14618         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14619     }
14620
14621     player->is_pushing = TRUE;
14622     player->is_active = TRUE;
14623
14624     if (!(IN_LEV_FIELD(nextx, nexty) &&
14625           (IS_FREE(nextx, nexty) ||
14626            (IS_SB_ELEMENT(element) &&
14627             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14628            (IS_CUSTOM_ELEMENT(element) &&
14629             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14630       return MP_NO_ACTION;
14631
14632     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14633       return MP_NO_ACTION;
14634
14635     if (player->push_delay == -1)       // new pushing; restart delay
14636       player->push_delay = 0;
14637
14638     if (player->push_delay < player->push_delay_value &&
14639         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14640         element != EL_SPRING && element != EL_BALLOON)
14641     {
14642       // make sure that there is no move delay before next try to push
14643       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14644         player->move_delay = 0;
14645
14646       return MP_NO_ACTION;
14647     }
14648
14649     if (IS_CUSTOM_ELEMENT(element) &&
14650         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14651     {
14652       if (!DigFieldByCE(nextx, nexty, element))
14653         return MP_NO_ACTION;
14654     }
14655
14656     if (IS_SB_ELEMENT(element))
14657     {
14658       boolean sokoban_task_solved = FALSE;
14659
14660       if (element == EL_SOKOBAN_FIELD_FULL)
14661       {
14662         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14663
14664         IncrementSokobanFieldsNeeded();
14665         IncrementSokobanObjectsNeeded();
14666       }
14667
14668       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14669       {
14670         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14671
14672         DecrementSokobanFieldsNeeded();
14673         DecrementSokobanObjectsNeeded();
14674
14675         // sokoban object was pushed from empty field to sokoban field
14676         if (Back[x][y] == EL_EMPTY)
14677           sokoban_task_solved = TRUE;
14678       }
14679
14680       Tile[x][y] = EL_SOKOBAN_OBJECT;
14681
14682       if (Back[x][y] == Back[nextx][nexty])
14683         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14684       else if (Back[x][y] != 0)
14685         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14686                                     ACTION_EMPTYING);
14687       else
14688         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14689                                     ACTION_FILLING);
14690
14691       if (sokoban_task_solved &&
14692           game.sokoban_fields_still_needed == 0 &&
14693           game.sokoban_objects_still_needed == 0 &&
14694           level.auto_exit_sokoban)
14695       {
14696         game.players_still_needed = 0;
14697
14698         LevelSolved();
14699
14700         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14701       }
14702     }
14703     else
14704       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14705
14706     InitMovingField(x, y, move_direction);
14707     GfxAction[x][y] = ACTION_PUSHING;
14708
14709     if (mode == DF_SNAP)
14710       ContinueMoving(x, y);
14711     else
14712       MovPos[x][y] = (dx != 0 ? dx : dy);
14713
14714     Pushed[x][y] = TRUE;
14715     Pushed[nextx][nexty] = TRUE;
14716
14717     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14718       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14719     else
14720       player->push_delay_value = -1;    // get new value later
14721
14722     // check for element change _after_ element has been pushed
14723     if (game.use_change_when_pushing_bug)
14724     {
14725       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14726                                  player->index_bit, dig_side);
14727       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14728                                           player->index_bit, dig_side);
14729     }
14730   }
14731   else if (IS_SWITCHABLE(element))
14732   {
14733     if (PLAYER_SWITCHING(player, x, y))
14734     {
14735       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14736                                           player->index_bit, dig_side);
14737
14738       return MP_ACTION;
14739     }
14740
14741     player->is_switching = TRUE;
14742     player->switch_x = x;
14743     player->switch_y = y;
14744
14745     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14746
14747     if (element == EL_ROBOT_WHEEL)
14748     {
14749       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14750
14751       game.robot_wheel_x = x;
14752       game.robot_wheel_y = y;
14753       game.robot_wheel_active = TRUE;
14754
14755       TEST_DrawLevelField(x, y);
14756     }
14757     else if (element == EL_SP_TERMINAL)
14758     {
14759       int xx, yy;
14760
14761       SCAN_PLAYFIELD(xx, yy)
14762       {
14763         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14764         {
14765           Bang(xx, yy);
14766         }
14767         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14768         {
14769           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14770
14771           ResetGfxAnimation(xx, yy);
14772           TEST_DrawLevelField(xx, yy);
14773         }
14774       }
14775     }
14776     else if (IS_BELT_SWITCH(element))
14777     {
14778       ToggleBeltSwitch(x, y);
14779     }
14780     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14781              element == EL_SWITCHGATE_SWITCH_DOWN ||
14782              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14783              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14784     {
14785       ToggleSwitchgateSwitch(x, y);
14786     }
14787     else if (element == EL_LIGHT_SWITCH ||
14788              element == EL_LIGHT_SWITCH_ACTIVE)
14789     {
14790       ToggleLightSwitch(x, y);
14791     }
14792     else if (element == EL_TIMEGATE_SWITCH ||
14793              element == EL_DC_TIMEGATE_SWITCH)
14794     {
14795       ActivateTimegateSwitch(x, y);
14796     }
14797     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14798              element == EL_BALLOON_SWITCH_RIGHT ||
14799              element == EL_BALLOON_SWITCH_UP    ||
14800              element == EL_BALLOON_SWITCH_DOWN  ||
14801              element == EL_BALLOON_SWITCH_NONE  ||
14802              element == EL_BALLOON_SWITCH_ANY)
14803     {
14804       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14805                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14806                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14807                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14808                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14809                              move_direction);
14810     }
14811     else if (element == EL_LAMP)
14812     {
14813       Tile[x][y] = EL_LAMP_ACTIVE;
14814       game.lights_still_needed--;
14815
14816       ResetGfxAnimation(x, y);
14817       TEST_DrawLevelField(x, y);
14818     }
14819     else if (element == EL_TIME_ORB_FULL)
14820     {
14821       Tile[x][y] = EL_TIME_ORB_EMPTY;
14822
14823       if (level.time > 0 || level.use_time_orb_bug)
14824       {
14825         TimeLeft += level.time_orb_time;
14826         game.no_time_limit = FALSE;
14827
14828         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14829
14830         DisplayGameControlValues();
14831       }
14832
14833       ResetGfxAnimation(x, y);
14834       TEST_DrawLevelField(x, y);
14835     }
14836     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14837              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14838     {
14839       int xx, yy;
14840
14841       game.ball_active = !game.ball_active;
14842
14843       SCAN_PLAYFIELD(xx, yy)
14844       {
14845         int e = Tile[xx][yy];
14846
14847         if (game.ball_active)
14848         {
14849           if (e == EL_EMC_MAGIC_BALL)
14850             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14851           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14852             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14853         }
14854         else
14855         {
14856           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14857             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14858           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14859             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14860         }
14861       }
14862     }
14863
14864     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14865                                         player->index_bit, dig_side);
14866
14867     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14868                                         player->index_bit, dig_side);
14869
14870     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14871                                         player->index_bit, dig_side);
14872
14873     return MP_ACTION;
14874   }
14875   else
14876   {
14877     if (!PLAYER_SWITCHING(player, x, y))
14878     {
14879       player->is_switching = TRUE;
14880       player->switch_x = x;
14881       player->switch_y = y;
14882
14883       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14884                                  player->index_bit, dig_side);
14885       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14886                                           player->index_bit, dig_side);
14887
14888       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14889                                  player->index_bit, dig_side);
14890       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14891                                           player->index_bit, dig_side);
14892     }
14893
14894     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14895                                player->index_bit, dig_side);
14896     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14897                                         player->index_bit, dig_side);
14898
14899     return MP_NO_ACTION;
14900   }
14901
14902   player->push_delay = -1;
14903
14904   if (is_player)                // function can also be called by EL_PENGUIN
14905   {
14906     if (Tile[x][y] != element)          // really digged/collected something
14907     {
14908       player->is_collecting = !player->is_digging;
14909       player->is_active = TRUE;
14910
14911       player->last_removed_element = element;
14912     }
14913   }
14914
14915   return MP_MOVING;
14916 }
14917
14918 static boolean DigFieldByCE(int x, int y, int digging_element)
14919 {
14920   int element = Tile[x][y];
14921
14922   if (!IS_FREE(x, y))
14923   {
14924     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14925                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14926                   ACTION_BREAKING);
14927
14928     // no element can dig solid indestructible elements
14929     if (IS_INDESTRUCTIBLE(element) &&
14930         !IS_DIGGABLE(element) &&
14931         !IS_COLLECTIBLE(element))
14932       return FALSE;
14933
14934     if (AmoebaNr[x][y] &&
14935         (element == EL_AMOEBA_FULL ||
14936          element == EL_BD_AMOEBA ||
14937          element == EL_AMOEBA_GROWING))
14938     {
14939       AmoebaCnt[AmoebaNr[x][y]]--;
14940       AmoebaCnt2[AmoebaNr[x][y]]--;
14941     }
14942
14943     if (IS_MOVING(x, y))
14944       RemoveMovingField(x, y);
14945     else
14946     {
14947       RemoveField(x, y);
14948       TEST_DrawLevelField(x, y);
14949     }
14950
14951     // if digged element was about to explode, prevent the explosion
14952     ExplodeField[x][y] = EX_TYPE_NONE;
14953
14954     PlayLevelSoundAction(x, y, action);
14955   }
14956
14957   Store[x][y] = EL_EMPTY;
14958
14959   // this makes it possible to leave the removed element again
14960   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14961     Store[x][y] = element;
14962
14963   return TRUE;
14964 }
14965
14966 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14967 {
14968   int jx = player->jx, jy = player->jy;
14969   int x = jx + dx, y = jy + dy;
14970   int snap_direction = (dx == -1 ? MV_LEFT  :
14971                         dx == +1 ? MV_RIGHT :
14972                         dy == -1 ? MV_UP    :
14973                         dy == +1 ? MV_DOWN  : MV_NONE);
14974   boolean can_continue_snapping = (level.continuous_snapping &&
14975                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14976
14977   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14978     return FALSE;
14979
14980   if (!player->active || !IN_LEV_FIELD(x, y))
14981     return FALSE;
14982
14983   if (dx && dy)
14984     return FALSE;
14985
14986   if (!dx && !dy)
14987   {
14988     if (player->MovPos == 0)
14989       player->is_pushing = FALSE;
14990
14991     player->is_snapping = FALSE;
14992
14993     if (player->MovPos == 0)
14994     {
14995       player->is_moving = FALSE;
14996       player->is_digging = FALSE;
14997       player->is_collecting = FALSE;
14998     }
14999
15000     return FALSE;
15001   }
15002
15003   // prevent snapping with already pressed snap key when not allowed
15004   if (player->is_snapping && !can_continue_snapping)
15005     return FALSE;
15006
15007   player->MovDir = snap_direction;
15008
15009   if (player->MovPos == 0)
15010   {
15011     player->is_moving = FALSE;
15012     player->is_digging = FALSE;
15013     player->is_collecting = FALSE;
15014   }
15015
15016   player->is_dropping = FALSE;
15017   player->is_dropping_pressed = FALSE;
15018   player->drop_pressed_delay = 0;
15019
15020   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15021     return FALSE;
15022
15023   player->is_snapping = TRUE;
15024   player->is_active = TRUE;
15025
15026   if (player->MovPos == 0)
15027   {
15028     player->is_moving = FALSE;
15029     player->is_digging = FALSE;
15030     player->is_collecting = FALSE;
15031   }
15032
15033   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15034     TEST_DrawLevelField(player->last_jx, player->last_jy);
15035
15036   TEST_DrawLevelField(x, y);
15037
15038   return TRUE;
15039 }
15040
15041 static boolean DropElement(struct PlayerInfo *player)
15042 {
15043   int old_element, new_element;
15044   int dropx = player->jx, dropy = player->jy;
15045   int drop_direction = player->MovDir;
15046   int drop_side = drop_direction;
15047   int drop_element = get_next_dropped_element(player);
15048
15049   /* do not drop an element on top of another element; when holding drop key
15050      pressed without moving, dropped element must move away before the next
15051      element can be dropped (this is especially important if the next element
15052      is dynamite, which can be placed on background for historical reasons) */
15053   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15054     return MP_ACTION;
15055
15056   if (IS_THROWABLE(drop_element))
15057   {
15058     dropx += GET_DX_FROM_DIR(drop_direction);
15059     dropy += GET_DY_FROM_DIR(drop_direction);
15060
15061     if (!IN_LEV_FIELD(dropx, dropy))
15062       return FALSE;
15063   }
15064
15065   old_element = Tile[dropx][dropy];     // old element at dropping position
15066   new_element = drop_element;           // default: no change when dropping
15067
15068   // check if player is active, not moving and ready to drop
15069   if (!player->active || player->MovPos || player->drop_delay > 0)
15070     return FALSE;
15071
15072   // check if player has anything that can be dropped
15073   if (new_element == EL_UNDEFINED)
15074     return FALSE;
15075
15076   // only set if player has anything that can be dropped
15077   player->is_dropping_pressed = TRUE;
15078
15079   // check if drop key was pressed long enough for EM style dynamite
15080   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15081     return FALSE;
15082
15083   // check if anything can be dropped at the current position
15084   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15085     return FALSE;
15086
15087   // collected custom elements can only be dropped on empty fields
15088   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15089     return FALSE;
15090
15091   if (old_element != EL_EMPTY)
15092     Back[dropx][dropy] = old_element;   // store old element on this field
15093
15094   ResetGfxAnimation(dropx, dropy);
15095   ResetRandomAnimationValue(dropx, dropy);
15096
15097   if (player->inventory_size > 0 ||
15098       player->inventory_infinite_element != EL_UNDEFINED)
15099   {
15100     if (player->inventory_size > 0)
15101     {
15102       player->inventory_size--;
15103
15104       DrawGameDoorValues();
15105
15106       if (new_element == EL_DYNAMITE)
15107         new_element = EL_DYNAMITE_ACTIVE;
15108       else if (new_element == EL_EM_DYNAMITE)
15109         new_element = EL_EM_DYNAMITE_ACTIVE;
15110       else if (new_element == EL_SP_DISK_RED)
15111         new_element = EL_SP_DISK_RED_ACTIVE;
15112     }
15113
15114     Tile[dropx][dropy] = new_element;
15115
15116     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15117       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15118                           el2img(Tile[dropx][dropy]), 0);
15119
15120     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15121
15122     // needed if previous element just changed to "empty" in the last frame
15123     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15124
15125     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15126                                player->index_bit, drop_side);
15127     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15128                                         CE_PLAYER_DROPS_X,
15129                                         player->index_bit, drop_side);
15130
15131     TestIfElementTouchesCustomElement(dropx, dropy);
15132   }
15133   else          // player is dropping a dyna bomb
15134   {
15135     player->dynabombs_left--;
15136
15137     Tile[dropx][dropy] = new_element;
15138
15139     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15140       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15141                           el2img(Tile[dropx][dropy]), 0);
15142
15143     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15144   }
15145
15146   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15147     InitField_WithBug1(dropx, dropy, FALSE);
15148
15149   new_element = Tile[dropx][dropy];     // element might have changed
15150
15151   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15152       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15153   {
15154     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15155       MovDir[dropx][dropy] = drop_direction;
15156
15157     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15158
15159     // do not cause impact style collision by dropping elements that can fall
15160     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15161   }
15162
15163   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15164   player->is_dropping = TRUE;
15165
15166   player->drop_pressed_delay = 0;
15167   player->is_dropping_pressed = FALSE;
15168
15169   player->drop_x = dropx;
15170   player->drop_y = dropy;
15171
15172   return TRUE;
15173 }
15174
15175 // ----------------------------------------------------------------------------
15176 // game sound playing functions
15177 // ----------------------------------------------------------------------------
15178
15179 static int *loop_sound_frame = NULL;
15180 static int *loop_sound_volume = NULL;
15181
15182 void InitPlayLevelSound(void)
15183 {
15184   int num_sounds = getSoundListSize();
15185
15186   checked_free(loop_sound_frame);
15187   checked_free(loop_sound_volume);
15188
15189   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15190   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15191 }
15192
15193 static void PlayLevelSound(int x, int y, int nr)
15194 {
15195   int sx = SCREENX(x), sy = SCREENY(y);
15196   int volume, stereo_position;
15197   int max_distance = 8;
15198   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15199
15200   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15201       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15202     return;
15203
15204   if (!IN_LEV_FIELD(x, y) ||
15205       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15206       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15207     return;
15208
15209   volume = SOUND_MAX_VOLUME;
15210
15211   if (!IN_SCR_FIELD(sx, sy))
15212   {
15213     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15214     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15215
15216     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15217   }
15218
15219   stereo_position = (SOUND_MAX_LEFT +
15220                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15221                      (SCR_FIELDX + 2 * max_distance));
15222
15223   if (IS_LOOP_SOUND(nr))
15224   {
15225     /* This assures that quieter loop sounds do not overwrite louder ones,
15226        while restarting sound volume comparison with each new game frame. */
15227
15228     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15229       return;
15230
15231     loop_sound_volume[nr] = volume;
15232     loop_sound_frame[nr] = FrameCounter;
15233   }
15234
15235   PlaySoundExt(nr, volume, stereo_position, type);
15236 }
15237
15238 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15239 {
15240   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15241                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15242                  y < LEVELY(BY1) ? LEVELY(BY1) :
15243                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15244                  sound_action);
15245 }
15246
15247 static void PlayLevelSoundAction(int x, int y, int action)
15248 {
15249   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15250 }
15251
15252 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15253 {
15254   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15255
15256   if (sound_effect != SND_UNDEFINED)
15257     PlayLevelSound(x, y, sound_effect);
15258 }
15259
15260 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15261                                               int action)
15262 {
15263   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15264
15265   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15266     PlayLevelSound(x, y, sound_effect);
15267 }
15268
15269 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15270 {
15271   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15272
15273   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15274     PlayLevelSound(x, y, sound_effect);
15275 }
15276
15277 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15278 {
15279   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15280
15281   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15282     StopSound(sound_effect);
15283 }
15284
15285 static int getLevelMusicNr(void)
15286 {
15287   if (levelset.music[level_nr] != MUS_UNDEFINED)
15288     return levelset.music[level_nr];            // from config file
15289   else
15290     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15291 }
15292
15293 static void FadeLevelSounds(void)
15294 {
15295   FadeSounds();
15296 }
15297
15298 static void FadeLevelMusic(void)
15299 {
15300   int music_nr = getLevelMusicNr();
15301   char *curr_music = getCurrentlyPlayingMusicFilename();
15302   char *next_music = getMusicInfoEntryFilename(music_nr);
15303
15304   if (!strEqual(curr_music, next_music))
15305     FadeMusic();
15306 }
15307
15308 void FadeLevelSoundsAndMusic(void)
15309 {
15310   FadeLevelSounds();
15311   FadeLevelMusic();
15312 }
15313
15314 static void PlayLevelMusic(void)
15315 {
15316   int music_nr = getLevelMusicNr();
15317   char *curr_music = getCurrentlyPlayingMusicFilename();
15318   char *next_music = getMusicInfoEntryFilename(music_nr);
15319
15320   if (!strEqual(curr_music, next_music))
15321     PlayMusicLoop(music_nr);
15322 }
15323
15324 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15325 {
15326   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15327   int offset = 0;
15328   int x = xx - offset;
15329   int y = yy - offset;
15330
15331   switch (sample)
15332   {
15333     case SOUND_blank:
15334       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15335       break;
15336
15337     case SOUND_roll:
15338       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15339       break;
15340
15341     case SOUND_stone:
15342       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15343       break;
15344
15345     case SOUND_nut:
15346       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15347       break;
15348
15349     case SOUND_crack:
15350       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15351       break;
15352
15353     case SOUND_bug:
15354       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15355       break;
15356
15357     case SOUND_tank:
15358       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15359       break;
15360
15361     case SOUND_android_clone:
15362       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15363       break;
15364
15365     case SOUND_android_move:
15366       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15367       break;
15368
15369     case SOUND_spring:
15370       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15371       break;
15372
15373     case SOUND_slurp:
15374       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15375       break;
15376
15377     case SOUND_eater:
15378       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15379       break;
15380
15381     case SOUND_eater_eat:
15382       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15383       break;
15384
15385     case SOUND_alien:
15386       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15387       break;
15388
15389     case SOUND_collect:
15390       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15391       break;
15392
15393     case SOUND_diamond:
15394       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15395       break;
15396
15397     case SOUND_squash:
15398       // !!! CHECK THIS !!!
15399 #if 1
15400       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15401 #else
15402       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15403 #endif
15404       break;
15405
15406     case SOUND_wonderfall:
15407       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15408       break;
15409
15410     case SOUND_drip:
15411       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15412       break;
15413
15414     case SOUND_push:
15415       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15416       break;
15417
15418     case SOUND_dirt:
15419       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15420       break;
15421
15422     case SOUND_acid:
15423       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15424       break;
15425
15426     case SOUND_ball:
15427       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15428       break;
15429
15430     case SOUND_slide:
15431       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15432       break;
15433
15434     case SOUND_wonder:
15435       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15436       break;
15437
15438     case SOUND_door:
15439       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15440       break;
15441
15442     case SOUND_exit_open:
15443       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15444       break;
15445
15446     case SOUND_exit_leave:
15447       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15448       break;
15449
15450     case SOUND_dynamite:
15451       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15452       break;
15453
15454     case SOUND_tick:
15455       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15456       break;
15457
15458     case SOUND_press:
15459       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15460       break;
15461
15462     case SOUND_wheel:
15463       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15464       break;
15465
15466     case SOUND_boom:
15467       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15468       break;
15469
15470     case SOUND_die:
15471       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15472       break;
15473
15474     case SOUND_time:
15475       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15476       break;
15477
15478     default:
15479       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15480       break;
15481   }
15482 }
15483
15484 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15485 {
15486   int element = map_element_SP_to_RND(element_sp);
15487   int action = map_action_SP_to_RND(action_sp);
15488   int offset = (setup.sp_show_border_elements ? 0 : 1);
15489   int x = xx - offset;
15490   int y = yy - offset;
15491
15492   PlayLevelSoundElementAction(x, y, element, action);
15493 }
15494
15495 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15496 {
15497   int element = map_element_MM_to_RND(element_mm);
15498   int action = map_action_MM_to_RND(action_mm);
15499   int offset = 0;
15500   int x = xx - offset;
15501   int y = yy - offset;
15502
15503   if (!IS_MM_ELEMENT(element))
15504     element = EL_MM_DEFAULT;
15505
15506   PlayLevelSoundElementAction(x, y, element, action);
15507 }
15508
15509 void PlaySound_MM(int sound_mm)
15510 {
15511   int sound = map_sound_MM_to_RND(sound_mm);
15512
15513   if (sound == SND_UNDEFINED)
15514     return;
15515
15516   PlaySound(sound);
15517 }
15518
15519 void PlaySoundLoop_MM(int sound_mm)
15520 {
15521   int sound = map_sound_MM_to_RND(sound_mm);
15522
15523   if (sound == SND_UNDEFINED)
15524     return;
15525
15526   PlaySoundLoop(sound);
15527 }
15528
15529 void StopSound_MM(int sound_mm)
15530 {
15531   int sound = map_sound_MM_to_RND(sound_mm);
15532
15533   if (sound == SND_UNDEFINED)
15534     return;
15535
15536   StopSound(sound);
15537 }
15538
15539 void RaiseScore(int value)
15540 {
15541   game.score += value;
15542
15543   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15544
15545   DisplayGameControlValues();
15546 }
15547
15548 void RaiseScoreElement(int element)
15549 {
15550   switch (element)
15551   {
15552     case EL_EMERALD:
15553     case EL_BD_DIAMOND:
15554     case EL_EMERALD_YELLOW:
15555     case EL_EMERALD_RED:
15556     case EL_EMERALD_PURPLE:
15557     case EL_SP_INFOTRON:
15558       RaiseScore(level.score[SC_EMERALD]);
15559       break;
15560     case EL_DIAMOND:
15561       RaiseScore(level.score[SC_DIAMOND]);
15562       break;
15563     case EL_CRYSTAL:
15564       RaiseScore(level.score[SC_CRYSTAL]);
15565       break;
15566     case EL_PEARL:
15567       RaiseScore(level.score[SC_PEARL]);
15568       break;
15569     case EL_BUG:
15570     case EL_BD_BUTTERFLY:
15571     case EL_SP_ELECTRON:
15572       RaiseScore(level.score[SC_BUG]);
15573       break;
15574     case EL_SPACESHIP:
15575     case EL_BD_FIREFLY:
15576     case EL_SP_SNIKSNAK:
15577       RaiseScore(level.score[SC_SPACESHIP]);
15578       break;
15579     case EL_YAMYAM:
15580     case EL_DARK_YAMYAM:
15581       RaiseScore(level.score[SC_YAMYAM]);
15582       break;
15583     case EL_ROBOT:
15584       RaiseScore(level.score[SC_ROBOT]);
15585       break;
15586     case EL_PACMAN:
15587       RaiseScore(level.score[SC_PACMAN]);
15588       break;
15589     case EL_NUT:
15590       RaiseScore(level.score[SC_NUT]);
15591       break;
15592     case EL_DYNAMITE:
15593     case EL_EM_DYNAMITE:
15594     case EL_SP_DISK_RED:
15595     case EL_DYNABOMB_INCREASE_NUMBER:
15596     case EL_DYNABOMB_INCREASE_SIZE:
15597     case EL_DYNABOMB_INCREASE_POWER:
15598       RaiseScore(level.score[SC_DYNAMITE]);
15599       break;
15600     case EL_SHIELD_NORMAL:
15601     case EL_SHIELD_DEADLY:
15602       RaiseScore(level.score[SC_SHIELD]);
15603       break;
15604     case EL_EXTRA_TIME:
15605       RaiseScore(level.extra_time_score);
15606       break;
15607     case EL_KEY_1:
15608     case EL_KEY_2:
15609     case EL_KEY_3:
15610     case EL_KEY_4:
15611     case EL_EM_KEY_1:
15612     case EL_EM_KEY_2:
15613     case EL_EM_KEY_3:
15614     case EL_EM_KEY_4:
15615     case EL_EMC_KEY_5:
15616     case EL_EMC_KEY_6:
15617     case EL_EMC_KEY_7:
15618     case EL_EMC_KEY_8:
15619     case EL_DC_KEY_WHITE:
15620       RaiseScore(level.score[SC_KEY]);
15621       break;
15622     default:
15623       RaiseScore(element_info[element].collect_score);
15624       break;
15625   }
15626 }
15627
15628 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15629 {
15630   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15631   {
15632     if (!quick_quit)
15633     {
15634       // prevent short reactivation of overlay buttons while closing door
15635       SetOverlayActive(FALSE);
15636
15637       // door may still be open due to skipped or envelope style request
15638       CloseDoor(DOOR_CLOSE_1);
15639     }
15640
15641     if (network.enabled)
15642       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15643     else
15644     {
15645       if (quick_quit)
15646         FadeSkipNextFadeIn();
15647
15648       SetGameStatus(GAME_MODE_MAIN);
15649
15650       DrawMainMenu();
15651     }
15652   }
15653   else          // continue playing the game
15654   {
15655     if (tape.playing && tape.deactivate_display)
15656       TapeDeactivateDisplayOff(TRUE);
15657
15658     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15659
15660     if (tape.playing && tape.deactivate_display)
15661       TapeDeactivateDisplayOn();
15662   }
15663 }
15664
15665 void RequestQuitGame(boolean escape_key_pressed)
15666 {
15667   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15668   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15669                         level_editor_test_game);
15670   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15671                           quick_quit);
15672
15673   RequestQuitGameExt(skip_request, quick_quit,
15674                      "Do you really want to quit the game?");
15675 }
15676
15677 void RequestRestartGame(char *message)
15678 {
15679   game.restart_game_message = NULL;
15680
15681   boolean has_started_game = hasStartedNetworkGame();
15682   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15683
15684   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15685   {
15686     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15687   }
15688   else
15689   {
15690     // needed in case of envelope request to close game panel
15691     CloseDoor(DOOR_CLOSE_1);
15692
15693     SetGameStatus(GAME_MODE_MAIN);
15694
15695     DrawMainMenu();
15696   }
15697 }
15698
15699 void CheckGameOver(void)
15700 {
15701   static boolean last_game_over = FALSE;
15702   static int game_over_delay = 0;
15703   int game_over_delay_value = 50;
15704   boolean game_over = checkGameFailed();
15705
15706   // do not handle game over if request dialog is already active
15707   if (game.request_active)
15708     return;
15709
15710   // do not ask to play again if game was never actually played
15711   if (!game.GamePlayed)
15712     return;
15713
15714   if (!game_over)
15715   {
15716     last_game_over = FALSE;
15717     game_over_delay = game_over_delay_value;
15718
15719     return;
15720   }
15721
15722   if (game_over_delay > 0)
15723   {
15724     game_over_delay--;
15725
15726     return;
15727   }
15728
15729   if (last_game_over != game_over)
15730     game.restart_game_message = (hasStartedNetworkGame() ?
15731                                  "Game over! Play it again?" :
15732                                  "Game over!");
15733
15734   last_game_over = game_over;
15735 }
15736
15737 boolean checkGameSolved(void)
15738 {
15739   // set for all game engines if level was solved
15740   return game.LevelSolved_GameEnd;
15741 }
15742
15743 boolean checkGameFailed(void)
15744 {
15745   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15746     return (game_em.game_over && !game_em.level_solved);
15747   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15748     return (game_sp.game_over && !game_sp.level_solved);
15749   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15750     return (game_mm.game_over && !game_mm.level_solved);
15751   else                          // GAME_ENGINE_TYPE_RND
15752     return (game.GameOver && !game.LevelSolved);
15753 }
15754
15755 boolean checkGameEnded(void)
15756 {
15757   return (checkGameSolved() || checkGameFailed());
15758 }
15759
15760
15761 // ----------------------------------------------------------------------------
15762 // random generator functions
15763 // ----------------------------------------------------------------------------
15764
15765 unsigned int InitEngineRandom_RND(int seed)
15766 {
15767   game.num_random_calls = 0;
15768
15769   return InitEngineRandom(seed);
15770 }
15771
15772 unsigned int RND(int max)
15773 {
15774   if (max > 0)
15775   {
15776     game.num_random_calls++;
15777
15778     return GetEngineRandom(max);
15779   }
15780
15781   return 0;
15782 }
15783
15784
15785 // ----------------------------------------------------------------------------
15786 // game engine snapshot handling functions
15787 // ----------------------------------------------------------------------------
15788
15789 struct EngineSnapshotInfo
15790 {
15791   // runtime values for custom element collect score
15792   int collect_score[NUM_CUSTOM_ELEMENTS];
15793
15794   // runtime values for group element choice position
15795   int choice_pos[NUM_GROUP_ELEMENTS];
15796
15797   // runtime values for belt position animations
15798   int belt_graphic[4][NUM_BELT_PARTS];
15799   int belt_anim_mode[4][NUM_BELT_PARTS];
15800 };
15801
15802 static struct EngineSnapshotInfo engine_snapshot_rnd;
15803 static char *snapshot_level_identifier = NULL;
15804 static int snapshot_level_nr = -1;
15805
15806 static void SaveEngineSnapshotValues_RND(void)
15807 {
15808   static int belt_base_active_element[4] =
15809   {
15810     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15811     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15812     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15813     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15814   };
15815   int i, j;
15816
15817   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15818   {
15819     int element = EL_CUSTOM_START + i;
15820
15821     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15822   }
15823
15824   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15825   {
15826     int element = EL_GROUP_START + i;
15827
15828     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15829   }
15830
15831   for (i = 0; i < 4; i++)
15832   {
15833     for (j = 0; j < NUM_BELT_PARTS; j++)
15834     {
15835       int element = belt_base_active_element[i] + j;
15836       int graphic = el2img(element);
15837       int anim_mode = graphic_info[graphic].anim_mode;
15838
15839       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15840       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15841     }
15842   }
15843 }
15844
15845 static void LoadEngineSnapshotValues_RND(void)
15846 {
15847   unsigned int num_random_calls = game.num_random_calls;
15848   int i, j;
15849
15850   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15851   {
15852     int element = EL_CUSTOM_START + i;
15853
15854     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15855   }
15856
15857   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15858   {
15859     int element = EL_GROUP_START + i;
15860
15861     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15862   }
15863
15864   for (i = 0; i < 4; i++)
15865   {
15866     for (j = 0; j < NUM_BELT_PARTS; j++)
15867     {
15868       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15869       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15870
15871       graphic_info[graphic].anim_mode = anim_mode;
15872     }
15873   }
15874
15875   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15876   {
15877     InitRND(tape.random_seed);
15878     for (i = 0; i < num_random_calls; i++)
15879       RND(1);
15880   }
15881
15882   if (game.num_random_calls != num_random_calls)
15883   {
15884     Error("number of random calls out of sync");
15885     Error("number of random calls should be %d", num_random_calls);
15886     Error("number of random calls is %d", game.num_random_calls);
15887
15888     Fail("this should not happen -- please debug");
15889   }
15890 }
15891
15892 void FreeEngineSnapshotSingle(void)
15893 {
15894   FreeSnapshotSingle();
15895
15896   setString(&snapshot_level_identifier, NULL);
15897   snapshot_level_nr = -1;
15898 }
15899
15900 void FreeEngineSnapshotList(void)
15901 {
15902   FreeSnapshotList();
15903 }
15904
15905 static ListNode *SaveEngineSnapshotBuffers(void)
15906 {
15907   ListNode *buffers = NULL;
15908
15909   // copy some special values to a structure better suited for the snapshot
15910
15911   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15912     SaveEngineSnapshotValues_RND();
15913   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15914     SaveEngineSnapshotValues_EM();
15915   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15916     SaveEngineSnapshotValues_SP(&buffers);
15917   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15918     SaveEngineSnapshotValues_MM(&buffers);
15919
15920   // save values stored in special snapshot structure
15921
15922   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15923     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15924   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15925     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15926   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15927     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15928   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15929     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15930
15931   // save further RND engine values
15932
15933   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15934   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15935   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15936
15937   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15938   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15939   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15940   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15941   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15942
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15944   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15945   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15946
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15948
15949   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15950   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15951
15952   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15953   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15956   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15959   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15960   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15970
15971   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15973
15974   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15976   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15977
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15979   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15980
15981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15983   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15986   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15987
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15990
15991 #if 0
15992   ListNode *node = engine_snapshot_list_rnd;
15993   int num_bytes = 0;
15994
15995   while (node != NULL)
15996   {
15997     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15998
15999     node = node->next;
16000   }
16001
16002   Debug("game:playing:SaveEngineSnapshotBuffers",
16003         "size of engine snapshot: %d bytes", num_bytes);
16004 #endif
16005
16006   return buffers;
16007 }
16008
16009 void SaveEngineSnapshotSingle(void)
16010 {
16011   ListNode *buffers = SaveEngineSnapshotBuffers();
16012
16013   // finally save all snapshot buffers to single snapshot
16014   SaveSnapshotSingle(buffers);
16015
16016   // save level identification information
16017   setString(&snapshot_level_identifier, leveldir_current->identifier);
16018   snapshot_level_nr = level_nr;
16019 }
16020
16021 boolean CheckSaveEngineSnapshotToList(void)
16022 {
16023   boolean save_snapshot =
16024     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16025      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16026       game.snapshot.changed_action) ||
16027      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16028       game.snapshot.collected_item));
16029
16030   game.snapshot.changed_action = FALSE;
16031   game.snapshot.collected_item = FALSE;
16032   game.snapshot.save_snapshot = save_snapshot;
16033
16034   return save_snapshot;
16035 }
16036
16037 void SaveEngineSnapshotToList(void)
16038 {
16039   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16040       tape.quick_resume)
16041     return;
16042
16043   ListNode *buffers = SaveEngineSnapshotBuffers();
16044
16045   // finally save all snapshot buffers to snapshot list
16046   SaveSnapshotToList(buffers);
16047 }
16048
16049 void SaveEngineSnapshotToListInitial(void)
16050 {
16051   FreeEngineSnapshotList();
16052
16053   SaveEngineSnapshotToList();
16054 }
16055
16056 static void LoadEngineSnapshotValues(void)
16057 {
16058   // restore special values from snapshot structure
16059
16060   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16061     LoadEngineSnapshotValues_RND();
16062   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16063     LoadEngineSnapshotValues_EM();
16064   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16065     LoadEngineSnapshotValues_SP();
16066   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16067     LoadEngineSnapshotValues_MM();
16068 }
16069
16070 void LoadEngineSnapshotSingle(void)
16071 {
16072   LoadSnapshotSingle();
16073
16074   LoadEngineSnapshotValues();
16075 }
16076
16077 static void LoadEngineSnapshot_Undo(int steps)
16078 {
16079   LoadSnapshotFromList_Older(steps);
16080
16081   LoadEngineSnapshotValues();
16082 }
16083
16084 static void LoadEngineSnapshot_Redo(int steps)
16085 {
16086   LoadSnapshotFromList_Newer(steps);
16087
16088   LoadEngineSnapshotValues();
16089 }
16090
16091 boolean CheckEngineSnapshotSingle(void)
16092 {
16093   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16094           snapshot_level_nr == level_nr);
16095 }
16096
16097 boolean CheckEngineSnapshotList(void)
16098 {
16099   return CheckSnapshotList();
16100 }
16101
16102
16103 // ---------- new game button stuff -------------------------------------------
16104
16105 static struct
16106 {
16107   int graphic;
16108   struct XY *pos;
16109   int gadget_id;
16110   boolean *setup_value;
16111   boolean allowed_on_tape;
16112   boolean is_touch_button;
16113   char *infotext;
16114 } gamebutton_info[NUM_GAME_BUTTONS] =
16115 {
16116   {
16117     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16118     GAME_CTRL_ID_STOP,                          NULL,
16119     TRUE, FALSE,                                "stop game"
16120   },
16121   {
16122     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16123     GAME_CTRL_ID_PAUSE,                         NULL,
16124     TRUE, FALSE,                                "pause game"
16125   },
16126   {
16127     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16128     GAME_CTRL_ID_PLAY,                          NULL,
16129     TRUE, FALSE,                                "play game"
16130   },
16131   {
16132     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16133     GAME_CTRL_ID_UNDO,                          NULL,
16134     TRUE, FALSE,                                "undo step"
16135   },
16136   {
16137     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16138     GAME_CTRL_ID_REDO,                          NULL,
16139     TRUE, FALSE,                                "redo step"
16140   },
16141   {
16142     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16143     GAME_CTRL_ID_SAVE,                          NULL,
16144     TRUE, FALSE,                                "save game"
16145   },
16146   {
16147     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16148     GAME_CTRL_ID_PAUSE2,                        NULL,
16149     TRUE, FALSE,                                "pause game"
16150   },
16151   {
16152     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16153     GAME_CTRL_ID_LOAD,                          NULL,
16154     TRUE, FALSE,                                "load game"
16155   },
16156   {
16157     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16158     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16159     FALSE, FALSE,                               "stop game"
16160   },
16161   {
16162     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16163     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16164     FALSE, FALSE,                               "pause game"
16165   },
16166   {
16167     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16168     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16169     FALSE, FALSE,                               "play game"
16170   },
16171   {
16172     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16173     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16174     FALSE, TRUE,                                "stop game"
16175   },
16176   {
16177     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16178     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16179     FALSE, TRUE,                                "pause game"
16180   },
16181   {
16182     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16183     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16184     TRUE, FALSE,                                "background music on/off"
16185   },
16186   {
16187     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16188     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16189     TRUE, FALSE,                                "sound loops on/off"
16190   },
16191   {
16192     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16193     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16194     TRUE, FALSE,                                "normal sounds on/off"
16195   },
16196   {
16197     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16198     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16199     FALSE, FALSE,                               "background music on/off"
16200   },
16201   {
16202     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16203     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16204     FALSE, FALSE,                               "sound loops on/off"
16205   },
16206   {
16207     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16208     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16209     FALSE, FALSE,                               "normal sounds on/off"
16210   }
16211 };
16212
16213 void CreateGameButtons(void)
16214 {
16215   int i;
16216
16217   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16218   {
16219     int graphic = gamebutton_info[i].graphic;
16220     struct GraphicInfo *gfx = &graphic_info[graphic];
16221     struct XY *pos = gamebutton_info[i].pos;
16222     struct GadgetInfo *gi;
16223     int button_type;
16224     boolean checked;
16225     unsigned int event_mask;
16226     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16227     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16228     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16229     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16230     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16231     int gd_x   = gfx->src_x;
16232     int gd_y   = gfx->src_y;
16233     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16234     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16235     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16236     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16237     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16238     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16239     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16240     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16241     int id = i;
16242
16243     if (gfx->bitmap == NULL)
16244     {
16245       game_gadget[id] = NULL;
16246
16247       continue;
16248     }
16249
16250     if (id == GAME_CTRL_ID_STOP ||
16251         id == GAME_CTRL_ID_PANEL_STOP ||
16252         id == GAME_CTRL_ID_TOUCH_STOP ||
16253         id == GAME_CTRL_ID_PLAY ||
16254         id == GAME_CTRL_ID_PANEL_PLAY ||
16255         id == GAME_CTRL_ID_SAVE ||
16256         id == GAME_CTRL_ID_LOAD)
16257     {
16258       button_type = GD_TYPE_NORMAL_BUTTON;
16259       checked = FALSE;
16260       event_mask = GD_EVENT_RELEASED;
16261     }
16262     else if (id == GAME_CTRL_ID_UNDO ||
16263              id == GAME_CTRL_ID_REDO)
16264     {
16265       button_type = GD_TYPE_NORMAL_BUTTON;
16266       checked = FALSE;
16267       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16268     }
16269     else
16270     {
16271       button_type = GD_TYPE_CHECK_BUTTON;
16272       checked = (gamebutton_info[i].setup_value != NULL ?
16273                  *gamebutton_info[i].setup_value : FALSE);
16274       event_mask = GD_EVENT_PRESSED;
16275     }
16276
16277     gi = CreateGadget(GDI_CUSTOM_ID, id,
16278                       GDI_IMAGE_ID, graphic,
16279                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16280                       GDI_X, base_x + x,
16281                       GDI_Y, base_y + y,
16282                       GDI_WIDTH, gfx->width,
16283                       GDI_HEIGHT, gfx->height,
16284                       GDI_TYPE, button_type,
16285                       GDI_STATE, GD_BUTTON_UNPRESSED,
16286                       GDI_CHECKED, checked,
16287                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16288                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16289                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16290                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16291                       GDI_DIRECT_DRAW, FALSE,
16292                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16293                       GDI_EVENT_MASK, event_mask,
16294                       GDI_CALLBACK_ACTION, HandleGameButtons,
16295                       GDI_END);
16296
16297     if (gi == NULL)
16298       Fail("cannot create gadget");
16299
16300     game_gadget[id] = gi;
16301   }
16302 }
16303
16304 void FreeGameButtons(void)
16305 {
16306   int i;
16307
16308   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16309     FreeGadget(game_gadget[i]);
16310 }
16311
16312 static void UnmapGameButtonsAtSamePosition(int id)
16313 {
16314   int i;
16315
16316   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16317     if (i != id &&
16318         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16319         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16320       UnmapGadget(game_gadget[i]);
16321 }
16322
16323 static void UnmapGameButtonsAtSamePosition_All(void)
16324 {
16325   if (setup.show_load_save_buttons)
16326   {
16327     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16328     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16329     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16330   }
16331   else if (setup.show_undo_redo_buttons)
16332   {
16333     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16334     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16335     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16336   }
16337   else
16338   {
16339     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16340     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16341     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16342
16343     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16344     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16345     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16346   }
16347 }
16348
16349 void MapLoadSaveButtons(void)
16350 {
16351   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16352   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16353
16354   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16355   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16356 }
16357
16358 void MapUndoRedoButtons(void)
16359 {
16360   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16361   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16362
16363   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16364   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16365 }
16366
16367 void ModifyPauseButtons(void)
16368 {
16369   static int ids[] =
16370   {
16371     GAME_CTRL_ID_PAUSE,
16372     GAME_CTRL_ID_PAUSE2,
16373     GAME_CTRL_ID_PANEL_PAUSE,
16374     GAME_CTRL_ID_TOUCH_PAUSE,
16375     -1
16376   };
16377   int i;
16378
16379   for (i = 0; ids[i] > -1; i++)
16380     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16381 }
16382
16383 static void MapGameButtonsExt(boolean on_tape)
16384 {
16385   int i;
16386
16387   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16388     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16389       MapGadget(game_gadget[i]);
16390
16391   UnmapGameButtonsAtSamePosition_All();
16392
16393   RedrawGameButtons();
16394 }
16395
16396 static void UnmapGameButtonsExt(boolean on_tape)
16397 {
16398   int i;
16399
16400   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16401     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16402       UnmapGadget(game_gadget[i]);
16403 }
16404
16405 static void RedrawGameButtonsExt(boolean on_tape)
16406 {
16407   int i;
16408
16409   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16410     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16411       RedrawGadget(game_gadget[i]);
16412 }
16413
16414 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16415 {
16416   if (gi == NULL)
16417     return;
16418
16419   gi->checked = state;
16420 }
16421
16422 static void RedrawSoundButtonGadget(int id)
16423 {
16424   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16425              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16426              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16427              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16428              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16429              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16430              id);
16431
16432   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16433   RedrawGadget(game_gadget[id2]);
16434 }
16435
16436 void MapGameButtons(void)
16437 {
16438   MapGameButtonsExt(FALSE);
16439 }
16440
16441 void UnmapGameButtons(void)
16442 {
16443   UnmapGameButtonsExt(FALSE);
16444 }
16445
16446 void RedrawGameButtons(void)
16447 {
16448   RedrawGameButtonsExt(FALSE);
16449 }
16450
16451 void MapGameButtonsOnTape(void)
16452 {
16453   MapGameButtonsExt(TRUE);
16454 }
16455
16456 void UnmapGameButtonsOnTape(void)
16457 {
16458   UnmapGameButtonsExt(TRUE);
16459 }
16460
16461 void RedrawGameButtonsOnTape(void)
16462 {
16463   RedrawGameButtonsExt(TRUE);
16464 }
16465
16466 static void GameUndoRedoExt(void)
16467 {
16468   ClearPlayerAction();
16469
16470   tape.pausing = TRUE;
16471
16472   RedrawPlayfield();
16473   UpdateAndDisplayGameControlValues();
16474
16475   DrawCompleteVideoDisplay();
16476   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16477   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16478   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16479
16480   ModifyPauseButtons();
16481
16482   BackToFront();
16483 }
16484
16485 static void GameUndo(int steps)
16486 {
16487   if (!CheckEngineSnapshotList())
16488     return;
16489
16490   int tape_property_bits = tape.property_bits;
16491
16492   LoadEngineSnapshot_Undo(steps);
16493
16494   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16495
16496   GameUndoRedoExt();
16497 }
16498
16499 static void GameRedo(int steps)
16500 {
16501   if (!CheckEngineSnapshotList())
16502     return;
16503
16504   int tape_property_bits = tape.property_bits;
16505
16506   LoadEngineSnapshot_Redo(steps);
16507
16508   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16509
16510   GameUndoRedoExt();
16511 }
16512
16513 static void HandleGameButtonsExt(int id, int button)
16514 {
16515   static boolean game_undo_executed = FALSE;
16516   int steps = BUTTON_STEPSIZE(button);
16517   boolean handle_game_buttons =
16518     (game_status == GAME_MODE_PLAYING ||
16519      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16520
16521   if (!handle_game_buttons)
16522     return;
16523
16524   switch (id)
16525   {
16526     case GAME_CTRL_ID_STOP:
16527     case GAME_CTRL_ID_PANEL_STOP:
16528     case GAME_CTRL_ID_TOUCH_STOP:
16529       if (game_status == GAME_MODE_MAIN)
16530         break;
16531
16532       if (tape.playing)
16533         TapeStop();
16534       else
16535         RequestQuitGame(FALSE);
16536
16537       break;
16538
16539     case GAME_CTRL_ID_PAUSE:
16540     case GAME_CTRL_ID_PAUSE2:
16541     case GAME_CTRL_ID_PANEL_PAUSE:
16542     case GAME_CTRL_ID_TOUCH_PAUSE:
16543       if (network.enabled && game_status == GAME_MODE_PLAYING)
16544       {
16545         if (tape.pausing)
16546           SendToServer_ContinuePlaying();
16547         else
16548           SendToServer_PausePlaying();
16549       }
16550       else
16551         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16552
16553       game_undo_executed = FALSE;
16554
16555       break;
16556
16557     case GAME_CTRL_ID_PLAY:
16558     case GAME_CTRL_ID_PANEL_PLAY:
16559       if (game_status == GAME_MODE_MAIN)
16560       {
16561         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16562       }
16563       else if (tape.pausing)
16564       {
16565         if (network.enabled)
16566           SendToServer_ContinuePlaying();
16567         else
16568           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16569       }
16570       break;
16571
16572     case GAME_CTRL_ID_UNDO:
16573       // Important: When using "save snapshot when collecting an item" mode,
16574       // load last (current) snapshot for first "undo" after pressing "pause"
16575       // (else the last-but-one snapshot would be loaded, because the snapshot
16576       // pointer already points to the last snapshot when pressing "pause",
16577       // which is fine for "every step/move" mode, but not for "every collect")
16578       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16579           !game_undo_executed)
16580         steps--;
16581
16582       game_undo_executed = TRUE;
16583
16584       GameUndo(steps);
16585       break;
16586
16587     case GAME_CTRL_ID_REDO:
16588       GameRedo(steps);
16589       break;
16590
16591     case GAME_CTRL_ID_SAVE:
16592       TapeQuickSave();
16593       break;
16594
16595     case GAME_CTRL_ID_LOAD:
16596       TapeQuickLoad();
16597       break;
16598
16599     case SOUND_CTRL_ID_MUSIC:
16600     case SOUND_CTRL_ID_PANEL_MUSIC:
16601       if (setup.sound_music)
16602       { 
16603         setup.sound_music = FALSE;
16604
16605         FadeMusic();
16606       }
16607       else if (audio.music_available)
16608       { 
16609         setup.sound = setup.sound_music = TRUE;
16610
16611         SetAudioMode(setup.sound);
16612
16613         if (game_status == GAME_MODE_PLAYING)
16614           PlayLevelMusic();
16615       }
16616
16617       RedrawSoundButtonGadget(id);
16618
16619       break;
16620
16621     case SOUND_CTRL_ID_LOOPS:
16622     case SOUND_CTRL_ID_PANEL_LOOPS:
16623       if (setup.sound_loops)
16624         setup.sound_loops = FALSE;
16625       else if (audio.loops_available)
16626       {
16627         setup.sound = setup.sound_loops = TRUE;
16628
16629         SetAudioMode(setup.sound);
16630       }
16631
16632       RedrawSoundButtonGadget(id);
16633
16634       break;
16635
16636     case SOUND_CTRL_ID_SIMPLE:
16637     case SOUND_CTRL_ID_PANEL_SIMPLE:
16638       if (setup.sound_simple)
16639         setup.sound_simple = FALSE;
16640       else if (audio.sound_available)
16641       {
16642         setup.sound = setup.sound_simple = TRUE;
16643
16644         SetAudioMode(setup.sound);
16645       }
16646
16647       RedrawSoundButtonGadget(id);
16648
16649       break;
16650
16651     default:
16652       break;
16653   }
16654 }
16655
16656 static void HandleGameButtons(struct GadgetInfo *gi)
16657 {
16658   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16659 }
16660
16661 void HandleSoundButtonKeys(Key key)
16662 {
16663   if (key == setup.shortcut.sound_simple)
16664     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16665   else if (key == setup.shortcut.sound_loops)
16666     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16667   else if (key == setup.shortcut.sound_music)
16668     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16669 }