367a86a4e38ed0663bc41a4ad40075ff393e0880
[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           if (Store[x][y] == EL_EMPTY)
8424             Store[x][y] = GfxElementEmpty[x][y];
8425
8426           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8427                                    GfxDir[x][y]);
8428
8429           DrawLevelGraphicAnimation(x, y, graphic);
8430           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8431
8432           if (Tile[newx][newy] == EL_ACID)
8433           {
8434             SplashAcid(newx, newy);
8435
8436             return;
8437           }
8438
8439           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8440
8441           Store[newx][newy] = EL_EMC_ANDROID;
8442           GfxElement[newx][newy] = EL_EMC_ANDROID;
8443           GfxAction[newx][newy] = ACTION_GROWING;
8444           GfxDir[newx][newy] = diagonal_move_dir;
8445           ChangeDelay[newx][newy] = change_delay;
8446
8447           graphic = el_act_dir2img(GfxElement[newx][newy],
8448                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8449
8450           DrawLevelGraphicAnimation(newx, newy, graphic);
8451           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8452
8453           return;
8454         }
8455         else
8456         {
8457           Tile[newx][newy] = EL_EMPTY;
8458           TEST_DrawLevelField(newx, newy);
8459
8460           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8461         }
8462       }
8463       else if (!IS_FREE(newx, newy))
8464       {
8465         return;
8466       }
8467     }
8468     else if (IS_CUSTOM_ELEMENT(element) &&
8469              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8470     {
8471       if (!DigFieldByCE(newx, newy, element))
8472         return;
8473
8474       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8475       {
8476         RunnerVisit[x][y] = FrameCounter;
8477         PlayerVisit[x][y] /= 8;         // expire player visit path
8478       }
8479     }
8480     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8481     {
8482       if (!IS_FREE(newx, newy))
8483       {
8484         if (IS_PLAYER(x, y))
8485           DrawPlayerField(x, y);
8486         else
8487           TEST_DrawLevelField(x, y);
8488
8489         return;
8490       }
8491       else
8492       {
8493         boolean wanna_flame = !RND(10);
8494         int dx = newx - x, dy = newy - y;
8495         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8496         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8497         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8498                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8499         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8500                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8501
8502         if ((wanna_flame ||
8503              IS_CLASSIC_ENEMY(element1) ||
8504              IS_CLASSIC_ENEMY(element2)) &&
8505             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8506             element1 != EL_FLAMES && element2 != EL_FLAMES)
8507         {
8508           ResetGfxAnimation(x, y);
8509           GfxAction[x][y] = ACTION_ATTACKING;
8510
8511           if (IS_PLAYER(x, y))
8512             DrawPlayerField(x, y);
8513           else
8514             TEST_DrawLevelField(x, y);
8515
8516           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8517
8518           MovDelay[x][y] = 50;
8519
8520           Tile[newx][newy] = EL_FLAMES;
8521           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8522             Tile[newx1][newy1] = EL_FLAMES;
8523           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8524             Tile[newx2][newy2] = EL_FLAMES;
8525
8526           return;
8527         }
8528       }
8529     }
8530     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8531              Tile[newx][newy] == EL_DIAMOND)
8532     {
8533       if (IS_MOVING(newx, newy))
8534         RemoveMovingField(newx, newy);
8535       else
8536       {
8537         Tile[newx][newy] = EL_EMPTY;
8538         TEST_DrawLevelField(newx, newy);
8539       }
8540
8541       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8542     }
8543     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8544              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8545     {
8546       if (AmoebaNr[newx][newy])
8547       {
8548         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8549         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8550             Tile[newx][newy] == EL_BD_AMOEBA)
8551           AmoebaCnt[AmoebaNr[newx][newy]]--;
8552       }
8553
8554       if (IS_MOVING(newx, newy))
8555       {
8556         RemoveMovingField(newx, newy);
8557       }
8558       else
8559       {
8560         Tile[newx][newy] = EL_EMPTY;
8561         TEST_DrawLevelField(newx, newy);
8562       }
8563
8564       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8565     }
8566     else if ((element == EL_PACMAN || element == EL_MOLE)
8567              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8568     {
8569       if (AmoebaNr[newx][newy])
8570       {
8571         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8572         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8573             Tile[newx][newy] == EL_BD_AMOEBA)
8574           AmoebaCnt[AmoebaNr[newx][newy]]--;
8575       }
8576
8577       if (element == EL_MOLE)
8578       {
8579         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8580         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8581
8582         ResetGfxAnimation(x, y);
8583         GfxAction[x][y] = ACTION_DIGGING;
8584         TEST_DrawLevelField(x, y);
8585
8586         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8587
8588         return;                         // wait for shrinking amoeba
8589       }
8590       else      // element == EL_PACMAN
8591       {
8592         Tile[newx][newy] = EL_EMPTY;
8593         TEST_DrawLevelField(newx, newy);
8594         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8595       }
8596     }
8597     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8598              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8599               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8600     {
8601       // wait for shrinking amoeba to completely disappear
8602       return;
8603     }
8604     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8605     {
8606       // object was running against a wall
8607
8608       TurnRound(x, y);
8609
8610       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8611         DrawLevelElementAnimation(x, y, element);
8612
8613       if (DONT_TOUCH(element))
8614         TestIfBadThingTouchesPlayer(x, y);
8615
8616       return;
8617     }
8618
8619     InitMovingField(x, y, MovDir[x][y]);
8620
8621     PlayLevelSoundAction(x, y, ACTION_MOVING);
8622   }
8623
8624   if (MovDir[x][y])
8625     ContinueMoving(x, y);
8626 }
8627
8628 void ContinueMoving(int x, int y)
8629 {
8630   int element = Tile[x][y];
8631   struct ElementInfo *ei = &element_info[element];
8632   int direction = MovDir[x][y];
8633   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8634   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8635   int newx = x + dx, newy = y + dy;
8636   int stored = Store[x][y];
8637   int stored_new = Store[newx][newy];
8638   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8639   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8640   boolean last_line = (newy == lev_fieldy - 1);
8641   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8642
8643   if (pushed_by_player)         // special case: moving object pushed by player
8644   {
8645     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8646   }
8647   else if (use_step_delay)      // special case: moving object has step delay
8648   {
8649     if (!MovDelay[x][y])
8650       MovPos[x][y] += getElementMoveStepsize(x, y);
8651
8652     if (MovDelay[x][y])
8653       MovDelay[x][y]--;
8654     else
8655       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8656
8657     if (MovDelay[x][y])
8658     {
8659       TEST_DrawLevelField(x, y);
8660
8661       return;   // element is still waiting
8662     }
8663   }
8664   else                          // normal case: generically moving object
8665   {
8666     MovPos[x][y] += getElementMoveStepsize(x, y);
8667   }
8668
8669   if (ABS(MovPos[x][y]) < TILEX)
8670   {
8671     TEST_DrawLevelField(x, y);
8672
8673     return;     // element is still moving
8674   }
8675
8676   // element reached destination field
8677
8678   Tile[x][y] = EL_EMPTY;
8679   Tile[newx][newy] = element;
8680   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8681
8682   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8683   {
8684     element = Tile[newx][newy] = EL_ACID;
8685   }
8686   else if (element == EL_MOLE)
8687   {
8688     Tile[x][y] = EL_SAND;
8689
8690     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8691   }
8692   else if (element == EL_QUICKSAND_FILLING)
8693   {
8694     element = Tile[newx][newy] = get_next_element(element);
8695     Store[newx][newy] = Store[x][y];
8696   }
8697   else if (element == EL_QUICKSAND_EMPTYING)
8698   {
8699     Tile[x][y] = get_next_element(element);
8700     element = Tile[newx][newy] = Store[x][y];
8701   }
8702   else if (element == EL_QUICKSAND_FAST_FILLING)
8703   {
8704     element = Tile[newx][newy] = get_next_element(element);
8705     Store[newx][newy] = Store[x][y];
8706   }
8707   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8708   {
8709     Tile[x][y] = get_next_element(element);
8710     element = Tile[newx][newy] = Store[x][y];
8711   }
8712   else if (element == EL_MAGIC_WALL_FILLING)
8713   {
8714     element = Tile[newx][newy] = get_next_element(element);
8715     if (!game.magic_wall_active)
8716       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8717     Store[newx][newy] = Store[x][y];
8718   }
8719   else if (element == EL_MAGIC_WALL_EMPTYING)
8720   {
8721     Tile[x][y] = get_next_element(element);
8722     if (!game.magic_wall_active)
8723       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8724     element = Tile[newx][newy] = Store[x][y];
8725
8726     InitField(newx, newy, FALSE);
8727   }
8728   else if (element == EL_BD_MAGIC_WALL_FILLING)
8729   {
8730     element = Tile[newx][newy] = get_next_element(element);
8731     if (!game.magic_wall_active)
8732       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8733     Store[newx][newy] = Store[x][y];
8734   }
8735   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8736   {
8737     Tile[x][y] = get_next_element(element);
8738     if (!game.magic_wall_active)
8739       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8740     element = Tile[newx][newy] = Store[x][y];
8741
8742     InitField(newx, newy, FALSE);
8743   }
8744   else if (element == EL_DC_MAGIC_WALL_FILLING)
8745   {
8746     element = Tile[newx][newy] = get_next_element(element);
8747     if (!game.magic_wall_active)
8748       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8749     Store[newx][newy] = Store[x][y];
8750   }
8751   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8752   {
8753     Tile[x][y] = get_next_element(element);
8754     if (!game.magic_wall_active)
8755       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8756     element = Tile[newx][newy] = Store[x][y];
8757
8758     InitField(newx, newy, FALSE);
8759   }
8760   else if (element == EL_AMOEBA_DROPPING)
8761   {
8762     Tile[x][y] = get_next_element(element);
8763     element = Tile[newx][newy] = Store[x][y];
8764   }
8765   else if (element == EL_SOKOBAN_OBJECT)
8766   {
8767     if (Back[x][y])
8768       Tile[x][y] = Back[x][y];
8769
8770     if (Back[newx][newy])
8771       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8772
8773     Back[x][y] = Back[newx][newy] = 0;
8774   }
8775
8776   Store[x][y] = EL_EMPTY;
8777   MovPos[x][y] = 0;
8778   MovDir[x][y] = 0;
8779   MovDelay[x][y] = 0;
8780
8781   MovDelay[newx][newy] = 0;
8782
8783   if (CAN_CHANGE_OR_HAS_ACTION(element))
8784   {
8785     // copy element change control values to new field
8786     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8787     ChangePage[newx][newy]  = ChangePage[x][y];
8788     ChangeCount[newx][newy] = ChangeCount[x][y];
8789     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8790   }
8791
8792   CustomValue[newx][newy] = CustomValue[x][y];
8793
8794   ChangeDelay[x][y] = 0;
8795   ChangePage[x][y] = -1;
8796   ChangeCount[x][y] = 0;
8797   ChangeEvent[x][y] = -1;
8798
8799   CustomValue[x][y] = 0;
8800
8801   // copy animation control values to new field
8802   GfxFrame[newx][newy]  = GfxFrame[x][y];
8803   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8804   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8805   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8806
8807   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8808
8809   // some elements can leave other elements behind after moving
8810   if (ei->move_leave_element != EL_EMPTY &&
8811       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8812       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8813   {
8814     int move_leave_element = ei->move_leave_element;
8815
8816     // this makes it possible to leave the removed element again
8817     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8818       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8819
8820     Tile[x][y] = move_leave_element;
8821
8822     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8823       MovDir[x][y] = direction;
8824
8825     InitField(x, y, FALSE);
8826
8827     if (GFX_CRUMBLED(Tile[x][y]))
8828       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8829
8830     if (IS_PLAYER_ELEMENT(move_leave_element))
8831       RelocatePlayer(x, y, move_leave_element);
8832   }
8833
8834   // do this after checking for left-behind element
8835   ResetGfxAnimation(x, y);      // reset animation values for old field
8836
8837   if (!CAN_MOVE(element) ||
8838       (CAN_FALL(element) && direction == MV_DOWN &&
8839        (element == EL_SPRING ||
8840         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8841         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8842     GfxDir[x][y] = MovDir[newx][newy] = 0;
8843
8844   TEST_DrawLevelField(x, y);
8845   TEST_DrawLevelField(newx, newy);
8846
8847   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8848
8849   // prevent pushed element from moving on in pushed direction
8850   if (pushed_by_player && CAN_MOVE(element) &&
8851       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8852       !(element_info[element].move_pattern & direction))
8853     TurnRound(newx, newy);
8854
8855   // prevent elements on conveyor belt from moving on in last direction
8856   if (pushed_by_conveyor && CAN_FALL(element) &&
8857       direction & MV_HORIZONTAL)
8858     MovDir[newx][newy] = 0;
8859
8860   if (!pushed_by_player)
8861   {
8862     int nextx = newx + dx, nexty = newy + dy;
8863     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8864
8865     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8866
8867     if (CAN_FALL(element) && direction == MV_DOWN)
8868       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8869
8870     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8871       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8872
8873     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8874       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8875   }
8876
8877   if (DONT_TOUCH(element))      // object may be nasty to player or others
8878   {
8879     TestIfBadThingTouchesPlayer(newx, newy);
8880     TestIfBadThingTouchesFriend(newx, newy);
8881
8882     if (!IS_CUSTOM_ELEMENT(element))
8883       TestIfBadThingTouchesOtherBadThing(newx, newy);
8884   }
8885   else if (element == EL_PENGUIN)
8886     TestIfFriendTouchesBadThing(newx, newy);
8887
8888   if (DONT_GET_HIT_BY(element))
8889   {
8890     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8891   }
8892
8893   // give the player one last chance (one more frame) to move away
8894   if (CAN_FALL(element) && direction == MV_DOWN &&
8895       (last_line || (!IS_FREE(x, newy + 1) &&
8896                      (!IS_PLAYER(x, newy + 1) ||
8897                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8898     Impact(x, newy);
8899
8900   if (pushed_by_player && !game.use_change_when_pushing_bug)
8901   {
8902     int push_side = MV_DIR_OPPOSITE(direction);
8903     struct PlayerInfo *player = PLAYERINFO(x, y);
8904
8905     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8906                                player->index_bit, push_side);
8907     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8908                                         player->index_bit, push_side);
8909   }
8910
8911   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8912     MovDelay[newx][newy] = 1;
8913
8914   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8915
8916   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8917   TestIfElementHitsCustomElement(newx, newy, direction);
8918   TestIfPlayerTouchesCustomElement(newx, newy);
8919   TestIfElementTouchesCustomElement(newx, newy);
8920
8921   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8922       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8923     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8924                              MV_DIR_OPPOSITE(direction));
8925 }
8926
8927 int AmoebaNeighbourNr(int ax, int ay)
8928 {
8929   int i;
8930   int element = Tile[ax][ay];
8931   int group_nr = 0;
8932   static int xy[4][2] =
8933   {
8934     { 0, -1 },
8935     { -1, 0 },
8936     { +1, 0 },
8937     { 0, +1 }
8938   };
8939
8940   for (i = 0; i < NUM_DIRECTIONS; i++)
8941   {
8942     int x = ax + xy[i][0];
8943     int y = ay + xy[i][1];
8944
8945     if (!IN_LEV_FIELD(x, y))
8946       continue;
8947
8948     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8949       group_nr = AmoebaNr[x][y];
8950   }
8951
8952   return group_nr;
8953 }
8954
8955 static void AmoebaMerge(int ax, int ay)
8956 {
8957   int i, x, y, xx, yy;
8958   int new_group_nr = AmoebaNr[ax][ay];
8959   static int xy[4][2] =
8960   {
8961     { 0, -1 },
8962     { -1, 0 },
8963     { +1, 0 },
8964     { 0, +1 }
8965   };
8966
8967   if (new_group_nr == 0)
8968     return;
8969
8970   for (i = 0; i < NUM_DIRECTIONS; i++)
8971   {
8972     x = ax + xy[i][0];
8973     y = ay + xy[i][1];
8974
8975     if (!IN_LEV_FIELD(x, y))
8976       continue;
8977
8978     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8979          Tile[x][y] == EL_BD_AMOEBA ||
8980          Tile[x][y] == EL_AMOEBA_DEAD) &&
8981         AmoebaNr[x][y] != new_group_nr)
8982     {
8983       int old_group_nr = AmoebaNr[x][y];
8984
8985       if (old_group_nr == 0)
8986         return;
8987
8988       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8989       AmoebaCnt[old_group_nr] = 0;
8990       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8991       AmoebaCnt2[old_group_nr] = 0;
8992
8993       SCAN_PLAYFIELD(xx, yy)
8994       {
8995         if (AmoebaNr[xx][yy] == old_group_nr)
8996           AmoebaNr[xx][yy] = new_group_nr;
8997       }
8998     }
8999   }
9000 }
9001
9002 void AmoebaToDiamond(int ax, int ay)
9003 {
9004   int i, x, y;
9005
9006   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9007   {
9008     int group_nr = AmoebaNr[ax][ay];
9009
9010 #ifdef DEBUG
9011     if (group_nr == 0)
9012     {
9013       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9014       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9015
9016       return;
9017     }
9018 #endif
9019
9020     SCAN_PLAYFIELD(x, y)
9021     {
9022       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9023       {
9024         AmoebaNr[x][y] = 0;
9025         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9026       }
9027     }
9028
9029     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9030                             SND_AMOEBA_TURNING_TO_GEM :
9031                             SND_AMOEBA_TURNING_TO_ROCK));
9032     Bang(ax, ay);
9033   }
9034   else
9035   {
9036     static int xy[4][2] =
9037     {
9038       { 0, -1 },
9039       { -1, 0 },
9040       { +1, 0 },
9041       { 0, +1 }
9042     };
9043
9044     for (i = 0; i < NUM_DIRECTIONS; i++)
9045     {
9046       x = ax + xy[i][0];
9047       y = ay + xy[i][1];
9048
9049       if (!IN_LEV_FIELD(x, y))
9050         continue;
9051
9052       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9053       {
9054         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9055                               SND_AMOEBA_TURNING_TO_GEM :
9056                               SND_AMOEBA_TURNING_TO_ROCK));
9057         Bang(x, y);
9058       }
9059     }
9060   }
9061 }
9062
9063 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9064 {
9065   int x, y;
9066   int group_nr = AmoebaNr[ax][ay];
9067   boolean done = FALSE;
9068
9069 #ifdef DEBUG
9070   if (group_nr == 0)
9071   {
9072     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9073     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9074
9075     return;
9076   }
9077 #endif
9078
9079   SCAN_PLAYFIELD(x, y)
9080   {
9081     if (AmoebaNr[x][y] == group_nr &&
9082         (Tile[x][y] == EL_AMOEBA_DEAD ||
9083          Tile[x][y] == EL_BD_AMOEBA ||
9084          Tile[x][y] == EL_AMOEBA_GROWING))
9085     {
9086       AmoebaNr[x][y] = 0;
9087       Tile[x][y] = new_element;
9088       InitField(x, y, FALSE);
9089       TEST_DrawLevelField(x, y);
9090       done = TRUE;
9091     }
9092   }
9093
9094   if (done)
9095     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9096                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9097                             SND_BD_AMOEBA_TURNING_TO_GEM));
9098 }
9099
9100 static void AmoebaGrowing(int x, int y)
9101 {
9102   static unsigned int sound_delay = 0;
9103   static unsigned int sound_delay_value = 0;
9104
9105   if (!MovDelay[x][y])          // start new growing cycle
9106   {
9107     MovDelay[x][y] = 7;
9108
9109     if (DelayReached(&sound_delay, sound_delay_value))
9110     {
9111       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9112       sound_delay_value = 30;
9113     }
9114   }
9115
9116   if (MovDelay[x][y])           // wait some time before growing bigger
9117   {
9118     MovDelay[x][y]--;
9119     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9120     {
9121       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9122                                            6 - MovDelay[x][y]);
9123
9124       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9125     }
9126
9127     if (!MovDelay[x][y])
9128     {
9129       Tile[x][y] = Store[x][y];
9130       Store[x][y] = 0;
9131       TEST_DrawLevelField(x, y);
9132     }
9133   }
9134 }
9135
9136 static void AmoebaShrinking(int x, int y)
9137 {
9138   static unsigned int sound_delay = 0;
9139   static unsigned int sound_delay_value = 0;
9140
9141   if (!MovDelay[x][y])          // start new shrinking cycle
9142   {
9143     MovDelay[x][y] = 7;
9144
9145     if (DelayReached(&sound_delay, sound_delay_value))
9146       sound_delay_value = 30;
9147   }
9148
9149   if (MovDelay[x][y])           // wait some time before shrinking
9150   {
9151     MovDelay[x][y]--;
9152     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9153     {
9154       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9155                                            6 - MovDelay[x][y]);
9156
9157       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9158     }
9159
9160     if (!MovDelay[x][y])
9161     {
9162       Tile[x][y] = EL_EMPTY;
9163       TEST_DrawLevelField(x, y);
9164
9165       // don't let mole enter this field in this cycle;
9166       // (give priority to objects falling to this field from above)
9167       Stop[x][y] = TRUE;
9168     }
9169   }
9170 }
9171
9172 static void AmoebaReproduce(int ax, int ay)
9173 {
9174   int i;
9175   int element = Tile[ax][ay];
9176   int graphic = el2img(element);
9177   int newax = ax, neway = ay;
9178   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9179   static int xy[4][2] =
9180   {
9181     { 0, -1 },
9182     { -1, 0 },
9183     { +1, 0 },
9184     { 0, +1 }
9185   };
9186
9187   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9188   {
9189     Tile[ax][ay] = EL_AMOEBA_DEAD;
9190     TEST_DrawLevelField(ax, ay);
9191     return;
9192   }
9193
9194   if (IS_ANIMATED(graphic))
9195     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9196
9197   if (!MovDelay[ax][ay])        // start making new amoeba field
9198     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9199
9200   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9201   {
9202     MovDelay[ax][ay]--;
9203     if (MovDelay[ax][ay])
9204       return;
9205   }
9206
9207   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9208   {
9209     int start = RND(4);
9210     int x = ax + xy[start][0];
9211     int y = ay + xy[start][1];
9212
9213     if (!IN_LEV_FIELD(x, y))
9214       return;
9215
9216     if (IS_FREE(x, y) ||
9217         CAN_GROW_INTO(Tile[x][y]) ||
9218         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9219         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9220     {
9221       newax = x;
9222       neway = y;
9223     }
9224
9225     if (newax == ax && neway == ay)
9226       return;
9227   }
9228   else                          // normal or "filled" (BD style) amoeba
9229   {
9230     int start = RND(4);
9231     boolean waiting_for_player = FALSE;
9232
9233     for (i = 0; i < NUM_DIRECTIONS; i++)
9234     {
9235       int j = (start + i) % 4;
9236       int x = ax + xy[j][0];
9237       int y = ay + xy[j][1];
9238
9239       if (!IN_LEV_FIELD(x, y))
9240         continue;
9241
9242       if (IS_FREE(x, y) ||
9243           CAN_GROW_INTO(Tile[x][y]) ||
9244           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9245           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9246       {
9247         newax = x;
9248         neway = y;
9249         break;
9250       }
9251       else if (IS_PLAYER(x, y))
9252         waiting_for_player = TRUE;
9253     }
9254
9255     if (newax == ax && neway == ay)             // amoeba cannot grow
9256     {
9257       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9258       {
9259         Tile[ax][ay] = EL_AMOEBA_DEAD;
9260         TEST_DrawLevelField(ax, ay);
9261         AmoebaCnt[AmoebaNr[ax][ay]]--;
9262
9263         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9264         {
9265           if (element == EL_AMOEBA_FULL)
9266             AmoebaToDiamond(ax, ay);
9267           else if (element == EL_BD_AMOEBA)
9268             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9269         }
9270       }
9271       return;
9272     }
9273     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9274     {
9275       // amoeba gets larger by growing in some direction
9276
9277       int new_group_nr = AmoebaNr[ax][ay];
9278
9279 #ifdef DEBUG
9280   if (new_group_nr == 0)
9281   {
9282     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9283           newax, neway);
9284     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9285
9286     return;
9287   }
9288 #endif
9289
9290       AmoebaNr[newax][neway] = new_group_nr;
9291       AmoebaCnt[new_group_nr]++;
9292       AmoebaCnt2[new_group_nr]++;
9293
9294       // if amoeba touches other amoeba(s) after growing, unify them
9295       AmoebaMerge(newax, neway);
9296
9297       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9298       {
9299         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9300         return;
9301       }
9302     }
9303   }
9304
9305   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9306       (neway == lev_fieldy - 1 && newax != ax))
9307   {
9308     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9309     Store[newax][neway] = element;
9310   }
9311   else if (neway == ay || element == EL_EMC_DRIPPER)
9312   {
9313     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9314
9315     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9316   }
9317   else
9318   {
9319     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9320     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9321     Store[ax][ay] = EL_AMOEBA_DROP;
9322     ContinueMoving(ax, ay);
9323     return;
9324   }
9325
9326   TEST_DrawLevelField(newax, neway);
9327 }
9328
9329 static void Life(int ax, int ay)
9330 {
9331   int x1, y1, x2, y2;
9332   int life_time = 40;
9333   int element = Tile[ax][ay];
9334   int graphic = el2img(element);
9335   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9336                          level.biomaze);
9337   boolean changed = FALSE;
9338
9339   if (IS_ANIMATED(graphic))
9340     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9341
9342   if (Stop[ax][ay])
9343     return;
9344
9345   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9346     MovDelay[ax][ay] = life_time;
9347
9348   if (MovDelay[ax][ay])         // wait some time before next cycle
9349   {
9350     MovDelay[ax][ay]--;
9351     if (MovDelay[ax][ay])
9352       return;
9353   }
9354
9355   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9356   {
9357     int xx = ax+x1, yy = ay+y1;
9358     int old_element = Tile[xx][yy];
9359     int num_neighbours = 0;
9360
9361     if (!IN_LEV_FIELD(xx, yy))
9362       continue;
9363
9364     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9365     {
9366       int x = xx+x2, y = yy+y2;
9367
9368       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9369         continue;
9370
9371       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9372       boolean is_neighbour = FALSE;
9373
9374       if (level.use_life_bugs)
9375         is_neighbour =
9376           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9377            (IS_FREE(x, y)                             &&  Stop[x][y]));
9378       else
9379         is_neighbour =
9380           (Last[x][y] == element || is_player_cell);
9381
9382       if (is_neighbour)
9383         num_neighbours++;
9384     }
9385
9386     boolean is_free = FALSE;
9387
9388     if (level.use_life_bugs)
9389       is_free = (IS_FREE(xx, yy));
9390     else
9391       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9392
9393     if (xx == ax && yy == ay)           // field in the middle
9394     {
9395       if (num_neighbours < life_parameter[0] ||
9396           num_neighbours > life_parameter[1])
9397       {
9398         Tile[xx][yy] = EL_EMPTY;
9399         if (Tile[xx][yy] != old_element)
9400           TEST_DrawLevelField(xx, yy);
9401         Stop[xx][yy] = TRUE;
9402         changed = TRUE;
9403       }
9404     }
9405     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9406     {                                   // free border field
9407       if (num_neighbours >= life_parameter[2] &&
9408           num_neighbours <= life_parameter[3])
9409       {
9410         Tile[xx][yy] = element;
9411         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9412         if (Tile[xx][yy] != old_element)
9413           TEST_DrawLevelField(xx, yy);
9414         Stop[xx][yy] = TRUE;
9415         changed = TRUE;
9416       }
9417     }
9418   }
9419
9420   if (changed)
9421     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9422                    SND_GAME_OF_LIFE_GROWING);
9423 }
9424
9425 static void InitRobotWheel(int x, int y)
9426 {
9427   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9428 }
9429
9430 static void RunRobotWheel(int x, int y)
9431 {
9432   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9433 }
9434
9435 static void StopRobotWheel(int x, int y)
9436 {
9437   if (game.robot_wheel_x == x &&
9438       game.robot_wheel_y == y)
9439   {
9440     game.robot_wheel_x = -1;
9441     game.robot_wheel_y = -1;
9442     game.robot_wheel_active = FALSE;
9443   }
9444 }
9445
9446 static void InitTimegateWheel(int x, int y)
9447 {
9448   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9449 }
9450
9451 static void RunTimegateWheel(int x, int y)
9452 {
9453   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9454 }
9455
9456 static void InitMagicBallDelay(int x, int y)
9457 {
9458   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9459 }
9460
9461 static void ActivateMagicBall(int bx, int by)
9462 {
9463   int x, y;
9464
9465   if (level.ball_random)
9466   {
9467     int pos_border = RND(8);    // select one of the eight border elements
9468     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9469     int xx = pos_content % 3;
9470     int yy = pos_content / 3;
9471
9472     x = bx - 1 + xx;
9473     y = by - 1 + yy;
9474
9475     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9476       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9477   }
9478   else
9479   {
9480     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9481     {
9482       int xx = x - bx + 1;
9483       int yy = y - by + 1;
9484
9485       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9486         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9487     }
9488   }
9489
9490   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9491 }
9492
9493 static void CheckExit(int x, int y)
9494 {
9495   if (game.gems_still_needed > 0 ||
9496       game.sokoban_fields_still_needed > 0 ||
9497       game.sokoban_objects_still_needed > 0 ||
9498       game.lights_still_needed > 0)
9499   {
9500     int element = Tile[x][y];
9501     int graphic = el2img(element);
9502
9503     if (IS_ANIMATED(graphic))
9504       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9505
9506     return;
9507   }
9508
9509   // do not re-open exit door closed after last player
9510   if (game.all_players_gone)
9511     return;
9512
9513   Tile[x][y] = EL_EXIT_OPENING;
9514
9515   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9516 }
9517
9518 static void CheckExitEM(int x, int y)
9519 {
9520   if (game.gems_still_needed > 0 ||
9521       game.sokoban_fields_still_needed > 0 ||
9522       game.sokoban_objects_still_needed > 0 ||
9523       game.lights_still_needed > 0)
9524   {
9525     int element = Tile[x][y];
9526     int graphic = el2img(element);
9527
9528     if (IS_ANIMATED(graphic))
9529       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9530
9531     return;
9532   }
9533
9534   // do not re-open exit door closed after last player
9535   if (game.all_players_gone)
9536     return;
9537
9538   Tile[x][y] = EL_EM_EXIT_OPENING;
9539
9540   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9541 }
9542
9543 static void CheckExitSteel(int x, int y)
9544 {
9545   if (game.gems_still_needed > 0 ||
9546       game.sokoban_fields_still_needed > 0 ||
9547       game.sokoban_objects_still_needed > 0 ||
9548       game.lights_still_needed > 0)
9549   {
9550     int element = Tile[x][y];
9551     int graphic = el2img(element);
9552
9553     if (IS_ANIMATED(graphic))
9554       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9555
9556     return;
9557   }
9558
9559   // do not re-open exit door closed after last player
9560   if (game.all_players_gone)
9561     return;
9562
9563   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9564
9565   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9566 }
9567
9568 static void CheckExitSteelEM(int x, int y)
9569 {
9570   if (game.gems_still_needed > 0 ||
9571       game.sokoban_fields_still_needed > 0 ||
9572       game.sokoban_objects_still_needed > 0 ||
9573       game.lights_still_needed > 0)
9574   {
9575     int element = Tile[x][y];
9576     int graphic = el2img(element);
9577
9578     if (IS_ANIMATED(graphic))
9579       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9580
9581     return;
9582   }
9583
9584   // do not re-open exit door closed after last player
9585   if (game.all_players_gone)
9586     return;
9587
9588   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9589
9590   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9591 }
9592
9593 static void CheckExitSP(int x, int y)
9594 {
9595   if (game.gems_still_needed > 0)
9596   {
9597     int element = Tile[x][y];
9598     int graphic = el2img(element);
9599
9600     if (IS_ANIMATED(graphic))
9601       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9602
9603     return;
9604   }
9605
9606   // do not re-open exit door closed after last player
9607   if (game.all_players_gone)
9608     return;
9609
9610   Tile[x][y] = EL_SP_EXIT_OPENING;
9611
9612   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9613 }
9614
9615 static void CloseAllOpenTimegates(void)
9616 {
9617   int x, y;
9618
9619   SCAN_PLAYFIELD(x, y)
9620   {
9621     int element = Tile[x][y];
9622
9623     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9624     {
9625       Tile[x][y] = EL_TIMEGATE_CLOSING;
9626
9627       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9628     }
9629   }
9630 }
9631
9632 static void DrawTwinkleOnField(int x, int y)
9633 {
9634   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9635     return;
9636
9637   if (Tile[x][y] == EL_BD_DIAMOND)
9638     return;
9639
9640   if (MovDelay[x][y] == 0)      // next animation frame
9641     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9642
9643   if (MovDelay[x][y] != 0)      // wait some time before next frame
9644   {
9645     MovDelay[x][y]--;
9646
9647     DrawLevelElementAnimation(x, y, Tile[x][y]);
9648
9649     if (MovDelay[x][y] != 0)
9650     {
9651       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9652                                            10 - MovDelay[x][y]);
9653
9654       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9655     }
9656   }
9657 }
9658
9659 static void MauerWaechst(int x, int y)
9660 {
9661   int delay = 6;
9662
9663   if (!MovDelay[x][y])          // next animation frame
9664     MovDelay[x][y] = 3 * delay;
9665
9666   if (MovDelay[x][y])           // wait some time before next frame
9667   {
9668     MovDelay[x][y]--;
9669
9670     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9671     {
9672       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9673       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9674
9675       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9676     }
9677
9678     if (!MovDelay[x][y])
9679     {
9680       if (MovDir[x][y] == MV_LEFT)
9681       {
9682         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9683           TEST_DrawLevelField(x - 1, y);
9684       }
9685       else if (MovDir[x][y] == MV_RIGHT)
9686       {
9687         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9688           TEST_DrawLevelField(x + 1, y);
9689       }
9690       else if (MovDir[x][y] == MV_UP)
9691       {
9692         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9693           TEST_DrawLevelField(x, y - 1);
9694       }
9695       else
9696       {
9697         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9698           TEST_DrawLevelField(x, y + 1);
9699       }
9700
9701       Tile[x][y] = Store[x][y];
9702       Store[x][y] = 0;
9703       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9704       TEST_DrawLevelField(x, y);
9705     }
9706   }
9707 }
9708
9709 static void MauerAbleger(int ax, int ay)
9710 {
9711   int element = Tile[ax][ay];
9712   int graphic = el2img(element);
9713   boolean oben_frei = FALSE, unten_frei = FALSE;
9714   boolean links_frei = FALSE, rechts_frei = FALSE;
9715   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9716   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9717   boolean new_wall = FALSE;
9718
9719   if (IS_ANIMATED(graphic))
9720     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9721
9722   if (!MovDelay[ax][ay])        // start building new wall
9723     MovDelay[ax][ay] = 6;
9724
9725   if (MovDelay[ax][ay])         // wait some time before building new wall
9726   {
9727     MovDelay[ax][ay]--;
9728     if (MovDelay[ax][ay])
9729       return;
9730   }
9731
9732   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9733     oben_frei = TRUE;
9734   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9735     unten_frei = TRUE;
9736   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9737     links_frei = TRUE;
9738   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9739     rechts_frei = TRUE;
9740
9741   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9742       element == EL_EXPANDABLE_WALL_ANY)
9743   {
9744     if (oben_frei)
9745     {
9746       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9747       Store[ax][ay-1] = element;
9748       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9749       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9750         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9751                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9752       new_wall = TRUE;
9753     }
9754     if (unten_frei)
9755     {
9756       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9757       Store[ax][ay+1] = element;
9758       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9759       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9760         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9761                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9762       new_wall = TRUE;
9763     }
9764   }
9765
9766   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9767       element == EL_EXPANDABLE_WALL_ANY ||
9768       element == EL_EXPANDABLE_WALL ||
9769       element == EL_BD_EXPANDABLE_WALL)
9770   {
9771     if (links_frei)
9772     {
9773       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9774       Store[ax-1][ay] = element;
9775       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9776       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9777         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9778                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9779       new_wall = TRUE;
9780     }
9781
9782     if (rechts_frei)
9783     {
9784       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9785       Store[ax+1][ay] = element;
9786       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9787       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9788         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9789                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9790       new_wall = TRUE;
9791     }
9792   }
9793
9794   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9795     TEST_DrawLevelField(ax, ay);
9796
9797   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9798     oben_massiv = TRUE;
9799   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9800     unten_massiv = TRUE;
9801   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9802     links_massiv = TRUE;
9803   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9804     rechts_massiv = TRUE;
9805
9806   if (((oben_massiv && unten_massiv) ||
9807        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9808        element == EL_EXPANDABLE_WALL) &&
9809       ((links_massiv && rechts_massiv) ||
9810        element == EL_EXPANDABLE_WALL_VERTICAL))
9811     Tile[ax][ay] = EL_WALL;
9812
9813   if (new_wall)
9814     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9815 }
9816
9817 static void MauerAblegerStahl(int ax, int ay)
9818 {
9819   int element = Tile[ax][ay];
9820   int graphic = el2img(element);
9821   boolean oben_frei = FALSE, unten_frei = FALSE;
9822   boolean links_frei = FALSE, rechts_frei = FALSE;
9823   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9824   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9825   boolean new_wall = FALSE;
9826
9827   if (IS_ANIMATED(graphic))
9828     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9829
9830   if (!MovDelay[ax][ay])        // start building new wall
9831     MovDelay[ax][ay] = 6;
9832
9833   if (MovDelay[ax][ay])         // wait some time before building new wall
9834   {
9835     MovDelay[ax][ay]--;
9836     if (MovDelay[ax][ay])
9837       return;
9838   }
9839
9840   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9841     oben_frei = TRUE;
9842   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9843     unten_frei = TRUE;
9844   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9845     links_frei = TRUE;
9846   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9847     rechts_frei = TRUE;
9848
9849   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9850       element == EL_EXPANDABLE_STEELWALL_ANY)
9851   {
9852     if (oben_frei)
9853     {
9854       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9855       Store[ax][ay-1] = element;
9856       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9857       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9858         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9859                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9860       new_wall = TRUE;
9861     }
9862     if (unten_frei)
9863     {
9864       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9865       Store[ax][ay+1] = element;
9866       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9867       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9868         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9869                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9870       new_wall = TRUE;
9871     }
9872   }
9873
9874   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9875       element == EL_EXPANDABLE_STEELWALL_ANY)
9876   {
9877     if (links_frei)
9878     {
9879       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9880       Store[ax-1][ay] = element;
9881       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9882       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9883         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9884                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9885       new_wall = TRUE;
9886     }
9887
9888     if (rechts_frei)
9889     {
9890       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9891       Store[ax+1][ay] = element;
9892       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9893       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9894         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9895                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9896       new_wall = TRUE;
9897     }
9898   }
9899
9900   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9901     oben_massiv = TRUE;
9902   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9903     unten_massiv = TRUE;
9904   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9905     links_massiv = TRUE;
9906   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9907     rechts_massiv = TRUE;
9908
9909   if (((oben_massiv && unten_massiv) ||
9910        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9911       ((links_massiv && rechts_massiv) ||
9912        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9913     Tile[ax][ay] = EL_STEELWALL;
9914
9915   if (new_wall)
9916     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9917 }
9918
9919 static void CheckForDragon(int x, int y)
9920 {
9921   int i, j;
9922   boolean dragon_found = FALSE;
9923   static int xy[4][2] =
9924   {
9925     { 0, -1 },
9926     { -1, 0 },
9927     { +1, 0 },
9928     { 0, +1 }
9929   };
9930
9931   for (i = 0; i < NUM_DIRECTIONS; i++)
9932   {
9933     for (j = 0; j < 4; j++)
9934     {
9935       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9936
9937       if (IN_LEV_FIELD(xx, yy) &&
9938           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9939       {
9940         if (Tile[xx][yy] == EL_DRAGON)
9941           dragon_found = TRUE;
9942       }
9943       else
9944         break;
9945     }
9946   }
9947
9948   if (!dragon_found)
9949   {
9950     for (i = 0; i < NUM_DIRECTIONS; i++)
9951     {
9952       for (j = 0; j < 3; j++)
9953       {
9954         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9955   
9956         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9957         {
9958           Tile[xx][yy] = EL_EMPTY;
9959           TEST_DrawLevelField(xx, yy);
9960         }
9961         else
9962           break;
9963       }
9964     }
9965   }
9966 }
9967
9968 static void InitBuggyBase(int x, int y)
9969 {
9970   int element = Tile[x][y];
9971   int activating_delay = FRAMES_PER_SECOND / 4;
9972
9973   ChangeDelay[x][y] =
9974     (element == EL_SP_BUGGY_BASE ?
9975      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9976      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9977      activating_delay :
9978      element == EL_SP_BUGGY_BASE_ACTIVE ?
9979      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9980 }
9981
9982 static void WarnBuggyBase(int x, int y)
9983 {
9984   int i;
9985   static int xy[4][2] =
9986   {
9987     { 0, -1 },
9988     { -1, 0 },
9989     { +1, 0 },
9990     { 0, +1 }
9991   };
9992
9993   for (i = 0; i < NUM_DIRECTIONS; i++)
9994   {
9995     int xx = x + xy[i][0];
9996     int yy = y + xy[i][1];
9997
9998     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9999     {
10000       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10001
10002       break;
10003     }
10004   }
10005 }
10006
10007 static void InitTrap(int x, int y)
10008 {
10009   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10010 }
10011
10012 static void ActivateTrap(int x, int y)
10013 {
10014   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10015 }
10016
10017 static void ChangeActiveTrap(int x, int y)
10018 {
10019   int graphic = IMG_TRAP_ACTIVE;
10020
10021   // if new animation frame was drawn, correct crumbled sand border
10022   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10023     TEST_DrawLevelFieldCrumbled(x, y);
10024 }
10025
10026 static int getSpecialActionElement(int element, int number, int base_element)
10027 {
10028   return (element != EL_EMPTY ? element :
10029           number != -1 ? base_element + number - 1 :
10030           EL_EMPTY);
10031 }
10032
10033 static int getModifiedActionNumber(int value_old, int operator, int operand,
10034                                    int value_min, int value_max)
10035 {
10036   int value_new = (operator == CA_MODE_SET      ? operand :
10037                    operator == CA_MODE_ADD      ? value_old + operand :
10038                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10039                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10040                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10041                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10042                    value_old);
10043
10044   return (value_new < value_min ? value_min :
10045           value_new > value_max ? value_max :
10046           value_new);
10047 }
10048
10049 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10050 {
10051   struct ElementInfo *ei = &element_info[element];
10052   struct ElementChangeInfo *change = &ei->change_page[page];
10053   int target_element = change->target_element;
10054   int action_type = change->action_type;
10055   int action_mode = change->action_mode;
10056   int action_arg = change->action_arg;
10057   int action_element = change->action_element;
10058   int i;
10059
10060   if (!change->has_action)
10061     return;
10062
10063   // ---------- determine action paramater values -----------------------------
10064
10065   int level_time_value =
10066     (level.time > 0 ? TimeLeft :
10067      TimePlayed);
10068
10069   int action_arg_element_raw =
10070     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10071      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10072      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10073      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10074      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10075      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10076      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10077      EL_EMPTY);
10078   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10079
10080   int action_arg_direction =
10081     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10082      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10083      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10084      change->actual_trigger_side :
10085      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10086      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10087      MV_NONE);
10088
10089   int action_arg_number_min =
10090     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10091      CA_ARG_MIN);
10092
10093   int action_arg_number_max =
10094     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10095      action_type == CA_SET_LEVEL_GEMS ? 999 :
10096      action_type == CA_SET_LEVEL_TIME ? 9999 :
10097      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10098      action_type == CA_SET_CE_VALUE ? 9999 :
10099      action_type == CA_SET_CE_SCORE ? 9999 :
10100      CA_ARG_MAX);
10101
10102   int action_arg_number_reset =
10103     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10104      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10105      action_type == CA_SET_LEVEL_TIME ? level.time :
10106      action_type == CA_SET_LEVEL_SCORE ? 0 :
10107      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10108      action_type == CA_SET_CE_SCORE ? 0 :
10109      0);
10110
10111   int action_arg_number =
10112     (action_arg <= CA_ARG_MAX ? action_arg :
10113      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10114      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10115      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10116      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10117      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10118      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10119      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10120      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10121      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10122      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10123      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10124      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10125      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10126      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10127      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10128      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10129      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10130      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10131      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10132      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10133      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10134      -1);
10135
10136   int action_arg_number_old =
10137     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10138      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10139      action_type == CA_SET_LEVEL_SCORE ? game.score :
10140      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10141      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10142      0);
10143
10144   int action_arg_number_new =
10145     getModifiedActionNumber(action_arg_number_old,
10146                             action_mode, action_arg_number,
10147                             action_arg_number_min, action_arg_number_max);
10148
10149   int trigger_player_bits =
10150     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10151      change->actual_trigger_player_bits : change->trigger_player);
10152
10153   int action_arg_player_bits =
10154     (action_arg >= CA_ARG_PLAYER_1 &&
10155      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10156      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10157      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10158      PLAYER_BITS_ANY);
10159
10160   // ---------- execute action  -----------------------------------------------
10161
10162   switch (action_type)
10163   {
10164     case CA_NO_ACTION:
10165     {
10166       return;
10167     }
10168
10169     // ---------- level actions  ----------------------------------------------
10170
10171     case CA_RESTART_LEVEL:
10172     {
10173       game.restart_level = TRUE;
10174
10175       break;
10176     }
10177
10178     case CA_SHOW_ENVELOPE:
10179     {
10180       int element = getSpecialActionElement(action_arg_element,
10181                                             action_arg_number, EL_ENVELOPE_1);
10182
10183       if (IS_ENVELOPE(element))
10184         local_player->show_envelope = element;
10185
10186       break;
10187     }
10188
10189     case CA_SET_LEVEL_TIME:
10190     {
10191       if (level.time > 0)       // only modify limited time value
10192       {
10193         TimeLeft = action_arg_number_new;
10194
10195         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10196
10197         DisplayGameControlValues();
10198
10199         if (!TimeLeft && setup.time_limit)
10200           for (i = 0; i < MAX_PLAYERS; i++)
10201             KillPlayer(&stored_player[i]);
10202       }
10203
10204       break;
10205     }
10206
10207     case CA_SET_LEVEL_SCORE:
10208     {
10209       game.score = action_arg_number_new;
10210
10211       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10212
10213       DisplayGameControlValues();
10214
10215       break;
10216     }
10217
10218     case CA_SET_LEVEL_GEMS:
10219     {
10220       game.gems_still_needed = action_arg_number_new;
10221
10222       game.snapshot.collected_item = TRUE;
10223
10224       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10225
10226       DisplayGameControlValues();
10227
10228       break;
10229     }
10230
10231     case CA_SET_LEVEL_WIND:
10232     {
10233       game.wind_direction = action_arg_direction;
10234
10235       break;
10236     }
10237
10238     case CA_SET_LEVEL_RANDOM_SEED:
10239     {
10240       // ensure that setting a new random seed while playing is predictable
10241       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10242
10243       break;
10244     }
10245
10246     // ---------- player actions  ---------------------------------------------
10247
10248     case CA_MOVE_PLAYER:
10249     case CA_MOVE_PLAYER_NEW:
10250     {
10251       // automatically move to the next field in specified direction
10252       for (i = 0; i < MAX_PLAYERS; i++)
10253         if (trigger_player_bits & (1 << i))
10254           if (action_type == CA_MOVE_PLAYER ||
10255               stored_player[i].MovPos == 0)
10256             stored_player[i].programmed_action = action_arg_direction;
10257
10258       break;
10259     }
10260
10261     case CA_EXIT_PLAYER:
10262     {
10263       for (i = 0; i < MAX_PLAYERS; i++)
10264         if (action_arg_player_bits & (1 << i))
10265           ExitPlayer(&stored_player[i]);
10266
10267       if (game.players_still_needed == 0)
10268         LevelSolved();
10269
10270       break;
10271     }
10272
10273     case CA_KILL_PLAYER:
10274     {
10275       for (i = 0; i < MAX_PLAYERS; i++)
10276         if (action_arg_player_bits & (1 << i))
10277           KillPlayer(&stored_player[i]);
10278
10279       break;
10280     }
10281
10282     case CA_SET_PLAYER_KEYS:
10283     {
10284       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10285       int element = getSpecialActionElement(action_arg_element,
10286                                             action_arg_number, EL_KEY_1);
10287
10288       if (IS_KEY(element))
10289       {
10290         for (i = 0; i < MAX_PLAYERS; i++)
10291         {
10292           if (trigger_player_bits & (1 << i))
10293           {
10294             stored_player[i].key[KEY_NR(element)] = key_state;
10295
10296             DrawGameDoorValues();
10297           }
10298         }
10299       }
10300
10301       break;
10302     }
10303
10304     case CA_SET_PLAYER_SPEED:
10305     {
10306       for (i = 0; i < MAX_PLAYERS; i++)
10307       {
10308         if (trigger_player_bits & (1 << i))
10309         {
10310           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10311
10312           if (action_arg == CA_ARG_SPEED_FASTER &&
10313               stored_player[i].cannot_move)
10314           {
10315             action_arg_number = STEPSIZE_VERY_SLOW;
10316           }
10317           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10318                    action_arg == CA_ARG_SPEED_FASTER)
10319           {
10320             action_arg_number = 2;
10321             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10322                            CA_MODE_MULTIPLY);
10323           }
10324           else if (action_arg == CA_ARG_NUMBER_RESET)
10325           {
10326             action_arg_number = level.initial_player_stepsize[i];
10327           }
10328
10329           move_stepsize =
10330             getModifiedActionNumber(move_stepsize,
10331                                     action_mode,
10332                                     action_arg_number,
10333                                     action_arg_number_min,
10334                                     action_arg_number_max);
10335
10336           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10337         }
10338       }
10339
10340       break;
10341     }
10342
10343     case CA_SET_PLAYER_SHIELD:
10344     {
10345       for (i = 0; i < MAX_PLAYERS; i++)
10346       {
10347         if (trigger_player_bits & (1 << i))
10348         {
10349           if (action_arg == CA_ARG_SHIELD_OFF)
10350           {
10351             stored_player[i].shield_normal_time_left = 0;
10352             stored_player[i].shield_deadly_time_left = 0;
10353           }
10354           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10355           {
10356             stored_player[i].shield_normal_time_left = 999999;
10357           }
10358           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10359           {
10360             stored_player[i].shield_normal_time_left = 999999;
10361             stored_player[i].shield_deadly_time_left = 999999;
10362           }
10363         }
10364       }
10365
10366       break;
10367     }
10368
10369     case CA_SET_PLAYER_GRAVITY:
10370     {
10371       for (i = 0; i < MAX_PLAYERS; i++)
10372       {
10373         if (trigger_player_bits & (1 << i))
10374         {
10375           stored_player[i].gravity =
10376             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10377              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10378              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10379              stored_player[i].gravity);
10380         }
10381       }
10382
10383       break;
10384     }
10385
10386     case CA_SET_PLAYER_ARTWORK:
10387     {
10388       for (i = 0; i < MAX_PLAYERS; i++)
10389       {
10390         if (trigger_player_bits & (1 << i))
10391         {
10392           int artwork_element = action_arg_element;
10393
10394           if (action_arg == CA_ARG_ELEMENT_RESET)
10395             artwork_element =
10396               (level.use_artwork_element[i] ? level.artwork_element[i] :
10397                stored_player[i].element_nr);
10398
10399           if (stored_player[i].artwork_element != artwork_element)
10400             stored_player[i].Frame = 0;
10401
10402           stored_player[i].artwork_element = artwork_element;
10403
10404           SetPlayerWaiting(&stored_player[i], FALSE);
10405
10406           // set number of special actions for bored and sleeping animation
10407           stored_player[i].num_special_action_bored =
10408             get_num_special_action(artwork_element,
10409                                    ACTION_BORING_1, ACTION_BORING_LAST);
10410           stored_player[i].num_special_action_sleeping =
10411             get_num_special_action(artwork_element,
10412                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10413         }
10414       }
10415
10416       break;
10417     }
10418
10419     case CA_SET_PLAYER_INVENTORY:
10420     {
10421       for (i = 0; i < MAX_PLAYERS; i++)
10422       {
10423         struct PlayerInfo *player = &stored_player[i];
10424         int j, k;
10425
10426         if (trigger_player_bits & (1 << i))
10427         {
10428           int inventory_element = action_arg_element;
10429
10430           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10431               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10432               action_arg == CA_ARG_ELEMENT_ACTION)
10433           {
10434             int element = inventory_element;
10435             int collect_count = element_info[element].collect_count_initial;
10436
10437             if (!IS_CUSTOM_ELEMENT(element))
10438               collect_count = 1;
10439
10440             if (collect_count == 0)
10441               player->inventory_infinite_element = element;
10442             else
10443               for (k = 0; k < collect_count; k++)
10444                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10445                   player->inventory_element[player->inventory_size++] =
10446                     element;
10447           }
10448           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10449                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10450                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10451           {
10452             if (player->inventory_infinite_element != EL_UNDEFINED &&
10453                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10454                                      action_arg_element_raw))
10455               player->inventory_infinite_element = EL_UNDEFINED;
10456
10457             for (k = 0, j = 0; j < player->inventory_size; j++)
10458             {
10459               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10460                                         action_arg_element_raw))
10461                 player->inventory_element[k++] = player->inventory_element[j];
10462             }
10463
10464             player->inventory_size = k;
10465           }
10466           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10467           {
10468             if (player->inventory_size > 0)
10469             {
10470               for (j = 0; j < player->inventory_size - 1; j++)
10471                 player->inventory_element[j] = player->inventory_element[j + 1];
10472
10473               player->inventory_size--;
10474             }
10475           }
10476           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10477           {
10478             if (player->inventory_size > 0)
10479               player->inventory_size--;
10480           }
10481           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10482           {
10483             player->inventory_infinite_element = EL_UNDEFINED;
10484             player->inventory_size = 0;
10485           }
10486           else if (action_arg == CA_ARG_INVENTORY_RESET)
10487           {
10488             player->inventory_infinite_element = EL_UNDEFINED;
10489             player->inventory_size = 0;
10490
10491             if (level.use_initial_inventory[i])
10492             {
10493               for (j = 0; j < level.initial_inventory_size[i]; j++)
10494               {
10495                 int element = level.initial_inventory_content[i][j];
10496                 int collect_count = element_info[element].collect_count_initial;
10497
10498                 if (!IS_CUSTOM_ELEMENT(element))
10499                   collect_count = 1;
10500
10501                 if (collect_count == 0)
10502                   player->inventory_infinite_element = element;
10503                 else
10504                   for (k = 0; k < collect_count; k++)
10505                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10506                       player->inventory_element[player->inventory_size++] =
10507                         element;
10508               }
10509             }
10510           }
10511         }
10512       }
10513
10514       break;
10515     }
10516
10517     // ---------- CE actions  -------------------------------------------------
10518
10519     case CA_SET_CE_VALUE:
10520     {
10521       int last_ce_value = CustomValue[x][y];
10522
10523       CustomValue[x][y] = action_arg_number_new;
10524
10525       if (CustomValue[x][y] != last_ce_value)
10526       {
10527         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10528         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10529
10530         if (CustomValue[x][y] == 0)
10531         {
10532           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10533           ChangeCount[x][y] = 0;        // allow at least one more change
10534
10535           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10536           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10537         }
10538       }
10539
10540       break;
10541     }
10542
10543     case CA_SET_CE_SCORE:
10544     {
10545       int last_ce_score = ei->collect_score;
10546
10547       ei->collect_score = action_arg_number_new;
10548
10549       if (ei->collect_score != last_ce_score)
10550       {
10551         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10552         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10553
10554         if (ei->collect_score == 0)
10555         {
10556           int xx, yy;
10557
10558           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10559           ChangeCount[x][y] = 0;        // allow at least one more change
10560
10561           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10562           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10563
10564           /*
10565             This is a very special case that seems to be a mixture between
10566             CheckElementChange() and CheckTriggeredElementChange(): while
10567             the first one only affects single elements that are triggered
10568             directly, the second one affects multiple elements in the playfield
10569             that are triggered indirectly by another element. This is a third
10570             case: Changing the CE score always affects multiple identical CEs,
10571             so every affected CE must be checked, not only the single CE for
10572             which the CE score was changed in the first place (as every instance
10573             of that CE shares the same CE score, and therefore also can change)!
10574           */
10575           SCAN_PLAYFIELD(xx, yy)
10576           {
10577             if (Tile[xx][yy] == element)
10578               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10579                                  CE_SCORE_GETS_ZERO);
10580           }
10581         }
10582       }
10583
10584       break;
10585     }
10586
10587     case CA_SET_CE_ARTWORK:
10588     {
10589       int artwork_element = action_arg_element;
10590       boolean reset_frame = FALSE;
10591       int xx, yy;
10592
10593       if (action_arg == CA_ARG_ELEMENT_RESET)
10594         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10595                            element);
10596
10597       if (ei->gfx_element != artwork_element)
10598         reset_frame = TRUE;
10599
10600       ei->gfx_element = artwork_element;
10601
10602       SCAN_PLAYFIELD(xx, yy)
10603       {
10604         if (Tile[xx][yy] == element)
10605         {
10606           if (reset_frame)
10607           {
10608             ResetGfxAnimation(xx, yy);
10609             ResetRandomAnimationValue(xx, yy);
10610           }
10611
10612           TEST_DrawLevelField(xx, yy);
10613         }
10614       }
10615
10616       break;
10617     }
10618
10619     // ---------- engine actions  ---------------------------------------------
10620
10621     case CA_SET_ENGINE_SCAN_MODE:
10622     {
10623       InitPlayfieldScanMode(action_arg);
10624
10625       break;
10626     }
10627
10628     default:
10629       break;
10630   }
10631 }
10632
10633 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10634 {
10635   int old_element = Tile[x][y];
10636   int new_element = GetElementFromGroupElement(element);
10637   int previous_move_direction = MovDir[x][y];
10638   int last_ce_value = CustomValue[x][y];
10639   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10640   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10641   boolean add_player_onto_element = (new_element_is_player &&
10642                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10643                                      IS_WALKABLE(old_element));
10644
10645   if (!add_player_onto_element)
10646   {
10647     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10648       RemoveMovingField(x, y);
10649     else
10650       RemoveField(x, y);
10651
10652     Tile[x][y] = new_element;
10653
10654     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10655       MovDir[x][y] = previous_move_direction;
10656
10657     if (element_info[new_element].use_last_ce_value)
10658       CustomValue[x][y] = last_ce_value;
10659
10660     InitField_WithBug1(x, y, FALSE);
10661
10662     new_element = Tile[x][y];   // element may have changed
10663
10664     ResetGfxAnimation(x, y);
10665     ResetRandomAnimationValue(x, y);
10666
10667     TEST_DrawLevelField(x, y);
10668
10669     if (GFX_CRUMBLED(new_element))
10670       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10671   }
10672
10673   // check if element under the player changes from accessible to unaccessible
10674   // (needed for special case of dropping element which then changes)
10675   // (must be checked after creating new element for walkable group elements)
10676   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10677       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10678   {
10679     Bang(x, y);
10680
10681     return;
10682   }
10683
10684   // "ChangeCount" not set yet to allow "entered by player" change one time
10685   if (new_element_is_player)
10686     RelocatePlayer(x, y, new_element);
10687
10688   if (is_change)
10689     ChangeCount[x][y]++;        // count number of changes in the same frame
10690
10691   TestIfBadThingTouchesPlayer(x, y);
10692   TestIfPlayerTouchesCustomElement(x, y);
10693   TestIfElementTouchesCustomElement(x, y);
10694 }
10695
10696 static void CreateField(int x, int y, int element)
10697 {
10698   CreateFieldExt(x, y, element, FALSE);
10699 }
10700
10701 static void CreateElementFromChange(int x, int y, int element)
10702 {
10703   element = GET_VALID_RUNTIME_ELEMENT(element);
10704
10705   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10706   {
10707     int old_element = Tile[x][y];
10708
10709     // prevent changed element from moving in same engine frame
10710     // unless both old and new element can either fall or move
10711     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10712         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10713       Stop[x][y] = TRUE;
10714   }
10715
10716   CreateFieldExt(x, y, element, TRUE);
10717 }
10718
10719 static boolean ChangeElement(int x, int y, int element, int page)
10720 {
10721   struct ElementInfo *ei = &element_info[element];
10722   struct ElementChangeInfo *change = &ei->change_page[page];
10723   int ce_value = CustomValue[x][y];
10724   int ce_score = ei->collect_score;
10725   int target_element;
10726   int old_element = Tile[x][y];
10727
10728   // always use default change event to prevent running into a loop
10729   if (ChangeEvent[x][y] == -1)
10730     ChangeEvent[x][y] = CE_DELAY;
10731
10732   if (ChangeEvent[x][y] == CE_DELAY)
10733   {
10734     // reset actual trigger element, trigger player and action element
10735     change->actual_trigger_element = EL_EMPTY;
10736     change->actual_trigger_player = EL_EMPTY;
10737     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10738     change->actual_trigger_side = CH_SIDE_NONE;
10739     change->actual_trigger_ce_value = 0;
10740     change->actual_trigger_ce_score = 0;
10741   }
10742
10743   // do not change elements more than a specified maximum number of changes
10744   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10745     return FALSE;
10746
10747   ChangeCount[x][y]++;          // count number of changes in the same frame
10748
10749   if (change->explode)
10750   {
10751     Bang(x, y);
10752
10753     return TRUE;
10754   }
10755
10756   if (change->use_target_content)
10757   {
10758     boolean complete_replace = TRUE;
10759     boolean can_replace[3][3];
10760     int xx, yy;
10761
10762     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10763     {
10764       boolean is_empty;
10765       boolean is_walkable;
10766       boolean is_diggable;
10767       boolean is_collectible;
10768       boolean is_removable;
10769       boolean is_destructible;
10770       int ex = x + xx - 1;
10771       int ey = y + yy - 1;
10772       int content_element = change->target_content.e[xx][yy];
10773       int e;
10774
10775       can_replace[xx][yy] = TRUE;
10776
10777       if (ex == x && ey == y)   // do not check changing element itself
10778         continue;
10779
10780       if (content_element == EL_EMPTY_SPACE)
10781       {
10782         can_replace[xx][yy] = FALSE;    // do not replace border with space
10783
10784         continue;
10785       }
10786
10787       if (!IN_LEV_FIELD(ex, ey))
10788       {
10789         can_replace[xx][yy] = FALSE;
10790         complete_replace = FALSE;
10791
10792         continue;
10793       }
10794
10795       e = Tile[ex][ey];
10796
10797       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10798         e = MovingOrBlocked2Element(ex, ey);
10799
10800       is_empty = (IS_FREE(ex, ey) ||
10801                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10802
10803       is_walkable     = (is_empty || IS_WALKABLE(e));
10804       is_diggable     = (is_empty || IS_DIGGABLE(e));
10805       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10806       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10807       is_removable    = (is_diggable || is_collectible);
10808
10809       can_replace[xx][yy] =
10810         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10811           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10812           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10813           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10814           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10815           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10816          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10817
10818       if (!can_replace[xx][yy])
10819         complete_replace = FALSE;
10820     }
10821
10822     if (!change->only_if_complete || complete_replace)
10823     {
10824       boolean something_has_changed = FALSE;
10825
10826       if (change->only_if_complete && change->use_random_replace &&
10827           RND(100) < change->random_percentage)
10828         return FALSE;
10829
10830       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10831       {
10832         int ex = x + xx - 1;
10833         int ey = y + yy - 1;
10834         int content_element;
10835
10836         if (can_replace[xx][yy] && (!change->use_random_replace ||
10837                                     RND(100) < change->random_percentage))
10838         {
10839           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10840             RemoveMovingField(ex, ey);
10841
10842           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10843
10844           content_element = change->target_content.e[xx][yy];
10845           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10846                                               ce_value, ce_score);
10847
10848           CreateElementFromChange(ex, ey, target_element);
10849
10850           something_has_changed = TRUE;
10851
10852           // for symmetry reasons, freeze newly created border elements
10853           if (ex != x || ey != y)
10854             Stop[ex][ey] = TRUE;        // no more moving in this frame
10855         }
10856       }
10857
10858       if (something_has_changed)
10859       {
10860         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10861         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10862       }
10863     }
10864   }
10865   else
10866   {
10867     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10868                                         ce_value, ce_score);
10869
10870     if (element == EL_DIAGONAL_GROWING ||
10871         element == EL_DIAGONAL_SHRINKING)
10872     {
10873       target_element = Store[x][y];
10874
10875       Store[x][y] = EL_EMPTY;
10876     }
10877
10878     // special case: element changes to player (and may be kept if walkable)
10879     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10880       CreateElementFromChange(x, y, EL_EMPTY);
10881
10882     CreateElementFromChange(x, y, target_element);
10883
10884     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10885     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10886   }
10887
10888   // this uses direct change before indirect change
10889   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10890
10891   return TRUE;
10892 }
10893
10894 static void HandleElementChange(int x, int y, int page)
10895 {
10896   int element = MovingOrBlocked2Element(x, y);
10897   struct ElementInfo *ei = &element_info[element];
10898   struct ElementChangeInfo *change = &ei->change_page[page];
10899   boolean handle_action_before_change = FALSE;
10900
10901 #ifdef DEBUG
10902   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10903       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10904   {
10905     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10906           x, y, element, element_info[element].token_name);
10907     Debug("game:playing:HandleElementChange", "This should never happen!");
10908   }
10909 #endif
10910
10911   // this can happen with classic bombs on walkable, changing elements
10912   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10913   {
10914     return;
10915   }
10916
10917   if (ChangeDelay[x][y] == 0)           // initialize element change
10918   {
10919     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10920
10921     if (change->can_change)
10922     {
10923       // !!! not clear why graphic animation should be reset at all here !!!
10924       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10925       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10926
10927       /*
10928         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10929
10930         When using an animation frame delay of 1 (this only happens with
10931         "sp_zonk.moving.left/right" in the classic graphics), the default
10932         (non-moving) animation shows wrong animation frames (while the
10933         moving animation, like "sp_zonk.moving.left/right", is correct,
10934         so this graphical bug never shows up with the classic graphics).
10935         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10936         be drawn instead of the correct frames 0,1,2,3. This is caused by
10937         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10938         an element change: First when the change delay ("ChangeDelay[][]")
10939         counter has reached zero after decrementing, then a second time in
10940         the next frame (after "GfxFrame[][]" was already incremented) when
10941         "ChangeDelay[][]" is reset to the initial delay value again.
10942
10943         This causes frame 0 to be drawn twice, while the last frame won't
10944         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10945
10946         As some animations may already be cleverly designed around this bug
10947         (at least the "Snake Bite" snake tail animation does this), it cannot
10948         simply be fixed here without breaking such existing animations.
10949         Unfortunately, it cannot easily be detected if a graphics set was
10950         designed "before" or "after" the bug was fixed. As a workaround,
10951         a new graphics set option "game.graphics_engine_version" was added
10952         to be able to specify the game's major release version for which the
10953         graphics set was designed, which can then be used to decide if the
10954         bugfix should be used (version 4 and above) or not (version 3 or
10955         below, or if no version was specified at all, as with old sets).
10956
10957         (The wrong/fixed animation frames can be tested with the test level set
10958         "test_gfxframe" and level "000", which contains a specially prepared
10959         custom element at level position (x/y) == (11/9) which uses the zonk
10960         animation mentioned above. Using "game.graphics_engine_version: 4"
10961         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10962         This can also be seen from the debug output for this test element.)
10963       */
10964
10965       // when a custom element is about to change (for example by change delay),
10966       // do not reset graphic animation when the custom element is moving
10967       if (game.graphics_engine_version < 4 &&
10968           !IS_MOVING(x, y))
10969       {
10970         ResetGfxAnimation(x, y);
10971         ResetRandomAnimationValue(x, y);
10972       }
10973
10974       if (change->pre_change_function)
10975         change->pre_change_function(x, y);
10976     }
10977   }
10978
10979   ChangeDelay[x][y]--;
10980
10981   if (ChangeDelay[x][y] != 0)           // continue element change
10982   {
10983     if (change->can_change)
10984     {
10985       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10986
10987       if (IS_ANIMATED(graphic))
10988         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10989
10990       if (change->change_function)
10991         change->change_function(x, y);
10992     }
10993   }
10994   else                                  // finish element change
10995   {
10996     if (ChangePage[x][y] != -1)         // remember page from delayed change
10997     {
10998       page = ChangePage[x][y];
10999       ChangePage[x][y] = -1;
11000
11001       change = &ei->change_page[page];
11002     }
11003
11004     if (IS_MOVING(x, y))                // never change a running system ;-)
11005     {
11006       ChangeDelay[x][y] = 1;            // try change after next move step
11007       ChangePage[x][y] = page;          // remember page to use for change
11008
11009       return;
11010     }
11011
11012     // special case: set new level random seed before changing element
11013     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11014       handle_action_before_change = TRUE;
11015
11016     if (change->has_action && handle_action_before_change)
11017       ExecuteCustomElementAction(x, y, element, page);
11018
11019     if (change->can_change)
11020     {
11021       if (ChangeElement(x, y, element, page))
11022       {
11023         if (change->post_change_function)
11024           change->post_change_function(x, y);
11025       }
11026     }
11027
11028     if (change->has_action && !handle_action_before_change)
11029       ExecuteCustomElementAction(x, y, element, page);
11030   }
11031 }
11032
11033 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11034                                               int trigger_element,
11035                                               int trigger_event,
11036                                               int trigger_player,
11037                                               int trigger_side,
11038                                               int trigger_page)
11039 {
11040   boolean change_done_any = FALSE;
11041   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11042   int i;
11043
11044   if (!(trigger_events[trigger_element][trigger_event]))
11045     return FALSE;
11046
11047   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11048
11049   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11050   {
11051     int element = EL_CUSTOM_START + i;
11052     boolean change_done = FALSE;
11053     int p;
11054
11055     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11056         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11057       continue;
11058
11059     for (p = 0; p < element_info[element].num_change_pages; p++)
11060     {
11061       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11062
11063       if (change->can_change_or_has_action &&
11064           change->has_event[trigger_event] &&
11065           change->trigger_side & trigger_side &&
11066           change->trigger_player & trigger_player &&
11067           change->trigger_page & trigger_page_bits &&
11068           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11069       {
11070         change->actual_trigger_element = trigger_element;
11071         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11072         change->actual_trigger_player_bits = trigger_player;
11073         change->actual_trigger_side = trigger_side;
11074         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11075         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11076
11077         if ((change->can_change && !change_done) || change->has_action)
11078         {
11079           int x, y;
11080
11081           SCAN_PLAYFIELD(x, y)
11082           {
11083             if (Tile[x][y] == element)
11084             {
11085               if (change->can_change && !change_done)
11086               {
11087                 // if element already changed in this frame, not only prevent
11088                 // another element change (checked in ChangeElement()), but
11089                 // also prevent additional element actions for this element
11090
11091                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11092                     !level.use_action_after_change_bug)
11093                   continue;
11094
11095                 ChangeDelay[x][y] = 1;
11096                 ChangeEvent[x][y] = trigger_event;
11097
11098                 HandleElementChange(x, y, p);
11099               }
11100               else if (change->has_action)
11101               {
11102                 // if element already changed in this frame, not only prevent
11103                 // another element change (checked in ChangeElement()), but
11104                 // also prevent additional element actions for this element
11105
11106                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11107                     !level.use_action_after_change_bug)
11108                   continue;
11109
11110                 ExecuteCustomElementAction(x, y, element, p);
11111                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11112               }
11113             }
11114           }
11115
11116           if (change->can_change)
11117           {
11118             change_done = TRUE;
11119             change_done_any = TRUE;
11120           }
11121         }
11122       }
11123     }
11124   }
11125
11126   RECURSION_LOOP_DETECTION_END();
11127
11128   return change_done_any;
11129 }
11130
11131 static boolean CheckElementChangeExt(int x, int y,
11132                                      int element,
11133                                      int trigger_element,
11134                                      int trigger_event,
11135                                      int trigger_player,
11136                                      int trigger_side)
11137 {
11138   boolean change_done = FALSE;
11139   int p;
11140
11141   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11142       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11143     return FALSE;
11144
11145   if (Tile[x][y] == EL_BLOCKED)
11146   {
11147     Blocked2Moving(x, y, &x, &y);
11148     element = Tile[x][y];
11149   }
11150
11151   // check if element has already changed or is about to change after moving
11152   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11153        Tile[x][y] != element) ||
11154
11155       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11156        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11157         ChangePage[x][y] != -1)))
11158     return FALSE;
11159
11160   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11161
11162   for (p = 0; p < element_info[element].num_change_pages; p++)
11163   {
11164     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11165
11166     /* check trigger element for all events where the element that is checked
11167        for changing interacts with a directly adjacent element -- this is
11168        different to element changes that affect other elements to change on the
11169        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11170     boolean check_trigger_element =
11171       (trigger_event == CE_NEXT_TO_X ||
11172        trigger_event == CE_TOUCHING_X ||
11173        trigger_event == CE_HITTING_X ||
11174        trigger_event == CE_HIT_BY_X ||
11175        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11176
11177     if (change->can_change_or_has_action &&
11178         change->has_event[trigger_event] &&
11179         change->trigger_side & trigger_side &&
11180         change->trigger_player & trigger_player &&
11181         (!check_trigger_element ||
11182          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11183     {
11184       change->actual_trigger_element = trigger_element;
11185       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11186       change->actual_trigger_player_bits = trigger_player;
11187       change->actual_trigger_side = trigger_side;
11188       change->actual_trigger_ce_value = CustomValue[x][y];
11189       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11190
11191       // special case: trigger element not at (x,y) position for some events
11192       if (check_trigger_element)
11193       {
11194         static struct
11195         {
11196           int dx, dy;
11197         } move_xy[] =
11198           {
11199             {  0,  0 },
11200             { -1,  0 },
11201             { +1,  0 },
11202             {  0,  0 },
11203             {  0, -1 },
11204             {  0,  0 }, { 0, 0 }, { 0, 0 },
11205             {  0, +1 }
11206           };
11207
11208         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11209         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11210
11211         change->actual_trigger_ce_value = CustomValue[xx][yy];
11212         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11213       }
11214
11215       if (change->can_change && !change_done)
11216       {
11217         ChangeDelay[x][y] = 1;
11218         ChangeEvent[x][y] = trigger_event;
11219
11220         HandleElementChange(x, y, p);
11221
11222         change_done = TRUE;
11223       }
11224       else if (change->has_action)
11225       {
11226         ExecuteCustomElementAction(x, y, element, p);
11227         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11228       }
11229     }
11230   }
11231
11232   RECURSION_LOOP_DETECTION_END();
11233
11234   return change_done;
11235 }
11236
11237 static void PlayPlayerSound(struct PlayerInfo *player)
11238 {
11239   int jx = player->jx, jy = player->jy;
11240   int sound_element = player->artwork_element;
11241   int last_action = player->last_action_waiting;
11242   int action = player->action_waiting;
11243
11244   if (player->is_waiting)
11245   {
11246     if (action != last_action)
11247       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11248     else
11249       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11250   }
11251   else
11252   {
11253     if (action != last_action)
11254       StopSound(element_info[sound_element].sound[last_action]);
11255
11256     if (last_action == ACTION_SLEEPING)
11257       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11258   }
11259 }
11260
11261 static void PlayAllPlayersSound(void)
11262 {
11263   int i;
11264
11265   for (i = 0; i < MAX_PLAYERS; i++)
11266     if (stored_player[i].active)
11267       PlayPlayerSound(&stored_player[i]);
11268 }
11269
11270 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11271 {
11272   boolean last_waiting = player->is_waiting;
11273   int move_dir = player->MovDir;
11274
11275   player->dir_waiting = move_dir;
11276   player->last_action_waiting = player->action_waiting;
11277
11278   if (is_waiting)
11279   {
11280     if (!last_waiting)          // not waiting -> waiting
11281     {
11282       player->is_waiting = TRUE;
11283
11284       player->frame_counter_bored =
11285         FrameCounter +
11286         game.player_boring_delay_fixed +
11287         GetSimpleRandom(game.player_boring_delay_random);
11288       player->frame_counter_sleeping =
11289         FrameCounter +
11290         game.player_sleeping_delay_fixed +
11291         GetSimpleRandom(game.player_sleeping_delay_random);
11292
11293       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11294     }
11295
11296     if (game.player_sleeping_delay_fixed +
11297         game.player_sleeping_delay_random > 0 &&
11298         player->anim_delay_counter == 0 &&
11299         player->post_delay_counter == 0 &&
11300         FrameCounter >= player->frame_counter_sleeping)
11301       player->is_sleeping = TRUE;
11302     else if (game.player_boring_delay_fixed +
11303              game.player_boring_delay_random > 0 &&
11304              FrameCounter >= player->frame_counter_bored)
11305       player->is_bored = TRUE;
11306
11307     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11308                               player->is_bored ? ACTION_BORING :
11309                               ACTION_WAITING);
11310
11311     if (player->is_sleeping && player->use_murphy)
11312     {
11313       // special case for sleeping Murphy when leaning against non-free tile
11314
11315       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11316           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11317            !IS_MOVING(player->jx - 1, player->jy)))
11318         move_dir = MV_LEFT;
11319       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11320                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11321                 !IS_MOVING(player->jx + 1, player->jy)))
11322         move_dir = MV_RIGHT;
11323       else
11324         player->is_sleeping = FALSE;
11325
11326       player->dir_waiting = move_dir;
11327     }
11328
11329     if (player->is_sleeping)
11330     {
11331       if (player->num_special_action_sleeping > 0)
11332       {
11333         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11334         {
11335           int last_special_action = player->special_action_sleeping;
11336           int num_special_action = player->num_special_action_sleeping;
11337           int special_action =
11338             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11339              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11340              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11341              last_special_action + 1 : ACTION_SLEEPING);
11342           int special_graphic =
11343             el_act_dir2img(player->artwork_element, special_action, move_dir);
11344
11345           player->anim_delay_counter =
11346             graphic_info[special_graphic].anim_delay_fixed +
11347             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11348           player->post_delay_counter =
11349             graphic_info[special_graphic].post_delay_fixed +
11350             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11351
11352           player->special_action_sleeping = special_action;
11353         }
11354
11355         if (player->anim_delay_counter > 0)
11356         {
11357           player->action_waiting = player->special_action_sleeping;
11358           player->anim_delay_counter--;
11359         }
11360         else if (player->post_delay_counter > 0)
11361         {
11362           player->post_delay_counter--;
11363         }
11364       }
11365     }
11366     else if (player->is_bored)
11367     {
11368       if (player->num_special_action_bored > 0)
11369       {
11370         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11371         {
11372           int special_action =
11373             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11374           int special_graphic =
11375             el_act_dir2img(player->artwork_element, special_action, move_dir);
11376
11377           player->anim_delay_counter =
11378             graphic_info[special_graphic].anim_delay_fixed +
11379             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11380           player->post_delay_counter =
11381             graphic_info[special_graphic].post_delay_fixed +
11382             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11383
11384           player->special_action_bored = special_action;
11385         }
11386
11387         if (player->anim_delay_counter > 0)
11388         {
11389           player->action_waiting = player->special_action_bored;
11390           player->anim_delay_counter--;
11391         }
11392         else if (player->post_delay_counter > 0)
11393         {
11394           player->post_delay_counter--;
11395         }
11396       }
11397     }
11398   }
11399   else if (last_waiting)        // waiting -> not waiting
11400   {
11401     player->is_waiting = FALSE;
11402     player->is_bored = FALSE;
11403     player->is_sleeping = FALSE;
11404
11405     player->frame_counter_bored = -1;
11406     player->frame_counter_sleeping = -1;
11407
11408     player->anim_delay_counter = 0;
11409     player->post_delay_counter = 0;
11410
11411     player->dir_waiting = player->MovDir;
11412     player->action_waiting = ACTION_DEFAULT;
11413
11414     player->special_action_bored = ACTION_DEFAULT;
11415     player->special_action_sleeping = ACTION_DEFAULT;
11416   }
11417 }
11418
11419 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11420 {
11421   if ((!player->is_moving  && player->was_moving) ||
11422       (player->MovPos == 0 && player->was_moving) ||
11423       (player->is_snapping && !player->was_snapping) ||
11424       (player->is_dropping && !player->was_dropping))
11425   {
11426     if (!CheckSaveEngineSnapshotToList())
11427       return;
11428
11429     player->was_moving = FALSE;
11430     player->was_snapping = TRUE;
11431     player->was_dropping = TRUE;
11432   }
11433   else
11434   {
11435     if (player->is_moving)
11436       player->was_moving = TRUE;
11437
11438     if (!player->is_snapping)
11439       player->was_snapping = FALSE;
11440
11441     if (!player->is_dropping)
11442       player->was_dropping = FALSE;
11443   }
11444
11445   static struct MouseActionInfo mouse_action_last = { 0 };
11446   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11447   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11448
11449   if (new_released)
11450     CheckSaveEngineSnapshotToList();
11451
11452   mouse_action_last = mouse_action;
11453 }
11454
11455 static void CheckSingleStepMode(struct PlayerInfo *player)
11456 {
11457   if (tape.single_step && tape.recording && !tape.pausing)
11458   {
11459     // as it is called "single step mode", just return to pause mode when the
11460     // player stopped moving after one tile (or never starts moving at all)
11461     // (reverse logic needed here in case single step mode used in team mode)
11462     if (player->is_moving ||
11463         player->is_pushing ||
11464         player->is_dropping_pressed ||
11465         player->effective_mouse_action.button)
11466       game.enter_single_step_mode = FALSE;
11467   }
11468
11469   CheckSaveEngineSnapshot(player);
11470 }
11471
11472 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11473 {
11474   int left      = player_action & JOY_LEFT;
11475   int right     = player_action & JOY_RIGHT;
11476   int up        = player_action & JOY_UP;
11477   int down      = player_action & JOY_DOWN;
11478   int button1   = player_action & JOY_BUTTON_1;
11479   int button2   = player_action & JOY_BUTTON_2;
11480   int dx        = (left ? -1 : right ? 1 : 0);
11481   int dy        = (up   ? -1 : down  ? 1 : 0);
11482
11483   if (!player->active || tape.pausing)
11484     return 0;
11485
11486   if (player_action)
11487   {
11488     if (button1)
11489       SnapField(player, dx, dy);
11490     else
11491     {
11492       if (button2)
11493         DropElement(player);
11494
11495       MovePlayer(player, dx, dy);
11496     }
11497
11498     CheckSingleStepMode(player);
11499
11500     SetPlayerWaiting(player, FALSE);
11501
11502     return player_action;
11503   }
11504   else
11505   {
11506     // no actions for this player (no input at player's configured device)
11507
11508     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11509     SnapField(player, 0, 0);
11510     CheckGravityMovementWhenNotMoving(player);
11511
11512     if (player->MovPos == 0)
11513       SetPlayerWaiting(player, TRUE);
11514
11515     if (player->MovPos == 0)    // needed for tape.playing
11516       player->is_moving = FALSE;
11517
11518     player->is_dropping = FALSE;
11519     player->is_dropping_pressed = FALSE;
11520     player->drop_pressed_delay = 0;
11521
11522     CheckSingleStepMode(player);
11523
11524     return 0;
11525   }
11526 }
11527
11528 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11529                                          byte *tape_action)
11530 {
11531   if (!tape.use_mouse_actions)
11532     return;
11533
11534   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11535   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11536   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11537 }
11538
11539 static void SetTapeActionFromMouseAction(byte *tape_action,
11540                                          struct MouseActionInfo *mouse_action)
11541 {
11542   if (!tape.use_mouse_actions)
11543     return;
11544
11545   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11546   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11547   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11548 }
11549
11550 static void CheckLevelSolved(void)
11551 {
11552   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11553   {
11554     if (game_em.level_solved &&
11555         !game_em.game_over)                             // game won
11556     {
11557       LevelSolved();
11558
11559       game_em.game_over = TRUE;
11560
11561       game.all_players_gone = TRUE;
11562     }
11563
11564     if (game_em.game_over)                              // game lost
11565       game.all_players_gone = TRUE;
11566   }
11567   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11568   {
11569     if (game_sp.level_solved &&
11570         !game_sp.game_over)                             // game won
11571     {
11572       LevelSolved();
11573
11574       game_sp.game_over = TRUE;
11575
11576       game.all_players_gone = TRUE;
11577     }
11578
11579     if (game_sp.game_over)                              // game lost
11580       game.all_players_gone = TRUE;
11581   }
11582   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11583   {
11584     if (game_mm.level_solved &&
11585         !game_mm.game_over)                             // game won
11586     {
11587       LevelSolved();
11588
11589       game_mm.game_over = TRUE;
11590
11591       game.all_players_gone = TRUE;
11592     }
11593
11594     if (game_mm.game_over)                              // game lost
11595       game.all_players_gone = TRUE;
11596   }
11597 }
11598
11599 static void CheckLevelTime(void)
11600 {
11601   int i;
11602
11603   if (TimeFrames >= FRAMES_PER_SECOND)
11604   {
11605     TimeFrames = 0;
11606     TapeTime++;
11607
11608     for (i = 0; i < MAX_PLAYERS; i++)
11609     {
11610       struct PlayerInfo *player = &stored_player[i];
11611
11612       if (SHIELD_ON(player))
11613       {
11614         player->shield_normal_time_left--;
11615
11616         if (player->shield_deadly_time_left > 0)
11617           player->shield_deadly_time_left--;
11618       }
11619     }
11620
11621     if (!game.LevelSolved && !level.use_step_counter)
11622     {
11623       TimePlayed++;
11624
11625       if (TimeLeft > 0)
11626       {
11627         TimeLeft--;
11628
11629         if (TimeLeft <= 10 && setup.time_limit)
11630           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11631
11632         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11633            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11634
11635         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11636
11637         if (!TimeLeft && setup.time_limit)
11638         {
11639           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11640             game_em.lev->killed_out_of_time = TRUE;
11641           else
11642             for (i = 0; i < MAX_PLAYERS; i++)
11643               KillPlayer(&stored_player[i]);
11644         }
11645       }
11646       else if (game.no_time_limit && !game.all_players_gone)
11647       {
11648         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11649       }
11650
11651       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11652     }
11653
11654     if (tape.recording || tape.playing)
11655       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11656   }
11657
11658   if (tape.recording || tape.playing)
11659     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11660
11661   UpdateAndDisplayGameControlValues();
11662 }
11663
11664 void AdvanceFrameAndPlayerCounters(int player_nr)
11665 {
11666   int i;
11667
11668   // advance frame counters (global frame counter and time frame counter)
11669   FrameCounter++;
11670   TimeFrames++;
11671
11672   // advance player counters (counters for move delay, move animation etc.)
11673   for (i = 0; i < MAX_PLAYERS; i++)
11674   {
11675     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11676     int move_delay_value = stored_player[i].move_delay_value;
11677     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11678
11679     if (!advance_player_counters)       // not all players may be affected
11680       continue;
11681
11682     if (move_frames == 0)       // less than one move per game frame
11683     {
11684       int stepsize = TILEX / move_delay_value;
11685       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11686       int count = (stored_player[i].is_moving ?
11687                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11688
11689       if (count % delay == 0)
11690         move_frames = 1;
11691     }
11692
11693     stored_player[i].Frame += move_frames;
11694
11695     if (stored_player[i].MovPos != 0)
11696       stored_player[i].StepFrame += move_frames;
11697
11698     if (stored_player[i].move_delay > 0)
11699       stored_player[i].move_delay--;
11700
11701     // due to bugs in previous versions, counter must count up, not down
11702     if (stored_player[i].push_delay != -1)
11703       stored_player[i].push_delay++;
11704
11705     if (stored_player[i].drop_delay > 0)
11706       stored_player[i].drop_delay--;
11707
11708     if (stored_player[i].is_dropping_pressed)
11709       stored_player[i].drop_pressed_delay++;
11710   }
11711 }
11712
11713 void StartGameActions(boolean init_network_game, boolean record_tape,
11714                       int random_seed)
11715 {
11716   unsigned int new_random_seed = InitRND(random_seed);
11717
11718   if (record_tape)
11719     TapeStartRecording(new_random_seed);
11720
11721   if (init_network_game)
11722   {
11723     SendToServer_LevelFile();
11724     SendToServer_StartPlaying();
11725
11726     return;
11727   }
11728
11729   InitGame();
11730 }
11731
11732 static void GameActionsExt(void)
11733 {
11734 #if 0
11735   static unsigned int game_frame_delay = 0;
11736 #endif
11737   unsigned int game_frame_delay_value;
11738   byte *recorded_player_action;
11739   byte summarized_player_action = 0;
11740   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11741   int i;
11742
11743   // detect endless loops, caused by custom element programming
11744   if (recursion_loop_detected && recursion_loop_depth == 0)
11745   {
11746     char *message = getStringCat3("Internal Error! Element ",
11747                                   EL_NAME(recursion_loop_element),
11748                                   " caused endless loop! Quit the game?");
11749
11750     Warn("element '%s' caused endless loop in game engine",
11751          EL_NAME(recursion_loop_element));
11752
11753     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11754
11755     recursion_loop_detected = FALSE;    // if game should be continued
11756
11757     free(message);
11758
11759     return;
11760   }
11761
11762   if (game.restart_level)
11763     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11764
11765   CheckLevelSolved();
11766
11767   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11768     GameWon();
11769
11770   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11771     TapeStop();
11772
11773   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11774     return;
11775
11776   game_frame_delay_value =
11777     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11778
11779   if (tape.playing && tape.warp_forward && !tape.pausing)
11780     game_frame_delay_value = 0;
11781
11782   SetVideoFrameDelay(game_frame_delay_value);
11783
11784   // (de)activate virtual buttons depending on current game status
11785   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11786   {
11787     if (game.all_players_gone)  // if no players there to be controlled anymore
11788       SetOverlayActive(FALSE);
11789     else if (!tape.playing)     // if game continues after tape stopped playing
11790       SetOverlayActive(TRUE);
11791   }
11792
11793 #if 0
11794 #if 0
11795   // ---------- main game synchronization point ----------
11796
11797   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11798
11799   Debug("game:playing:skip", "skip == %d", skip);
11800
11801 #else
11802   // ---------- main game synchronization point ----------
11803
11804   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11805 #endif
11806 #endif
11807
11808   if (network_playing && !network_player_action_received)
11809   {
11810     // try to get network player actions in time
11811
11812     // last chance to get network player actions without main loop delay
11813     HandleNetworking();
11814
11815     // game was quit by network peer
11816     if (game_status != GAME_MODE_PLAYING)
11817       return;
11818
11819     // check if network player actions still missing and game still running
11820     if (!network_player_action_received && !checkGameEnded())
11821       return;           // failed to get network player actions in time
11822
11823     // do not yet reset "network_player_action_received" (for tape.pausing)
11824   }
11825
11826   if (tape.pausing)
11827     return;
11828
11829   // at this point we know that we really continue executing the game
11830
11831   network_player_action_received = FALSE;
11832
11833   // when playing tape, read previously recorded player input from tape data
11834   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11835
11836   local_player->effective_mouse_action = local_player->mouse_action;
11837
11838   if (recorded_player_action != NULL)
11839     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11840                                  recorded_player_action);
11841
11842   // TapePlayAction() may return NULL when toggling to "pause before death"
11843   if (tape.pausing)
11844     return;
11845
11846   if (tape.set_centered_player)
11847   {
11848     game.centered_player_nr_next = tape.centered_player_nr_next;
11849     game.set_centered_player = TRUE;
11850   }
11851
11852   for (i = 0; i < MAX_PLAYERS; i++)
11853   {
11854     summarized_player_action |= stored_player[i].action;
11855
11856     if (!network_playing && (game.team_mode || tape.playing))
11857       stored_player[i].effective_action = stored_player[i].action;
11858   }
11859
11860   if (network_playing && !checkGameEnded())
11861     SendToServer_MovePlayer(summarized_player_action);
11862
11863   // summarize all actions at local players mapped input device position
11864   // (this allows using different input devices in single player mode)
11865   if (!network.enabled && !game.team_mode)
11866     stored_player[map_player_action[local_player->index_nr]].effective_action =
11867       summarized_player_action;
11868
11869   // summarize all actions at centered player in local team mode
11870   if (tape.recording &&
11871       setup.team_mode && !network.enabled &&
11872       setup.input_on_focus &&
11873       game.centered_player_nr != -1)
11874   {
11875     for (i = 0; i < MAX_PLAYERS; i++)
11876       stored_player[map_player_action[i]].effective_action =
11877         (i == game.centered_player_nr ? summarized_player_action : 0);
11878   }
11879
11880   if (recorded_player_action != NULL)
11881     for (i = 0; i < MAX_PLAYERS; i++)
11882       stored_player[i].effective_action = recorded_player_action[i];
11883
11884   for (i = 0; i < MAX_PLAYERS; i++)
11885   {
11886     tape_action[i] = stored_player[i].effective_action;
11887
11888     /* (this may happen in the RND game engine if a player was not present on
11889        the playfield on level start, but appeared later from a custom element */
11890     if (setup.team_mode &&
11891         tape.recording &&
11892         tape_action[i] &&
11893         !tape.player_participates[i])
11894       tape.player_participates[i] = TRUE;
11895   }
11896
11897   SetTapeActionFromMouseAction(tape_action,
11898                                &local_player->effective_mouse_action);
11899
11900   // only record actions from input devices, but not programmed actions
11901   if (tape.recording)
11902     TapeRecordAction(tape_action);
11903
11904   // remember if game was played (especially after tape stopped playing)
11905   if (!tape.playing && summarized_player_action)
11906     game.GamePlayed = TRUE;
11907
11908 #if USE_NEW_PLAYER_ASSIGNMENTS
11909   // !!! also map player actions in single player mode !!!
11910   // if (game.team_mode)
11911   if (1)
11912   {
11913     byte mapped_action[MAX_PLAYERS];
11914
11915 #if DEBUG_PLAYER_ACTIONS
11916     for (i = 0; i < MAX_PLAYERS; i++)
11917       DebugContinued("", "%d, ", stored_player[i].effective_action);
11918 #endif
11919
11920     for (i = 0; i < MAX_PLAYERS; i++)
11921       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11922
11923     for (i = 0; i < MAX_PLAYERS; i++)
11924       stored_player[i].effective_action = mapped_action[i];
11925
11926 #if DEBUG_PLAYER_ACTIONS
11927     DebugContinued("", "=> ");
11928     for (i = 0; i < MAX_PLAYERS; i++)
11929       DebugContinued("", "%d, ", stored_player[i].effective_action);
11930     DebugContinued("game:playing:player", "\n");
11931 #endif
11932   }
11933 #if DEBUG_PLAYER_ACTIONS
11934   else
11935   {
11936     for (i = 0; i < MAX_PLAYERS; i++)
11937       DebugContinued("", "%d, ", stored_player[i].effective_action);
11938     DebugContinued("game:playing:player", "\n");
11939   }
11940 #endif
11941 #endif
11942
11943   for (i = 0; i < MAX_PLAYERS; i++)
11944   {
11945     // allow engine snapshot in case of changed movement attempt
11946     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11947         (stored_player[i].effective_action & KEY_MOTION))
11948       game.snapshot.changed_action = TRUE;
11949
11950     // allow engine snapshot in case of snapping/dropping attempt
11951     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11952         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11953       game.snapshot.changed_action = TRUE;
11954
11955     game.snapshot.last_action[i] = stored_player[i].effective_action;
11956   }
11957
11958   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11959   {
11960     GameActions_EM_Main();
11961   }
11962   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11963   {
11964     GameActions_SP_Main();
11965   }
11966   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11967   {
11968     GameActions_MM_Main();
11969   }
11970   else
11971   {
11972     GameActions_RND_Main();
11973   }
11974
11975   BlitScreenToBitmap(backbuffer);
11976
11977   CheckLevelSolved();
11978   CheckLevelTime();
11979
11980   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11981
11982   if (global.show_frames_per_second)
11983   {
11984     static unsigned int fps_counter = 0;
11985     static int fps_frames = 0;
11986     unsigned int fps_delay_ms = Counter() - fps_counter;
11987
11988     fps_frames++;
11989
11990     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11991     {
11992       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11993
11994       fps_frames = 0;
11995       fps_counter = Counter();
11996
11997       // always draw FPS to screen after FPS value was updated
11998       redraw_mask |= REDRAW_FPS;
11999     }
12000
12001     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12002     if (GetDrawDeactivationMask() == REDRAW_NONE)
12003       redraw_mask |= REDRAW_FPS;
12004   }
12005 }
12006
12007 static void GameActions_CheckSaveEngineSnapshot(void)
12008 {
12009   if (!game.snapshot.save_snapshot)
12010     return;
12011
12012   // clear flag for saving snapshot _before_ saving snapshot
12013   game.snapshot.save_snapshot = FALSE;
12014
12015   SaveEngineSnapshotToList();
12016 }
12017
12018 void GameActions(void)
12019 {
12020   GameActionsExt();
12021
12022   GameActions_CheckSaveEngineSnapshot();
12023 }
12024
12025 void GameActions_EM_Main(void)
12026 {
12027   byte effective_action[MAX_PLAYERS];
12028   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12029   int i;
12030
12031   for (i = 0; i < MAX_PLAYERS; i++)
12032     effective_action[i] = stored_player[i].effective_action;
12033
12034   GameActions_EM(effective_action, warp_mode);
12035 }
12036
12037 void GameActions_SP_Main(void)
12038 {
12039   byte effective_action[MAX_PLAYERS];
12040   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12041   int i;
12042
12043   for (i = 0; i < MAX_PLAYERS; i++)
12044     effective_action[i] = stored_player[i].effective_action;
12045
12046   GameActions_SP(effective_action, warp_mode);
12047
12048   for (i = 0; i < MAX_PLAYERS; i++)
12049   {
12050     if (stored_player[i].force_dropping)
12051       stored_player[i].action |= KEY_BUTTON_DROP;
12052
12053     stored_player[i].force_dropping = FALSE;
12054   }
12055 }
12056
12057 void GameActions_MM_Main(void)
12058 {
12059   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12060
12061   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12062 }
12063
12064 void GameActions_RND_Main(void)
12065 {
12066   GameActions_RND();
12067 }
12068
12069 void GameActions_RND(void)
12070 {
12071   static struct MouseActionInfo mouse_action_last = { 0 };
12072   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12073   int magic_wall_x = 0, magic_wall_y = 0;
12074   int i, x, y, element, graphic, last_gfx_frame;
12075
12076   InitPlayfieldScanModeVars();
12077
12078   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12079   {
12080     SCAN_PLAYFIELD(x, y)
12081     {
12082       ChangeCount[x][y] = 0;
12083       ChangeEvent[x][y] = -1;
12084     }
12085   }
12086
12087   if (game.set_centered_player)
12088   {
12089     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12090
12091     // switching to "all players" only possible if all players fit to screen
12092     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12093     {
12094       game.centered_player_nr_next = game.centered_player_nr;
12095       game.set_centered_player = FALSE;
12096     }
12097
12098     // do not switch focus to non-existing (or non-active) player
12099     if (game.centered_player_nr_next >= 0 &&
12100         !stored_player[game.centered_player_nr_next].active)
12101     {
12102       game.centered_player_nr_next = game.centered_player_nr;
12103       game.set_centered_player = FALSE;
12104     }
12105   }
12106
12107   if (game.set_centered_player &&
12108       ScreenMovPos == 0)        // screen currently aligned at tile position
12109   {
12110     int sx, sy;
12111
12112     if (game.centered_player_nr_next == -1)
12113     {
12114       setScreenCenteredToAllPlayers(&sx, &sy);
12115     }
12116     else
12117     {
12118       sx = stored_player[game.centered_player_nr_next].jx;
12119       sy = stored_player[game.centered_player_nr_next].jy;
12120     }
12121
12122     game.centered_player_nr = game.centered_player_nr_next;
12123     game.set_centered_player = FALSE;
12124
12125     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12126     DrawGameDoorValues();
12127   }
12128
12129   // check single step mode (set flag and clear again if any player is active)
12130   game.enter_single_step_mode =
12131     (tape.single_step && tape.recording && !tape.pausing);
12132
12133   for (i = 0; i < MAX_PLAYERS; i++)
12134   {
12135     int actual_player_action = stored_player[i].effective_action;
12136
12137 #if 1
12138     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12139        - rnd_equinox_tetrachloride 048
12140        - rnd_equinox_tetrachloride_ii 096
12141        - rnd_emanuel_schmieg 002
12142        - doctor_sloan_ww 001, 020
12143     */
12144     if (stored_player[i].MovPos == 0)
12145       CheckGravityMovement(&stored_player[i]);
12146 #endif
12147
12148     // overwrite programmed action with tape action
12149     if (stored_player[i].programmed_action)
12150       actual_player_action = stored_player[i].programmed_action;
12151
12152     PlayerActions(&stored_player[i], actual_player_action);
12153
12154     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12155   }
12156
12157   // single step pause mode may already have been toggled by "ScrollPlayer()"
12158   if (game.enter_single_step_mode && !tape.pausing)
12159     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12160
12161   ScrollScreen(NULL, SCROLL_GO_ON);
12162
12163   /* for backwards compatibility, the following code emulates a fixed bug that
12164      occured when pushing elements (causing elements that just made their last
12165      pushing step to already (if possible) make their first falling step in the
12166      same game frame, which is bad); this code is also needed to use the famous
12167      "spring push bug" which is used in older levels and might be wanted to be
12168      used also in newer levels, but in this case the buggy pushing code is only
12169      affecting the "spring" element and no other elements */
12170
12171   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12172   {
12173     for (i = 0; i < MAX_PLAYERS; i++)
12174     {
12175       struct PlayerInfo *player = &stored_player[i];
12176       int x = player->jx;
12177       int y = player->jy;
12178
12179       if (player->active && player->is_pushing && player->is_moving &&
12180           IS_MOVING(x, y) &&
12181           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12182            Tile[x][y] == EL_SPRING))
12183       {
12184         ContinueMoving(x, y);
12185
12186         // continue moving after pushing (this is actually a bug)
12187         if (!IS_MOVING(x, y))
12188           Stop[x][y] = FALSE;
12189       }
12190     }
12191   }
12192
12193   SCAN_PLAYFIELD(x, y)
12194   {
12195     Last[x][y] = Tile[x][y];
12196
12197     ChangeCount[x][y] = 0;
12198     ChangeEvent[x][y] = -1;
12199
12200     // this must be handled before main playfield loop
12201     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12202     {
12203       MovDelay[x][y]--;
12204       if (MovDelay[x][y] <= 0)
12205         RemoveField(x, y);
12206     }
12207
12208     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12209     {
12210       MovDelay[x][y]--;
12211       if (MovDelay[x][y] <= 0)
12212       {
12213         int element = Store[x][y];
12214         int move_direction = MovDir[x][y];
12215         int player_index_bit = Store2[x][y];
12216
12217         Store[x][y] = 0;
12218         Store2[x][y] = 0;
12219
12220         RemoveField(x, y);
12221         TEST_DrawLevelField(x, y);
12222
12223         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12224
12225         if (IS_ENVELOPE(element))
12226           local_player->show_envelope = element;
12227       }
12228     }
12229
12230 #if DEBUG
12231     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12232     {
12233       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12234             x, y);
12235       Debug("game:playing:GameActions_RND", "This should never happen!");
12236
12237       ChangePage[x][y] = -1;
12238     }
12239 #endif
12240
12241     Stop[x][y] = FALSE;
12242     if (WasJustMoving[x][y] > 0)
12243       WasJustMoving[x][y]--;
12244     if (WasJustFalling[x][y] > 0)
12245       WasJustFalling[x][y]--;
12246     if (CheckCollision[x][y] > 0)
12247       CheckCollision[x][y]--;
12248     if (CheckImpact[x][y] > 0)
12249       CheckImpact[x][y]--;
12250
12251     GfxFrame[x][y]++;
12252
12253     /* reset finished pushing action (not done in ContinueMoving() to allow
12254        continuous pushing animation for elements with zero push delay) */
12255     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12256     {
12257       ResetGfxAnimation(x, y);
12258       TEST_DrawLevelField(x, y);
12259     }
12260
12261 #if DEBUG
12262     if (IS_BLOCKED(x, y))
12263     {
12264       int oldx, oldy;
12265
12266       Blocked2Moving(x, y, &oldx, &oldy);
12267       if (!IS_MOVING(oldx, oldy))
12268       {
12269         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12270         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12271         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12272         Debug("game:playing:GameActions_RND", "This should never happen!");
12273       }
12274     }
12275 #endif
12276   }
12277
12278   if (mouse_action.button)
12279   {
12280     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12281     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12282
12283     x = mouse_action.lx;
12284     y = mouse_action.ly;
12285     element = Tile[x][y];
12286
12287     if (new_button)
12288     {
12289       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12290       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12291                                          ch_button);
12292     }
12293
12294     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12295     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12296                                        ch_button);
12297   }
12298
12299   SCAN_PLAYFIELD(x, y)
12300   {
12301     element = Tile[x][y];
12302     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12303     last_gfx_frame = GfxFrame[x][y];
12304
12305     if (element == EL_EMPTY)
12306       graphic = el2img(GfxElementEmpty[x][y]);
12307
12308     ResetGfxFrame(x, y);
12309
12310     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12311       DrawLevelGraphicAnimation(x, y, graphic);
12312
12313     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12314         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12315       ResetRandomAnimationValue(x, y);
12316
12317     SetRandomAnimationValue(x, y);
12318
12319     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12320
12321     if (IS_INACTIVE(element))
12322     {
12323       if (IS_ANIMATED(graphic))
12324         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12325
12326       continue;
12327     }
12328
12329     // this may take place after moving, so 'element' may have changed
12330     if (IS_CHANGING(x, y) &&
12331         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12332     {
12333       int page = element_info[element].event_page_nr[CE_DELAY];
12334
12335       HandleElementChange(x, y, page);
12336
12337       element = Tile[x][y];
12338       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12339     }
12340
12341     CheckNextToConditions(x, y);
12342
12343     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12344     {
12345       StartMoving(x, y);
12346
12347       element = Tile[x][y];
12348       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12349
12350       if (IS_ANIMATED(graphic) &&
12351           !IS_MOVING(x, y) &&
12352           !Stop[x][y])
12353         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12354
12355       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12356         TEST_DrawTwinkleOnField(x, y);
12357     }
12358     else if (element == EL_ACID)
12359     {
12360       if (!Stop[x][y])
12361         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12362     }
12363     else if ((element == EL_EXIT_OPEN ||
12364               element == EL_EM_EXIT_OPEN ||
12365               element == EL_SP_EXIT_OPEN ||
12366               element == EL_STEEL_EXIT_OPEN ||
12367               element == EL_EM_STEEL_EXIT_OPEN ||
12368               element == EL_SP_TERMINAL ||
12369               element == EL_SP_TERMINAL_ACTIVE ||
12370               element == EL_EXTRA_TIME ||
12371               element == EL_SHIELD_NORMAL ||
12372               element == EL_SHIELD_DEADLY) &&
12373              IS_ANIMATED(graphic))
12374       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12375     else if (IS_MOVING(x, y))
12376       ContinueMoving(x, y);
12377     else if (IS_ACTIVE_BOMB(element))
12378       CheckDynamite(x, y);
12379     else if (element == EL_AMOEBA_GROWING)
12380       AmoebaGrowing(x, y);
12381     else if (element == EL_AMOEBA_SHRINKING)
12382       AmoebaShrinking(x, y);
12383
12384 #if !USE_NEW_AMOEBA_CODE
12385     else if (IS_AMOEBALIVE(element))
12386       AmoebaReproduce(x, y);
12387 #endif
12388
12389     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12390       Life(x, y);
12391     else if (element == EL_EXIT_CLOSED)
12392       CheckExit(x, y);
12393     else if (element == EL_EM_EXIT_CLOSED)
12394       CheckExitEM(x, y);
12395     else if (element == EL_STEEL_EXIT_CLOSED)
12396       CheckExitSteel(x, y);
12397     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12398       CheckExitSteelEM(x, y);
12399     else if (element == EL_SP_EXIT_CLOSED)
12400       CheckExitSP(x, y);
12401     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12402              element == EL_EXPANDABLE_STEELWALL_GROWING)
12403       MauerWaechst(x, y);
12404     else if (element == EL_EXPANDABLE_WALL ||
12405              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12406              element == EL_EXPANDABLE_WALL_VERTICAL ||
12407              element == EL_EXPANDABLE_WALL_ANY ||
12408              element == EL_BD_EXPANDABLE_WALL)
12409       MauerAbleger(x, y);
12410     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12411              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12412              element == EL_EXPANDABLE_STEELWALL_ANY)
12413       MauerAblegerStahl(x, y);
12414     else if (element == EL_FLAMES)
12415       CheckForDragon(x, y);
12416     else if (element == EL_EXPLOSION)
12417       ; // drawing of correct explosion animation is handled separately
12418     else if (element == EL_ELEMENT_SNAPPING ||
12419              element == EL_DIAGONAL_SHRINKING ||
12420              element == EL_DIAGONAL_GROWING)
12421     {
12422       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12423
12424       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12425     }
12426     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12427       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12428
12429     if (IS_BELT_ACTIVE(element))
12430       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12431
12432     if (game.magic_wall_active)
12433     {
12434       int jx = local_player->jx, jy = local_player->jy;
12435
12436       // play the element sound at the position nearest to the player
12437       if ((element == EL_MAGIC_WALL_FULL ||
12438            element == EL_MAGIC_WALL_ACTIVE ||
12439            element == EL_MAGIC_WALL_EMPTYING ||
12440            element == EL_BD_MAGIC_WALL_FULL ||
12441            element == EL_BD_MAGIC_WALL_ACTIVE ||
12442            element == EL_BD_MAGIC_WALL_EMPTYING ||
12443            element == EL_DC_MAGIC_WALL_FULL ||
12444            element == EL_DC_MAGIC_WALL_ACTIVE ||
12445            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12446           ABS(x - jx) + ABS(y - jy) <
12447           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12448       {
12449         magic_wall_x = x;
12450         magic_wall_y = y;
12451       }
12452     }
12453   }
12454
12455 #if USE_NEW_AMOEBA_CODE
12456   // new experimental amoeba growth stuff
12457   if (!(FrameCounter % 8))
12458   {
12459     static unsigned int random = 1684108901;
12460
12461     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12462     {
12463       x = RND(lev_fieldx);
12464       y = RND(lev_fieldy);
12465       element = Tile[x][y];
12466
12467       if (!IS_PLAYER(x,y) &&
12468           (element == EL_EMPTY ||
12469            CAN_GROW_INTO(element) ||
12470            element == EL_QUICKSAND_EMPTY ||
12471            element == EL_QUICKSAND_FAST_EMPTY ||
12472            element == EL_ACID_SPLASH_LEFT ||
12473            element == EL_ACID_SPLASH_RIGHT))
12474       {
12475         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12476             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12477             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12478             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12479           Tile[x][y] = EL_AMOEBA_DROP;
12480       }
12481
12482       random = random * 129 + 1;
12483     }
12484   }
12485 #endif
12486
12487   game.explosions_delayed = FALSE;
12488
12489   SCAN_PLAYFIELD(x, y)
12490   {
12491     element = Tile[x][y];
12492
12493     if (ExplodeField[x][y])
12494       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12495     else if (element == EL_EXPLOSION)
12496       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12497
12498     ExplodeField[x][y] = EX_TYPE_NONE;
12499   }
12500
12501   game.explosions_delayed = TRUE;
12502
12503   if (game.magic_wall_active)
12504   {
12505     if (!(game.magic_wall_time_left % 4))
12506     {
12507       int element = Tile[magic_wall_x][magic_wall_y];
12508
12509       if (element == EL_BD_MAGIC_WALL_FULL ||
12510           element == EL_BD_MAGIC_WALL_ACTIVE ||
12511           element == EL_BD_MAGIC_WALL_EMPTYING)
12512         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12513       else if (element == EL_DC_MAGIC_WALL_FULL ||
12514                element == EL_DC_MAGIC_WALL_ACTIVE ||
12515                element == EL_DC_MAGIC_WALL_EMPTYING)
12516         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12517       else
12518         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12519     }
12520
12521     if (game.magic_wall_time_left > 0)
12522     {
12523       game.magic_wall_time_left--;
12524
12525       if (!game.magic_wall_time_left)
12526       {
12527         SCAN_PLAYFIELD(x, y)
12528         {
12529           element = Tile[x][y];
12530
12531           if (element == EL_MAGIC_WALL_ACTIVE ||
12532               element == EL_MAGIC_WALL_FULL)
12533           {
12534             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12535             TEST_DrawLevelField(x, y);
12536           }
12537           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12538                    element == EL_BD_MAGIC_WALL_FULL)
12539           {
12540             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12541             TEST_DrawLevelField(x, y);
12542           }
12543           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12544                    element == EL_DC_MAGIC_WALL_FULL)
12545           {
12546             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12547             TEST_DrawLevelField(x, y);
12548           }
12549         }
12550
12551         game.magic_wall_active = FALSE;
12552       }
12553     }
12554   }
12555
12556   if (game.light_time_left > 0)
12557   {
12558     game.light_time_left--;
12559
12560     if (game.light_time_left == 0)
12561       RedrawAllLightSwitchesAndInvisibleElements();
12562   }
12563
12564   if (game.timegate_time_left > 0)
12565   {
12566     game.timegate_time_left--;
12567
12568     if (game.timegate_time_left == 0)
12569       CloseAllOpenTimegates();
12570   }
12571
12572   if (game.lenses_time_left > 0)
12573   {
12574     game.lenses_time_left--;
12575
12576     if (game.lenses_time_left == 0)
12577       RedrawAllInvisibleElementsForLenses();
12578   }
12579
12580   if (game.magnify_time_left > 0)
12581   {
12582     game.magnify_time_left--;
12583
12584     if (game.magnify_time_left == 0)
12585       RedrawAllInvisibleElementsForMagnifier();
12586   }
12587
12588   for (i = 0; i < MAX_PLAYERS; i++)
12589   {
12590     struct PlayerInfo *player = &stored_player[i];
12591
12592     if (SHIELD_ON(player))
12593     {
12594       if (player->shield_deadly_time_left)
12595         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12596       else if (player->shield_normal_time_left)
12597         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12598     }
12599   }
12600
12601 #if USE_DELAYED_GFX_REDRAW
12602   SCAN_PLAYFIELD(x, y)
12603   {
12604     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12605     {
12606       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12607          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12608
12609       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12610         DrawLevelField(x, y);
12611
12612       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12613         DrawLevelFieldCrumbled(x, y);
12614
12615       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12616         DrawLevelFieldCrumbledNeighbours(x, y);
12617
12618       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12619         DrawTwinkleOnField(x, y);
12620     }
12621
12622     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12623   }
12624 #endif
12625
12626   DrawAllPlayers();
12627   PlayAllPlayersSound();
12628
12629   for (i = 0; i < MAX_PLAYERS; i++)
12630   {
12631     struct PlayerInfo *player = &stored_player[i];
12632
12633     if (player->show_envelope != 0 && (!player->active ||
12634                                        player->MovPos == 0))
12635     {
12636       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12637
12638       player->show_envelope = 0;
12639     }
12640   }
12641
12642   // use random number generator in every frame to make it less predictable
12643   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12644     RND(1);
12645
12646   mouse_action_last = mouse_action;
12647 }
12648
12649 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12650 {
12651   int min_x = x, min_y = y, max_x = x, max_y = y;
12652   int scr_fieldx = getScreenFieldSizeX();
12653   int scr_fieldy = getScreenFieldSizeY();
12654   int i;
12655
12656   for (i = 0; i < MAX_PLAYERS; i++)
12657   {
12658     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12659
12660     if (!stored_player[i].active || &stored_player[i] == player)
12661       continue;
12662
12663     min_x = MIN(min_x, jx);
12664     min_y = MIN(min_y, jy);
12665     max_x = MAX(max_x, jx);
12666     max_y = MAX(max_y, jy);
12667   }
12668
12669   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12670 }
12671
12672 static boolean AllPlayersInVisibleScreen(void)
12673 {
12674   int i;
12675
12676   for (i = 0; i < MAX_PLAYERS; i++)
12677   {
12678     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12679
12680     if (!stored_player[i].active)
12681       continue;
12682
12683     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12684       return FALSE;
12685   }
12686
12687   return TRUE;
12688 }
12689
12690 void ScrollLevel(int dx, int dy)
12691 {
12692   int scroll_offset = 2 * TILEX_VAR;
12693   int x, y;
12694
12695   BlitBitmap(drawto_field, drawto_field,
12696              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12697              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12698              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12699              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12700              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12701              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12702
12703   if (dx != 0)
12704   {
12705     x = (dx == 1 ? BX1 : BX2);
12706     for (y = BY1; y <= BY2; y++)
12707       DrawScreenField(x, y);
12708   }
12709
12710   if (dy != 0)
12711   {
12712     y = (dy == 1 ? BY1 : BY2);
12713     for (x = BX1; x <= BX2; x++)
12714       DrawScreenField(x, y);
12715   }
12716
12717   redraw_mask |= REDRAW_FIELD;
12718 }
12719
12720 static boolean canFallDown(struct PlayerInfo *player)
12721 {
12722   int jx = player->jx, jy = player->jy;
12723
12724   return (IN_LEV_FIELD(jx, jy + 1) &&
12725           (IS_FREE(jx, jy + 1) ||
12726            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12727           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12728           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12729 }
12730
12731 static boolean canPassField(int x, int y, int move_dir)
12732 {
12733   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12734   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12735   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12736   int nextx = x + dx;
12737   int nexty = y + dy;
12738   int element = Tile[x][y];
12739
12740   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12741           !CAN_MOVE(element) &&
12742           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12743           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12744           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12745 }
12746
12747 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12748 {
12749   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12750   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12751   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12752   int newx = x + dx;
12753   int newy = y + dy;
12754
12755   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12756           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12757           (IS_DIGGABLE(Tile[newx][newy]) ||
12758            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12759            canPassField(newx, newy, move_dir)));
12760 }
12761
12762 static void CheckGravityMovement(struct PlayerInfo *player)
12763 {
12764   if (player->gravity && !player->programmed_action)
12765   {
12766     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12767     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12768     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12769     int jx = player->jx, jy = player->jy;
12770     boolean player_is_moving_to_valid_field =
12771       (!player_is_snapping &&
12772        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12773         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12774     boolean player_can_fall_down = canFallDown(player);
12775
12776     if (player_can_fall_down &&
12777         !player_is_moving_to_valid_field)
12778       player->programmed_action = MV_DOWN;
12779   }
12780 }
12781
12782 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12783 {
12784   return CheckGravityMovement(player);
12785
12786   if (player->gravity && !player->programmed_action)
12787   {
12788     int jx = player->jx, jy = player->jy;
12789     boolean field_under_player_is_free =
12790       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12791     boolean player_is_standing_on_valid_field =
12792       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12793        (IS_WALKABLE(Tile[jx][jy]) &&
12794         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12795
12796     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12797       player->programmed_action = MV_DOWN;
12798   }
12799 }
12800
12801 /*
12802   MovePlayerOneStep()
12803   -----------------------------------------------------------------------------
12804   dx, dy:               direction (non-diagonal) to try to move the player to
12805   real_dx, real_dy:     direction as read from input device (can be diagonal)
12806 */
12807
12808 boolean MovePlayerOneStep(struct PlayerInfo *player,
12809                           int dx, int dy, int real_dx, int real_dy)
12810 {
12811   int jx = player->jx, jy = player->jy;
12812   int new_jx = jx + dx, new_jy = jy + dy;
12813   int can_move;
12814   boolean player_can_move = !player->cannot_move;
12815
12816   if (!player->active || (!dx && !dy))
12817     return MP_NO_ACTION;
12818
12819   player->MovDir = (dx < 0 ? MV_LEFT :
12820                     dx > 0 ? MV_RIGHT :
12821                     dy < 0 ? MV_UP :
12822                     dy > 0 ? MV_DOWN :  MV_NONE);
12823
12824   if (!IN_LEV_FIELD(new_jx, new_jy))
12825     return MP_NO_ACTION;
12826
12827   if (!player_can_move)
12828   {
12829     if (player->MovPos == 0)
12830     {
12831       player->is_moving = FALSE;
12832       player->is_digging = FALSE;
12833       player->is_collecting = FALSE;
12834       player->is_snapping = FALSE;
12835       player->is_pushing = FALSE;
12836     }
12837   }
12838
12839   if (!network.enabled && game.centered_player_nr == -1 &&
12840       !AllPlayersInSight(player, new_jx, new_jy))
12841     return MP_NO_ACTION;
12842
12843   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12844   if (can_move != MP_MOVING)
12845     return can_move;
12846
12847   // check if DigField() has caused relocation of the player
12848   if (player->jx != jx || player->jy != jy)
12849     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12850
12851   StorePlayer[jx][jy] = 0;
12852   player->last_jx = jx;
12853   player->last_jy = jy;
12854   player->jx = new_jx;
12855   player->jy = new_jy;
12856   StorePlayer[new_jx][new_jy] = player->element_nr;
12857
12858   if (player->move_delay_value_next != -1)
12859   {
12860     player->move_delay_value = player->move_delay_value_next;
12861     player->move_delay_value_next = -1;
12862   }
12863
12864   player->MovPos =
12865     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12866
12867   player->step_counter++;
12868
12869   PlayerVisit[jx][jy] = FrameCounter;
12870
12871   player->is_moving = TRUE;
12872
12873 #if 1
12874   // should better be called in MovePlayer(), but this breaks some tapes
12875   ScrollPlayer(player, SCROLL_INIT);
12876 #endif
12877
12878   return MP_MOVING;
12879 }
12880
12881 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12882 {
12883   int jx = player->jx, jy = player->jy;
12884   int old_jx = jx, old_jy = jy;
12885   int moved = MP_NO_ACTION;
12886
12887   if (!player->active)
12888     return FALSE;
12889
12890   if (!dx && !dy)
12891   {
12892     if (player->MovPos == 0)
12893     {
12894       player->is_moving = FALSE;
12895       player->is_digging = FALSE;
12896       player->is_collecting = FALSE;
12897       player->is_snapping = FALSE;
12898       player->is_pushing = FALSE;
12899     }
12900
12901     return FALSE;
12902   }
12903
12904   if (player->move_delay > 0)
12905     return FALSE;
12906
12907   player->move_delay = -1;              // set to "uninitialized" value
12908
12909   // store if player is automatically moved to next field
12910   player->is_auto_moving = (player->programmed_action != MV_NONE);
12911
12912   // remove the last programmed player action
12913   player->programmed_action = 0;
12914
12915   if (player->MovPos)
12916   {
12917     // should only happen if pre-1.2 tape recordings are played
12918     // this is only for backward compatibility
12919
12920     int original_move_delay_value = player->move_delay_value;
12921
12922 #if DEBUG
12923     Debug("game:playing:MovePlayer",
12924           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12925           tape.counter);
12926 #endif
12927
12928     // scroll remaining steps with finest movement resolution
12929     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12930
12931     while (player->MovPos)
12932     {
12933       ScrollPlayer(player, SCROLL_GO_ON);
12934       ScrollScreen(NULL, SCROLL_GO_ON);
12935
12936       AdvanceFrameAndPlayerCounters(player->index_nr);
12937
12938       DrawAllPlayers();
12939       BackToFront_WithFrameDelay(0);
12940     }
12941
12942     player->move_delay_value = original_move_delay_value;
12943   }
12944
12945   player->is_active = FALSE;
12946
12947   if (player->last_move_dir & MV_HORIZONTAL)
12948   {
12949     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12950       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12951   }
12952   else
12953   {
12954     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12955       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12956   }
12957
12958   if (!moved && !player->is_active)
12959   {
12960     player->is_moving = FALSE;
12961     player->is_digging = FALSE;
12962     player->is_collecting = FALSE;
12963     player->is_snapping = FALSE;
12964     player->is_pushing = FALSE;
12965   }
12966
12967   jx = player->jx;
12968   jy = player->jy;
12969
12970   if (moved & MP_MOVING && !ScreenMovPos &&
12971       (player->index_nr == game.centered_player_nr ||
12972        game.centered_player_nr == -1))
12973   {
12974     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12975
12976     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12977     {
12978       // actual player has left the screen -- scroll in that direction
12979       if (jx != old_jx)         // player has moved horizontally
12980         scroll_x += (jx - old_jx);
12981       else                      // player has moved vertically
12982         scroll_y += (jy - old_jy);
12983     }
12984     else
12985     {
12986       int offset_raw = game.scroll_delay_value;
12987
12988       if (jx != old_jx)         // player has moved horizontally
12989       {
12990         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12991         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12992         int new_scroll_x = jx - MIDPOSX + offset_x;
12993
12994         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12995             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12996           scroll_x = new_scroll_x;
12997
12998         // don't scroll over playfield boundaries
12999         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13000
13001         // don't scroll more than one field at a time
13002         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13003
13004         // don't scroll against the player's moving direction
13005         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13006             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13007           scroll_x = old_scroll_x;
13008       }
13009       else                      // player has moved vertically
13010       {
13011         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13012         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13013         int new_scroll_y = jy - MIDPOSY + offset_y;
13014
13015         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13016             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13017           scroll_y = new_scroll_y;
13018
13019         // don't scroll over playfield boundaries
13020         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13021
13022         // don't scroll more than one field at a time
13023         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13024
13025         // don't scroll against the player's moving direction
13026         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13027             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13028           scroll_y = old_scroll_y;
13029       }
13030     }
13031
13032     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13033     {
13034       if (!network.enabled && game.centered_player_nr == -1 &&
13035           !AllPlayersInVisibleScreen())
13036       {
13037         scroll_x = old_scroll_x;
13038         scroll_y = old_scroll_y;
13039       }
13040       else
13041       {
13042         ScrollScreen(player, SCROLL_INIT);
13043         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13044       }
13045     }
13046   }
13047
13048   player->StepFrame = 0;
13049
13050   if (moved & MP_MOVING)
13051   {
13052     if (old_jx != jx && old_jy == jy)
13053       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13054     else if (old_jx == jx && old_jy != jy)
13055       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13056
13057     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13058
13059     player->last_move_dir = player->MovDir;
13060     player->is_moving = TRUE;
13061     player->is_snapping = FALSE;
13062     player->is_switching = FALSE;
13063     player->is_dropping = FALSE;
13064     player->is_dropping_pressed = FALSE;
13065     player->drop_pressed_delay = 0;
13066
13067 #if 0
13068     // should better be called here than above, but this breaks some tapes
13069     ScrollPlayer(player, SCROLL_INIT);
13070 #endif
13071   }
13072   else
13073   {
13074     CheckGravityMovementWhenNotMoving(player);
13075
13076     player->is_moving = FALSE;
13077
13078     /* at this point, the player is allowed to move, but cannot move right now
13079        (e.g. because of something blocking the way) -- ensure that the player
13080        is also allowed to move in the next frame (in old versions before 3.1.1,
13081        the player was forced to wait again for eight frames before next try) */
13082
13083     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13084       player->move_delay = 0;   // allow direct movement in the next frame
13085   }
13086
13087   if (player->move_delay == -1)         // not yet initialized by DigField()
13088     player->move_delay = player->move_delay_value;
13089
13090   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13091   {
13092     TestIfPlayerTouchesBadThing(jx, jy);
13093     TestIfPlayerTouchesCustomElement(jx, jy);
13094   }
13095
13096   if (!player->active)
13097     RemovePlayer(player);
13098
13099   return moved;
13100 }
13101
13102 void ScrollPlayer(struct PlayerInfo *player, int mode)
13103 {
13104   int jx = player->jx, jy = player->jy;
13105   int last_jx = player->last_jx, last_jy = player->last_jy;
13106   int move_stepsize = TILEX / player->move_delay_value;
13107
13108   if (!player->active)
13109     return;
13110
13111   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13112     return;
13113
13114   if (mode == SCROLL_INIT)
13115   {
13116     player->actual_frame_counter = FrameCounter;
13117     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13118
13119     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13120         Tile[last_jx][last_jy] == EL_EMPTY)
13121     {
13122       int last_field_block_delay = 0;   // start with no blocking at all
13123       int block_delay_adjustment = player->block_delay_adjustment;
13124
13125       // if player blocks last field, add delay for exactly one move
13126       if (player->block_last_field)
13127       {
13128         last_field_block_delay += player->move_delay_value;
13129
13130         // when blocking enabled, prevent moving up despite gravity
13131         if (player->gravity && player->MovDir == MV_UP)
13132           block_delay_adjustment = -1;
13133       }
13134
13135       // add block delay adjustment (also possible when not blocking)
13136       last_field_block_delay += block_delay_adjustment;
13137
13138       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13139       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13140     }
13141
13142     if (player->MovPos != 0)    // player has not yet reached destination
13143       return;
13144   }
13145   else if (!FrameReached(&player->actual_frame_counter, 1))
13146     return;
13147
13148   if (player->MovPos != 0)
13149   {
13150     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13151     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13152
13153     // before DrawPlayer() to draw correct player graphic for this case
13154     if (player->MovPos == 0)
13155       CheckGravityMovement(player);
13156   }
13157
13158   if (player->MovPos == 0)      // player reached destination field
13159   {
13160     if (player->move_delay_reset_counter > 0)
13161     {
13162       player->move_delay_reset_counter--;
13163
13164       if (player->move_delay_reset_counter == 0)
13165       {
13166         // continue with normal speed after quickly moving through gate
13167         HALVE_PLAYER_SPEED(player);
13168
13169         // be able to make the next move without delay
13170         player->move_delay = 0;
13171       }
13172     }
13173
13174     player->last_jx = jx;
13175     player->last_jy = jy;
13176
13177     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13178         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13179         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13180         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13181         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13182         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13183         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13184         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13185     {
13186       ExitPlayer(player);
13187
13188       if (game.players_still_needed == 0 &&
13189           (game.friends_still_needed == 0 ||
13190            IS_SP_ELEMENT(Tile[jx][jy])))
13191         LevelSolved();
13192     }
13193
13194     // this breaks one level: "machine", level 000
13195     {
13196       int move_direction = player->MovDir;
13197       int enter_side = MV_DIR_OPPOSITE(move_direction);
13198       int leave_side = move_direction;
13199       int old_jx = last_jx;
13200       int old_jy = last_jy;
13201       int old_element = Tile[old_jx][old_jy];
13202       int new_element = Tile[jx][jy];
13203
13204       if (IS_CUSTOM_ELEMENT(old_element))
13205         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13206                                    CE_LEFT_BY_PLAYER,
13207                                    player->index_bit, leave_side);
13208
13209       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13210                                           CE_PLAYER_LEAVES_X,
13211                                           player->index_bit, leave_side);
13212
13213       if (IS_CUSTOM_ELEMENT(new_element))
13214         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13215                                    player->index_bit, enter_side);
13216
13217       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13218                                           CE_PLAYER_ENTERS_X,
13219                                           player->index_bit, enter_side);
13220
13221       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13222                                         CE_MOVE_OF_X, move_direction);
13223     }
13224
13225     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13226     {
13227       TestIfPlayerTouchesBadThing(jx, jy);
13228       TestIfPlayerTouchesCustomElement(jx, jy);
13229
13230       /* needed because pushed element has not yet reached its destination,
13231          so it would trigger a change event at its previous field location */
13232       if (!player->is_pushing)
13233         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13234
13235       if (level.finish_dig_collect &&
13236           (player->is_digging || player->is_collecting))
13237       {
13238         int last_element = player->last_removed_element;
13239         int move_direction = player->MovDir;
13240         int enter_side = MV_DIR_OPPOSITE(move_direction);
13241         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13242                             CE_PLAYER_COLLECTS_X);
13243
13244         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13245                                             player->index_bit, enter_side);
13246
13247         player->last_removed_element = EL_UNDEFINED;
13248       }
13249
13250       if (!player->active)
13251         RemovePlayer(player);
13252     }
13253
13254     if (level.use_step_counter)
13255     {
13256       int i;
13257
13258       TimePlayed++;
13259
13260       if (TimeLeft > 0)
13261       {
13262         TimeLeft--;
13263
13264         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13265           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13266
13267         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13268
13269         DisplayGameControlValues();
13270
13271         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13272           for (i = 0; i < MAX_PLAYERS; i++)
13273             KillPlayer(&stored_player[i]);
13274       }
13275       else if (game.no_time_limit && !game.all_players_gone)
13276       {
13277         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13278
13279         DisplayGameControlValues();
13280       }
13281     }
13282
13283     if (tape.single_step && tape.recording && !tape.pausing &&
13284         !player->programmed_action)
13285       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13286
13287     if (!player->programmed_action)
13288       CheckSaveEngineSnapshot(player);
13289   }
13290 }
13291
13292 void ScrollScreen(struct PlayerInfo *player, int mode)
13293 {
13294   static unsigned int screen_frame_counter = 0;
13295
13296   if (mode == SCROLL_INIT)
13297   {
13298     // set scrolling step size according to actual player's moving speed
13299     ScrollStepSize = TILEX / player->move_delay_value;
13300
13301     screen_frame_counter = FrameCounter;
13302     ScreenMovDir = player->MovDir;
13303     ScreenMovPos = player->MovPos;
13304     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13305     return;
13306   }
13307   else if (!FrameReached(&screen_frame_counter, 1))
13308     return;
13309
13310   if (ScreenMovPos)
13311   {
13312     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13313     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13314     redraw_mask |= REDRAW_FIELD;
13315   }
13316   else
13317     ScreenMovDir = MV_NONE;
13318 }
13319
13320 void CheckNextToConditions(int x, int y)
13321 {
13322   int element = Tile[x][y];
13323
13324   if (IS_PLAYER(x, y))
13325     TestIfPlayerNextToCustomElement(x, y);
13326
13327   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13328       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13329     TestIfElementNextToCustomElement(x, y);
13330 }
13331
13332 void TestIfPlayerNextToCustomElement(int x, int y)
13333 {
13334   static int xy[4][2] =
13335   {
13336     { 0, -1 },
13337     { -1, 0 },
13338     { +1, 0 },
13339     { 0, +1 }
13340   };
13341   static int trigger_sides[4][2] =
13342   {
13343     // center side       border side
13344     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13345     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13346     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13347     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13348   };
13349   int i;
13350
13351   if (!IS_PLAYER(x, y))
13352     return;
13353
13354   struct PlayerInfo *player = PLAYERINFO(x, y);
13355
13356   if (player->is_moving)
13357     return;
13358
13359   for (i = 0; i < NUM_DIRECTIONS; i++)
13360   {
13361     int xx = x + xy[i][0];
13362     int yy = y + xy[i][1];
13363     int border_side = trigger_sides[i][1];
13364     int border_element;
13365
13366     if (!IN_LEV_FIELD(xx, yy))
13367       continue;
13368
13369     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13370       continue;         // center and border element not connected
13371
13372     border_element = Tile[xx][yy];
13373
13374     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13375                                player->index_bit, border_side);
13376     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13377                                         CE_PLAYER_NEXT_TO_X,
13378                                         player->index_bit, border_side);
13379
13380     /* use player element that is initially defined in the level playfield,
13381        not the player element that corresponds to the runtime player number
13382        (example: a level that contains EL_PLAYER_3 as the only player would
13383        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13384
13385     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13386                              CE_NEXT_TO_X, border_side);
13387   }
13388 }
13389
13390 void TestIfPlayerTouchesCustomElement(int x, int y)
13391 {
13392   static int xy[4][2] =
13393   {
13394     { 0, -1 },
13395     { -1, 0 },
13396     { +1, 0 },
13397     { 0, +1 }
13398   };
13399   static int trigger_sides[4][2] =
13400   {
13401     // center side       border side
13402     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13403     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13404     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13405     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13406   };
13407   static int touch_dir[4] =
13408   {
13409     MV_LEFT | MV_RIGHT,
13410     MV_UP   | MV_DOWN,
13411     MV_UP   | MV_DOWN,
13412     MV_LEFT | MV_RIGHT
13413   };
13414   int center_element = Tile[x][y];      // should always be non-moving!
13415   int i;
13416
13417   for (i = 0; i < NUM_DIRECTIONS; i++)
13418   {
13419     int xx = x + xy[i][0];
13420     int yy = y + xy[i][1];
13421     int center_side = trigger_sides[i][0];
13422     int border_side = trigger_sides[i][1];
13423     int border_element;
13424
13425     if (!IN_LEV_FIELD(xx, yy))
13426       continue;
13427
13428     if (IS_PLAYER(x, y))                // player found at center element
13429     {
13430       struct PlayerInfo *player = PLAYERINFO(x, y);
13431
13432       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13433         border_element = Tile[xx][yy];          // may be moving!
13434       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13435         border_element = Tile[xx][yy];
13436       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13437         border_element = MovingOrBlocked2Element(xx, yy);
13438       else
13439         continue;               // center and border element do not touch
13440
13441       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13442                                  player->index_bit, border_side);
13443       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13444                                           CE_PLAYER_TOUCHES_X,
13445                                           player->index_bit, border_side);
13446
13447       {
13448         /* use player element that is initially defined in the level playfield,
13449            not the player element that corresponds to the runtime player number
13450            (example: a level that contains EL_PLAYER_3 as the only player would
13451            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13452         int player_element = PLAYERINFO(x, y)->initial_element;
13453
13454         CheckElementChangeBySide(xx, yy, border_element, player_element,
13455                                  CE_TOUCHING_X, border_side);
13456       }
13457     }
13458     else if (IS_PLAYER(xx, yy))         // player found at border element
13459     {
13460       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13461
13462       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13463       {
13464         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13465           continue;             // center and border element do not touch
13466       }
13467
13468       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13469                                  player->index_bit, center_side);
13470       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13471                                           CE_PLAYER_TOUCHES_X,
13472                                           player->index_bit, center_side);
13473
13474       {
13475         /* use player element that is initially defined in the level playfield,
13476            not the player element that corresponds to the runtime player number
13477            (example: a level that contains EL_PLAYER_3 as the only player would
13478            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13479         int player_element = PLAYERINFO(xx, yy)->initial_element;
13480
13481         CheckElementChangeBySide(x, y, center_element, player_element,
13482                                  CE_TOUCHING_X, center_side);
13483       }
13484
13485       break;
13486     }
13487   }
13488 }
13489
13490 void TestIfElementNextToCustomElement(int x, int y)
13491 {
13492   static int xy[4][2] =
13493   {
13494     { 0, -1 },
13495     { -1, 0 },
13496     { +1, 0 },
13497     { 0, +1 }
13498   };
13499   static int trigger_sides[4][2] =
13500   {
13501     // center side      border side
13502     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13503     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13504     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13505     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13506   };
13507   int center_element = Tile[x][y];      // should always be non-moving!
13508   int i;
13509
13510   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13511     return;
13512
13513   for (i = 0; i < NUM_DIRECTIONS; i++)
13514   {
13515     int xx = x + xy[i][0];
13516     int yy = y + xy[i][1];
13517     int border_side = trigger_sides[i][1];
13518     int border_element;
13519
13520     if (!IN_LEV_FIELD(xx, yy))
13521       continue;
13522
13523     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13524       continue;                 // center and border element not connected
13525
13526     border_element = Tile[xx][yy];
13527
13528     // check for change of center element (but change it only once)
13529     if (CheckElementChangeBySide(x, y, center_element, border_element,
13530                                  CE_NEXT_TO_X, border_side))
13531       break;
13532   }
13533 }
13534
13535 void TestIfElementTouchesCustomElement(int x, int y)
13536 {
13537   static int xy[4][2] =
13538   {
13539     { 0, -1 },
13540     { -1, 0 },
13541     { +1, 0 },
13542     { 0, +1 }
13543   };
13544   static int trigger_sides[4][2] =
13545   {
13546     // center side      border side
13547     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13548     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13549     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13550     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13551   };
13552   static int touch_dir[4] =
13553   {
13554     MV_LEFT | MV_RIGHT,
13555     MV_UP   | MV_DOWN,
13556     MV_UP   | MV_DOWN,
13557     MV_LEFT | MV_RIGHT
13558   };
13559   boolean change_center_element = FALSE;
13560   int center_element = Tile[x][y];      // should always be non-moving!
13561   int border_element_old[NUM_DIRECTIONS];
13562   int i;
13563
13564   for (i = 0; i < NUM_DIRECTIONS; i++)
13565   {
13566     int xx = x + xy[i][0];
13567     int yy = y + xy[i][1];
13568     int border_element;
13569
13570     border_element_old[i] = -1;
13571
13572     if (!IN_LEV_FIELD(xx, yy))
13573       continue;
13574
13575     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13576       border_element = Tile[xx][yy];    // may be moving!
13577     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13578       border_element = Tile[xx][yy];
13579     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13580       border_element = MovingOrBlocked2Element(xx, yy);
13581     else
13582       continue;                 // center and border element do not touch
13583
13584     border_element_old[i] = border_element;
13585   }
13586
13587   for (i = 0; i < NUM_DIRECTIONS; i++)
13588   {
13589     int xx = x + xy[i][0];
13590     int yy = y + xy[i][1];
13591     int center_side = trigger_sides[i][0];
13592     int border_element = border_element_old[i];
13593
13594     if (border_element == -1)
13595       continue;
13596
13597     // check for change of border element
13598     CheckElementChangeBySide(xx, yy, border_element, center_element,
13599                              CE_TOUCHING_X, center_side);
13600
13601     // (center element cannot be player, so we dont have to check this here)
13602   }
13603
13604   for (i = 0; i < NUM_DIRECTIONS; i++)
13605   {
13606     int xx = x + xy[i][0];
13607     int yy = y + xy[i][1];
13608     int border_side = trigger_sides[i][1];
13609     int border_element = border_element_old[i];
13610
13611     if (border_element == -1)
13612       continue;
13613
13614     // check for change of center element (but change it only once)
13615     if (!change_center_element)
13616       change_center_element =
13617         CheckElementChangeBySide(x, y, center_element, border_element,
13618                                  CE_TOUCHING_X, border_side);
13619
13620     if (IS_PLAYER(xx, yy))
13621     {
13622       /* use player element that is initially defined in the level playfield,
13623          not the player element that corresponds to the runtime player number
13624          (example: a level that contains EL_PLAYER_3 as the only player would
13625          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13626       int player_element = PLAYERINFO(xx, yy)->initial_element;
13627
13628       CheckElementChangeBySide(x, y, center_element, player_element,
13629                                CE_TOUCHING_X, border_side);
13630     }
13631   }
13632 }
13633
13634 void TestIfElementHitsCustomElement(int x, int y, int direction)
13635 {
13636   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13637   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13638   int hitx = x + dx, hity = y + dy;
13639   int hitting_element = Tile[x][y];
13640   int touched_element;
13641
13642   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13643     return;
13644
13645   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13646                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13647
13648   if (IN_LEV_FIELD(hitx, hity))
13649   {
13650     int opposite_direction = MV_DIR_OPPOSITE(direction);
13651     int hitting_side = direction;
13652     int touched_side = opposite_direction;
13653     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13654                           MovDir[hitx][hity] != direction ||
13655                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13656
13657     object_hit = TRUE;
13658
13659     if (object_hit)
13660     {
13661       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13662                                CE_HITTING_X, touched_side);
13663
13664       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13665                                CE_HIT_BY_X, hitting_side);
13666
13667       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13668                                CE_HIT_BY_SOMETHING, opposite_direction);
13669
13670       if (IS_PLAYER(hitx, hity))
13671       {
13672         /* use player element that is initially defined in the level playfield,
13673            not the player element that corresponds to the runtime player number
13674            (example: a level that contains EL_PLAYER_3 as the only player would
13675            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13676         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13677
13678         CheckElementChangeBySide(x, y, hitting_element, player_element,
13679                                  CE_HITTING_X, touched_side);
13680       }
13681     }
13682   }
13683
13684   // "hitting something" is also true when hitting the playfield border
13685   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13686                            CE_HITTING_SOMETHING, direction);
13687 }
13688
13689 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13690 {
13691   int i, kill_x = -1, kill_y = -1;
13692
13693   int bad_element = -1;
13694   static int test_xy[4][2] =
13695   {
13696     { 0, -1 },
13697     { -1, 0 },
13698     { +1, 0 },
13699     { 0, +1 }
13700   };
13701   static int test_dir[4] =
13702   {
13703     MV_UP,
13704     MV_LEFT,
13705     MV_RIGHT,
13706     MV_DOWN
13707   };
13708
13709   for (i = 0; i < NUM_DIRECTIONS; i++)
13710   {
13711     int test_x, test_y, test_move_dir, test_element;
13712
13713     test_x = good_x + test_xy[i][0];
13714     test_y = good_y + test_xy[i][1];
13715
13716     if (!IN_LEV_FIELD(test_x, test_y))
13717       continue;
13718
13719     test_move_dir =
13720       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13721
13722     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13723
13724     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13725        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13726     */
13727     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13728         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13729     {
13730       kill_x = test_x;
13731       kill_y = test_y;
13732       bad_element = test_element;
13733
13734       break;
13735     }
13736   }
13737
13738   if (kill_x != -1 || kill_y != -1)
13739   {
13740     if (IS_PLAYER(good_x, good_y))
13741     {
13742       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13743
13744       if (player->shield_deadly_time_left > 0 &&
13745           !IS_INDESTRUCTIBLE(bad_element))
13746         Bang(kill_x, kill_y);
13747       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13748         KillPlayer(player);
13749     }
13750     else
13751       Bang(good_x, good_y);
13752   }
13753 }
13754
13755 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13756 {
13757   int i, kill_x = -1, kill_y = -1;
13758   int bad_element = Tile[bad_x][bad_y];
13759   static int test_xy[4][2] =
13760   {
13761     { 0, -1 },
13762     { -1, 0 },
13763     { +1, 0 },
13764     { 0, +1 }
13765   };
13766   static int touch_dir[4] =
13767   {
13768     MV_LEFT | MV_RIGHT,
13769     MV_UP   | MV_DOWN,
13770     MV_UP   | MV_DOWN,
13771     MV_LEFT | MV_RIGHT
13772   };
13773   static int test_dir[4] =
13774   {
13775     MV_UP,
13776     MV_LEFT,
13777     MV_RIGHT,
13778     MV_DOWN
13779   };
13780
13781   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13782     return;
13783
13784   for (i = 0; i < NUM_DIRECTIONS; i++)
13785   {
13786     int test_x, test_y, test_move_dir, test_element;
13787
13788     test_x = bad_x + test_xy[i][0];
13789     test_y = bad_y + test_xy[i][1];
13790
13791     if (!IN_LEV_FIELD(test_x, test_y))
13792       continue;
13793
13794     test_move_dir =
13795       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13796
13797     test_element = Tile[test_x][test_y];
13798
13799     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13800        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13801     */
13802     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13803         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13804     {
13805       // good thing is player or penguin that does not move away
13806       if (IS_PLAYER(test_x, test_y))
13807       {
13808         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13809
13810         if (bad_element == EL_ROBOT && player->is_moving)
13811           continue;     // robot does not kill player if he is moving
13812
13813         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13814         {
13815           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13816             continue;           // center and border element do not touch
13817         }
13818
13819         kill_x = test_x;
13820         kill_y = test_y;
13821
13822         break;
13823       }
13824       else if (test_element == EL_PENGUIN)
13825       {
13826         kill_x = test_x;
13827         kill_y = test_y;
13828
13829         break;
13830       }
13831     }
13832   }
13833
13834   if (kill_x != -1 || kill_y != -1)
13835   {
13836     if (IS_PLAYER(kill_x, kill_y))
13837     {
13838       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13839
13840       if (player->shield_deadly_time_left > 0 &&
13841           !IS_INDESTRUCTIBLE(bad_element))
13842         Bang(bad_x, bad_y);
13843       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13844         KillPlayer(player);
13845     }
13846     else
13847       Bang(kill_x, kill_y);
13848   }
13849 }
13850
13851 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13852 {
13853   int bad_element = Tile[bad_x][bad_y];
13854   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13855   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13856   int test_x = bad_x + dx, test_y = bad_y + dy;
13857   int test_move_dir, test_element;
13858   int kill_x = -1, kill_y = -1;
13859
13860   if (!IN_LEV_FIELD(test_x, test_y))
13861     return;
13862
13863   test_move_dir =
13864     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13865
13866   test_element = Tile[test_x][test_y];
13867
13868   if (test_move_dir != bad_move_dir)
13869   {
13870     // good thing can be player or penguin that does not move away
13871     if (IS_PLAYER(test_x, test_y))
13872     {
13873       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13874
13875       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13876          player as being hit when he is moving towards the bad thing, because
13877          the "get hit by" condition would be lost after the player stops) */
13878       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13879         return;         // player moves away from bad thing
13880
13881       kill_x = test_x;
13882       kill_y = test_y;
13883     }
13884     else if (test_element == EL_PENGUIN)
13885     {
13886       kill_x = test_x;
13887       kill_y = test_y;
13888     }
13889   }
13890
13891   if (kill_x != -1 || kill_y != -1)
13892   {
13893     if (IS_PLAYER(kill_x, kill_y))
13894     {
13895       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13896
13897       if (player->shield_deadly_time_left > 0 &&
13898           !IS_INDESTRUCTIBLE(bad_element))
13899         Bang(bad_x, bad_y);
13900       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13901         KillPlayer(player);
13902     }
13903     else
13904       Bang(kill_x, kill_y);
13905   }
13906 }
13907
13908 void TestIfPlayerTouchesBadThing(int x, int y)
13909 {
13910   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13911 }
13912
13913 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13914 {
13915   TestIfGoodThingHitsBadThing(x, y, move_dir);
13916 }
13917
13918 void TestIfBadThingTouchesPlayer(int x, int y)
13919 {
13920   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13921 }
13922
13923 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13924 {
13925   TestIfBadThingHitsGoodThing(x, y, move_dir);
13926 }
13927
13928 void TestIfFriendTouchesBadThing(int x, int y)
13929 {
13930   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13931 }
13932
13933 void TestIfBadThingTouchesFriend(int x, int y)
13934 {
13935   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13936 }
13937
13938 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13939 {
13940   int i, kill_x = bad_x, kill_y = bad_y;
13941   static int xy[4][2] =
13942   {
13943     { 0, -1 },
13944     { -1, 0 },
13945     { +1, 0 },
13946     { 0, +1 }
13947   };
13948
13949   for (i = 0; i < NUM_DIRECTIONS; i++)
13950   {
13951     int x, y, element;
13952
13953     x = bad_x + xy[i][0];
13954     y = bad_y + xy[i][1];
13955     if (!IN_LEV_FIELD(x, y))
13956       continue;
13957
13958     element = Tile[x][y];
13959     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13960         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13961     {
13962       kill_x = x;
13963       kill_y = y;
13964       break;
13965     }
13966   }
13967
13968   if (kill_x != bad_x || kill_y != bad_y)
13969     Bang(bad_x, bad_y);
13970 }
13971
13972 void KillPlayer(struct PlayerInfo *player)
13973 {
13974   int jx = player->jx, jy = player->jy;
13975
13976   if (!player->active)
13977     return;
13978
13979 #if 0
13980   Debug("game:playing:KillPlayer",
13981         "0: killed == %d, active == %d, reanimated == %d",
13982         player->killed, player->active, player->reanimated);
13983 #endif
13984
13985   /* the following code was introduced to prevent an infinite loop when calling
13986      -> Bang()
13987      -> CheckTriggeredElementChangeExt()
13988      -> ExecuteCustomElementAction()
13989      -> KillPlayer()
13990      -> (infinitely repeating the above sequence of function calls)
13991      which occurs when killing the player while having a CE with the setting
13992      "kill player X when explosion of <player X>"; the solution using a new
13993      field "player->killed" was chosen for backwards compatibility, although
13994      clever use of the fields "player->active" etc. would probably also work */
13995 #if 1
13996   if (player->killed)
13997     return;
13998 #endif
13999
14000   player->killed = TRUE;
14001
14002   // remove accessible field at the player's position
14003   Tile[jx][jy] = EL_EMPTY;
14004
14005   // deactivate shield (else Bang()/Explode() would not work right)
14006   player->shield_normal_time_left = 0;
14007   player->shield_deadly_time_left = 0;
14008
14009 #if 0
14010   Debug("game:playing:KillPlayer",
14011         "1: killed == %d, active == %d, reanimated == %d",
14012         player->killed, player->active, player->reanimated);
14013 #endif
14014
14015   Bang(jx, jy);
14016
14017 #if 0
14018   Debug("game:playing:KillPlayer",
14019         "2: killed == %d, active == %d, reanimated == %d",
14020         player->killed, player->active, player->reanimated);
14021 #endif
14022
14023   if (player->reanimated)       // killed player may have been reanimated
14024     player->killed = player->reanimated = FALSE;
14025   else
14026     BuryPlayer(player);
14027 }
14028
14029 static void KillPlayerUnlessEnemyProtected(int x, int y)
14030 {
14031   if (!PLAYER_ENEMY_PROTECTED(x, y))
14032     KillPlayer(PLAYERINFO(x, y));
14033 }
14034
14035 static void KillPlayerUnlessExplosionProtected(int x, int y)
14036 {
14037   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14038     KillPlayer(PLAYERINFO(x, y));
14039 }
14040
14041 void BuryPlayer(struct PlayerInfo *player)
14042 {
14043   int jx = player->jx, jy = player->jy;
14044
14045   if (!player->active)
14046     return;
14047
14048   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14049   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14050
14051   RemovePlayer(player);
14052
14053   player->buried = TRUE;
14054
14055   if (game.all_players_gone)
14056     game.GameOver = TRUE;
14057 }
14058
14059 void RemovePlayer(struct PlayerInfo *player)
14060 {
14061   int jx = player->jx, jy = player->jy;
14062   int i, found = FALSE;
14063
14064   player->present = FALSE;
14065   player->active = FALSE;
14066
14067   // required for some CE actions (even if the player is not active anymore)
14068   player->MovPos = 0;
14069
14070   if (!ExplodeField[jx][jy])
14071     StorePlayer[jx][jy] = 0;
14072
14073   if (player->is_moving)
14074     TEST_DrawLevelField(player->last_jx, player->last_jy);
14075
14076   for (i = 0; i < MAX_PLAYERS; i++)
14077     if (stored_player[i].active)
14078       found = TRUE;
14079
14080   if (!found)
14081   {
14082     game.all_players_gone = TRUE;
14083     game.GameOver = TRUE;
14084   }
14085
14086   game.exit_x = game.robot_wheel_x = jx;
14087   game.exit_y = game.robot_wheel_y = jy;
14088 }
14089
14090 void ExitPlayer(struct PlayerInfo *player)
14091 {
14092   DrawPlayer(player);   // needed here only to cleanup last field
14093   RemovePlayer(player);
14094
14095   if (game.players_still_needed > 0)
14096     game.players_still_needed--;
14097 }
14098
14099 static void SetFieldForSnapping(int x, int y, int element, int direction,
14100                                 int player_index_bit)
14101 {
14102   struct ElementInfo *ei = &element_info[element];
14103   int direction_bit = MV_DIR_TO_BIT(direction);
14104   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14105   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14106                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14107
14108   Tile[x][y] = EL_ELEMENT_SNAPPING;
14109   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14110   MovDir[x][y] = direction;
14111   Store[x][y] = element;
14112   Store2[x][y] = player_index_bit;
14113
14114   ResetGfxAnimation(x, y);
14115
14116   GfxElement[x][y] = element;
14117   GfxAction[x][y] = action;
14118   GfxDir[x][y] = direction;
14119   GfxFrame[x][y] = -1;
14120 }
14121
14122 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14123                                    int player_index_bit)
14124 {
14125   TestIfElementTouchesCustomElement(x, y);      // for empty space
14126
14127   if (level.finish_dig_collect)
14128   {
14129     int dig_side = MV_DIR_OPPOSITE(direction);
14130     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14131                         CE_PLAYER_COLLECTS_X);
14132
14133     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14134                                         player_index_bit, dig_side);
14135     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14136                                         player_index_bit, dig_side);
14137   }
14138 }
14139
14140 /*
14141   =============================================================================
14142   checkDiagonalPushing()
14143   -----------------------------------------------------------------------------
14144   check if diagonal input device direction results in pushing of object
14145   (by checking if the alternative direction is walkable, diggable, ...)
14146   =============================================================================
14147 */
14148
14149 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14150                                     int x, int y, int real_dx, int real_dy)
14151 {
14152   int jx, jy, dx, dy, xx, yy;
14153
14154   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14155     return TRUE;
14156
14157   // diagonal direction: check alternative direction
14158   jx = player->jx;
14159   jy = player->jy;
14160   dx = x - jx;
14161   dy = y - jy;
14162   xx = jx + (dx == 0 ? real_dx : 0);
14163   yy = jy + (dy == 0 ? real_dy : 0);
14164
14165   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14166 }
14167
14168 /*
14169   =============================================================================
14170   DigField()
14171   -----------------------------------------------------------------------------
14172   x, y:                 field next to player (non-diagonal) to try to dig to
14173   real_dx, real_dy:     direction as read from input device (can be diagonal)
14174   =============================================================================
14175 */
14176
14177 static int DigField(struct PlayerInfo *player,
14178                     int oldx, int oldy, int x, int y,
14179                     int real_dx, int real_dy, int mode)
14180 {
14181   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14182   boolean player_was_pushing = player->is_pushing;
14183   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14184   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14185   int jx = oldx, jy = oldy;
14186   int dx = x - jx, dy = y - jy;
14187   int nextx = x + dx, nexty = y + dy;
14188   int move_direction = (dx == -1 ? MV_LEFT  :
14189                         dx == +1 ? MV_RIGHT :
14190                         dy == -1 ? MV_UP    :
14191                         dy == +1 ? MV_DOWN  : MV_NONE);
14192   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14193   int dig_side = MV_DIR_OPPOSITE(move_direction);
14194   int old_element = Tile[jx][jy];
14195   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14196   int collect_count;
14197
14198   if (is_player)                // function can also be called by EL_PENGUIN
14199   {
14200     if (player->MovPos == 0)
14201     {
14202       player->is_digging = FALSE;
14203       player->is_collecting = FALSE;
14204     }
14205
14206     if (player->MovPos == 0)    // last pushing move finished
14207       player->is_pushing = FALSE;
14208
14209     if (mode == DF_NO_PUSH)     // player just stopped pushing
14210     {
14211       player->is_switching = FALSE;
14212       player->push_delay = -1;
14213
14214       return MP_NO_ACTION;
14215     }
14216   }
14217   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14218     old_element = Back[jx][jy];
14219
14220   // in case of element dropped at player position, check background
14221   else if (Back[jx][jy] != EL_EMPTY &&
14222            game.engine_version >= VERSION_IDENT(2,2,0,0))
14223     old_element = Back[jx][jy];
14224
14225   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14226     return MP_NO_ACTION;        // field has no opening in this direction
14227
14228   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14229     return MP_NO_ACTION;        // field has no opening in this direction
14230
14231   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14232   {
14233     SplashAcid(x, y);
14234
14235     Tile[jx][jy] = player->artwork_element;
14236     InitMovingField(jx, jy, MV_DOWN);
14237     Store[jx][jy] = EL_ACID;
14238     ContinueMoving(jx, jy);
14239     BuryPlayer(player);
14240
14241     return MP_DONT_RUN_INTO;
14242   }
14243
14244   if (player_can_move && DONT_RUN_INTO(element))
14245   {
14246     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14247
14248     return MP_DONT_RUN_INTO;
14249   }
14250
14251   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14252     return MP_NO_ACTION;
14253
14254   collect_count = element_info[element].collect_count_initial;
14255
14256   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14257     return MP_NO_ACTION;
14258
14259   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14260     player_can_move = player_can_move_or_snap;
14261
14262   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14263       game.engine_version >= VERSION_IDENT(2,2,0,0))
14264   {
14265     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14266                                player->index_bit, dig_side);
14267     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14268                                         player->index_bit, dig_side);
14269
14270     if (element == EL_DC_LANDMINE)
14271       Bang(x, y);
14272
14273     if (Tile[x][y] != element)          // field changed by snapping
14274       return MP_ACTION;
14275
14276     return MP_NO_ACTION;
14277   }
14278
14279   if (player->gravity && is_player && !player->is_auto_moving &&
14280       canFallDown(player) && move_direction != MV_DOWN &&
14281       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14282     return MP_NO_ACTION;        // player cannot walk here due to gravity
14283
14284   if (player_can_move &&
14285       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14286   {
14287     int sound_element = SND_ELEMENT(element);
14288     int sound_action = ACTION_WALKING;
14289
14290     if (IS_RND_GATE(element))
14291     {
14292       if (!player->key[RND_GATE_NR(element)])
14293         return MP_NO_ACTION;
14294     }
14295     else if (IS_RND_GATE_GRAY(element))
14296     {
14297       if (!player->key[RND_GATE_GRAY_NR(element)])
14298         return MP_NO_ACTION;
14299     }
14300     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14301     {
14302       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14303         return MP_NO_ACTION;
14304     }
14305     else if (element == EL_EXIT_OPEN ||
14306              element == EL_EM_EXIT_OPEN ||
14307              element == EL_EM_EXIT_OPENING ||
14308              element == EL_STEEL_EXIT_OPEN ||
14309              element == EL_EM_STEEL_EXIT_OPEN ||
14310              element == EL_EM_STEEL_EXIT_OPENING ||
14311              element == EL_SP_EXIT_OPEN ||
14312              element == EL_SP_EXIT_OPENING)
14313     {
14314       sound_action = ACTION_PASSING;    // player is passing exit
14315     }
14316     else if (element == EL_EMPTY)
14317     {
14318       sound_action = ACTION_MOVING;             // nothing to walk on
14319     }
14320
14321     // play sound from background or player, whatever is available
14322     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14323       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14324     else
14325       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14326   }
14327   else if (player_can_move &&
14328            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14329   {
14330     if (!ACCESS_FROM(element, opposite_direction))
14331       return MP_NO_ACTION;      // field not accessible from this direction
14332
14333     if (CAN_MOVE(element))      // only fixed elements can be passed!
14334       return MP_NO_ACTION;
14335
14336     if (IS_EM_GATE(element))
14337     {
14338       if (!player->key[EM_GATE_NR(element)])
14339         return MP_NO_ACTION;
14340     }
14341     else if (IS_EM_GATE_GRAY(element))
14342     {
14343       if (!player->key[EM_GATE_GRAY_NR(element)])
14344         return MP_NO_ACTION;
14345     }
14346     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14347     {
14348       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14349         return MP_NO_ACTION;
14350     }
14351     else if (IS_EMC_GATE(element))
14352     {
14353       if (!player->key[EMC_GATE_NR(element)])
14354         return MP_NO_ACTION;
14355     }
14356     else if (IS_EMC_GATE_GRAY(element))
14357     {
14358       if (!player->key[EMC_GATE_GRAY_NR(element)])
14359         return MP_NO_ACTION;
14360     }
14361     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14362     {
14363       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14364         return MP_NO_ACTION;
14365     }
14366     else if (element == EL_DC_GATE_WHITE ||
14367              element == EL_DC_GATE_WHITE_GRAY ||
14368              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14369     {
14370       if (player->num_white_keys == 0)
14371         return MP_NO_ACTION;
14372
14373       player->num_white_keys--;
14374     }
14375     else if (IS_SP_PORT(element))
14376     {
14377       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14378           element == EL_SP_GRAVITY_PORT_RIGHT ||
14379           element == EL_SP_GRAVITY_PORT_UP ||
14380           element == EL_SP_GRAVITY_PORT_DOWN)
14381         player->gravity = !player->gravity;
14382       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14383                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14384                element == EL_SP_GRAVITY_ON_PORT_UP ||
14385                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14386         player->gravity = TRUE;
14387       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14388                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14389                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14390                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14391         player->gravity = FALSE;
14392     }
14393
14394     // automatically move to the next field with double speed
14395     player->programmed_action = move_direction;
14396
14397     if (player->move_delay_reset_counter == 0)
14398     {
14399       player->move_delay_reset_counter = 2;     // two double speed steps
14400
14401       DOUBLE_PLAYER_SPEED(player);
14402     }
14403
14404     PlayLevelSoundAction(x, y, ACTION_PASSING);
14405   }
14406   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14407   {
14408     RemoveField(x, y);
14409
14410     if (mode != DF_SNAP)
14411     {
14412       GfxElement[x][y] = GFX_ELEMENT(element);
14413       player->is_digging = TRUE;
14414     }
14415
14416     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14417
14418     // use old behaviour for old levels (digging)
14419     if (!level.finish_dig_collect)
14420     {
14421       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14422                                           player->index_bit, dig_side);
14423
14424       // if digging triggered player relocation, finish digging tile
14425       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14426         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14427     }
14428
14429     if (mode == DF_SNAP)
14430     {
14431       if (level.block_snap_field)
14432         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14433       else
14434         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14435
14436       // use old behaviour for old levels (snapping)
14437       if (!level.finish_dig_collect)
14438         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14439                                             player->index_bit, dig_side);
14440     }
14441   }
14442   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14443   {
14444     RemoveField(x, y);
14445
14446     if (is_player && mode != DF_SNAP)
14447     {
14448       GfxElement[x][y] = element;
14449       player->is_collecting = TRUE;
14450     }
14451
14452     if (element == EL_SPEED_PILL)
14453     {
14454       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14455     }
14456     else if (element == EL_EXTRA_TIME && level.time > 0)
14457     {
14458       TimeLeft += level.extra_time;
14459
14460       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14461
14462       DisplayGameControlValues();
14463     }
14464     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14465     {
14466       player->shield_normal_time_left += level.shield_normal_time;
14467       if (element == EL_SHIELD_DEADLY)
14468         player->shield_deadly_time_left += level.shield_deadly_time;
14469     }
14470     else if (element == EL_DYNAMITE ||
14471              element == EL_EM_DYNAMITE ||
14472              element == EL_SP_DISK_RED)
14473     {
14474       if (player->inventory_size < MAX_INVENTORY_SIZE)
14475         player->inventory_element[player->inventory_size++] = element;
14476
14477       DrawGameDoorValues();
14478     }
14479     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14480     {
14481       player->dynabomb_count++;
14482       player->dynabombs_left++;
14483     }
14484     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14485     {
14486       player->dynabomb_size++;
14487     }
14488     else if (element == EL_DYNABOMB_INCREASE_POWER)
14489     {
14490       player->dynabomb_xl = TRUE;
14491     }
14492     else if (IS_KEY(element))
14493     {
14494       player->key[KEY_NR(element)] = TRUE;
14495
14496       DrawGameDoorValues();
14497     }
14498     else if (element == EL_DC_KEY_WHITE)
14499     {
14500       player->num_white_keys++;
14501
14502       // display white keys?
14503       // DrawGameDoorValues();
14504     }
14505     else if (IS_ENVELOPE(element))
14506     {
14507       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14508
14509       if (!wait_for_snapping)
14510         player->show_envelope = element;
14511     }
14512     else if (element == EL_EMC_LENSES)
14513     {
14514       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14515
14516       RedrawAllInvisibleElementsForLenses();
14517     }
14518     else if (element == EL_EMC_MAGNIFIER)
14519     {
14520       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14521
14522       RedrawAllInvisibleElementsForMagnifier();
14523     }
14524     else if (IS_DROPPABLE(element) ||
14525              IS_THROWABLE(element))     // can be collected and dropped
14526     {
14527       int i;
14528
14529       if (collect_count == 0)
14530         player->inventory_infinite_element = element;
14531       else
14532         for (i = 0; i < collect_count; i++)
14533           if (player->inventory_size < MAX_INVENTORY_SIZE)
14534             player->inventory_element[player->inventory_size++] = element;
14535
14536       DrawGameDoorValues();
14537     }
14538     else if (collect_count > 0)
14539     {
14540       game.gems_still_needed -= collect_count;
14541       if (game.gems_still_needed < 0)
14542         game.gems_still_needed = 0;
14543
14544       game.snapshot.collected_item = TRUE;
14545
14546       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14547
14548       DisplayGameControlValues();
14549     }
14550
14551     RaiseScoreElement(element);
14552     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14553
14554     // use old behaviour for old levels (collecting)
14555     if (!level.finish_dig_collect && is_player)
14556     {
14557       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14558                                           player->index_bit, dig_side);
14559
14560       // if collecting triggered player relocation, finish collecting tile
14561       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14562         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14563     }
14564
14565     if (mode == DF_SNAP)
14566     {
14567       if (level.block_snap_field)
14568         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14569       else
14570         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14571
14572       // use old behaviour for old levels (snapping)
14573       if (!level.finish_dig_collect)
14574         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14575                                             player->index_bit, dig_side);
14576     }
14577   }
14578   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14579   {
14580     if (mode == DF_SNAP && element != EL_BD_ROCK)
14581       return MP_NO_ACTION;
14582
14583     if (CAN_FALL(element) && dy)
14584       return MP_NO_ACTION;
14585
14586     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14587         !(element == EL_SPRING && level.use_spring_bug))
14588       return MP_NO_ACTION;
14589
14590     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14591         ((move_direction & MV_VERTICAL &&
14592           ((element_info[element].move_pattern & MV_LEFT &&
14593             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14594            (element_info[element].move_pattern & MV_RIGHT &&
14595             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14596          (move_direction & MV_HORIZONTAL &&
14597           ((element_info[element].move_pattern & MV_UP &&
14598             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14599            (element_info[element].move_pattern & MV_DOWN &&
14600             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14601       return MP_NO_ACTION;
14602
14603     // do not push elements already moving away faster than player
14604     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14605         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14606       return MP_NO_ACTION;
14607
14608     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14609     {
14610       if (player->push_delay_value == -1 || !player_was_pushing)
14611         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14612     }
14613     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14614     {
14615       if (player->push_delay_value == -1)
14616         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14617     }
14618     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14619     {
14620       if (!player->is_pushing)
14621         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14622     }
14623
14624     player->is_pushing = TRUE;
14625     player->is_active = TRUE;
14626
14627     if (!(IN_LEV_FIELD(nextx, nexty) &&
14628           (IS_FREE(nextx, nexty) ||
14629            (IS_SB_ELEMENT(element) &&
14630             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14631            (IS_CUSTOM_ELEMENT(element) &&
14632             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14633       return MP_NO_ACTION;
14634
14635     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14636       return MP_NO_ACTION;
14637
14638     if (player->push_delay == -1)       // new pushing; restart delay
14639       player->push_delay = 0;
14640
14641     if (player->push_delay < player->push_delay_value &&
14642         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14643         element != EL_SPRING && element != EL_BALLOON)
14644     {
14645       // make sure that there is no move delay before next try to push
14646       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14647         player->move_delay = 0;
14648
14649       return MP_NO_ACTION;
14650     }
14651
14652     if (IS_CUSTOM_ELEMENT(element) &&
14653         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14654     {
14655       if (!DigFieldByCE(nextx, nexty, element))
14656         return MP_NO_ACTION;
14657     }
14658
14659     if (IS_SB_ELEMENT(element))
14660     {
14661       boolean sokoban_task_solved = FALSE;
14662
14663       if (element == EL_SOKOBAN_FIELD_FULL)
14664       {
14665         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14666
14667         IncrementSokobanFieldsNeeded();
14668         IncrementSokobanObjectsNeeded();
14669       }
14670
14671       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14672       {
14673         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14674
14675         DecrementSokobanFieldsNeeded();
14676         DecrementSokobanObjectsNeeded();
14677
14678         // sokoban object was pushed from empty field to sokoban field
14679         if (Back[x][y] == EL_EMPTY)
14680           sokoban_task_solved = TRUE;
14681       }
14682
14683       Tile[x][y] = EL_SOKOBAN_OBJECT;
14684
14685       if (Back[x][y] == Back[nextx][nexty])
14686         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14687       else if (Back[x][y] != 0)
14688         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14689                                     ACTION_EMPTYING);
14690       else
14691         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14692                                     ACTION_FILLING);
14693
14694       if (sokoban_task_solved &&
14695           game.sokoban_fields_still_needed == 0 &&
14696           game.sokoban_objects_still_needed == 0 &&
14697           level.auto_exit_sokoban)
14698       {
14699         game.players_still_needed = 0;
14700
14701         LevelSolved();
14702
14703         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14704       }
14705     }
14706     else
14707       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14708
14709     InitMovingField(x, y, move_direction);
14710     GfxAction[x][y] = ACTION_PUSHING;
14711
14712     if (mode == DF_SNAP)
14713       ContinueMoving(x, y);
14714     else
14715       MovPos[x][y] = (dx != 0 ? dx : dy);
14716
14717     Pushed[x][y] = TRUE;
14718     Pushed[nextx][nexty] = TRUE;
14719
14720     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14721       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14722     else
14723       player->push_delay_value = -1;    // get new value later
14724
14725     // check for element change _after_ element has been pushed
14726     if (game.use_change_when_pushing_bug)
14727     {
14728       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14729                                  player->index_bit, dig_side);
14730       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14731                                           player->index_bit, dig_side);
14732     }
14733   }
14734   else if (IS_SWITCHABLE(element))
14735   {
14736     if (PLAYER_SWITCHING(player, x, y))
14737     {
14738       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14739                                           player->index_bit, dig_side);
14740
14741       return MP_ACTION;
14742     }
14743
14744     player->is_switching = TRUE;
14745     player->switch_x = x;
14746     player->switch_y = y;
14747
14748     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14749
14750     if (element == EL_ROBOT_WHEEL)
14751     {
14752       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14753
14754       game.robot_wheel_x = x;
14755       game.robot_wheel_y = y;
14756       game.robot_wheel_active = TRUE;
14757
14758       TEST_DrawLevelField(x, y);
14759     }
14760     else if (element == EL_SP_TERMINAL)
14761     {
14762       int xx, yy;
14763
14764       SCAN_PLAYFIELD(xx, yy)
14765       {
14766         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14767         {
14768           Bang(xx, yy);
14769         }
14770         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14771         {
14772           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14773
14774           ResetGfxAnimation(xx, yy);
14775           TEST_DrawLevelField(xx, yy);
14776         }
14777       }
14778     }
14779     else if (IS_BELT_SWITCH(element))
14780     {
14781       ToggleBeltSwitch(x, y);
14782     }
14783     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14784              element == EL_SWITCHGATE_SWITCH_DOWN ||
14785              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14786              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14787     {
14788       ToggleSwitchgateSwitch(x, y);
14789     }
14790     else if (element == EL_LIGHT_SWITCH ||
14791              element == EL_LIGHT_SWITCH_ACTIVE)
14792     {
14793       ToggleLightSwitch(x, y);
14794     }
14795     else if (element == EL_TIMEGATE_SWITCH ||
14796              element == EL_DC_TIMEGATE_SWITCH)
14797     {
14798       ActivateTimegateSwitch(x, y);
14799     }
14800     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14801              element == EL_BALLOON_SWITCH_RIGHT ||
14802              element == EL_BALLOON_SWITCH_UP    ||
14803              element == EL_BALLOON_SWITCH_DOWN  ||
14804              element == EL_BALLOON_SWITCH_NONE  ||
14805              element == EL_BALLOON_SWITCH_ANY)
14806     {
14807       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14808                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14809                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14810                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14811                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14812                              move_direction);
14813     }
14814     else if (element == EL_LAMP)
14815     {
14816       Tile[x][y] = EL_LAMP_ACTIVE;
14817       game.lights_still_needed--;
14818
14819       ResetGfxAnimation(x, y);
14820       TEST_DrawLevelField(x, y);
14821     }
14822     else if (element == EL_TIME_ORB_FULL)
14823     {
14824       Tile[x][y] = EL_TIME_ORB_EMPTY;
14825
14826       if (level.time > 0 || level.use_time_orb_bug)
14827       {
14828         TimeLeft += level.time_orb_time;
14829         game.no_time_limit = FALSE;
14830
14831         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14832
14833         DisplayGameControlValues();
14834       }
14835
14836       ResetGfxAnimation(x, y);
14837       TEST_DrawLevelField(x, y);
14838     }
14839     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14840              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14841     {
14842       int xx, yy;
14843
14844       game.ball_active = !game.ball_active;
14845
14846       SCAN_PLAYFIELD(xx, yy)
14847       {
14848         int e = Tile[xx][yy];
14849
14850         if (game.ball_active)
14851         {
14852           if (e == EL_EMC_MAGIC_BALL)
14853             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14854           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14855             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14856         }
14857         else
14858         {
14859           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14860             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14861           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14862             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14863         }
14864       }
14865     }
14866
14867     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14868                                         player->index_bit, dig_side);
14869
14870     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14871                                         player->index_bit, dig_side);
14872
14873     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14874                                         player->index_bit, dig_side);
14875
14876     return MP_ACTION;
14877   }
14878   else
14879   {
14880     if (!PLAYER_SWITCHING(player, x, y))
14881     {
14882       player->is_switching = TRUE;
14883       player->switch_x = x;
14884       player->switch_y = y;
14885
14886       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14887                                  player->index_bit, dig_side);
14888       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14889                                           player->index_bit, dig_side);
14890
14891       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14892                                  player->index_bit, dig_side);
14893       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14894                                           player->index_bit, dig_side);
14895     }
14896
14897     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14898                                player->index_bit, dig_side);
14899     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14900                                         player->index_bit, dig_side);
14901
14902     return MP_NO_ACTION;
14903   }
14904
14905   player->push_delay = -1;
14906
14907   if (is_player)                // function can also be called by EL_PENGUIN
14908   {
14909     if (Tile[x][y] != element)          // really digged/collected something
14910     {
14911       player->is_collecting = !player->is_digging;
14912       player->is_active = TRUE;
14913
14914       player->last_removed_element = element;
14915     }
14916   }
14917
14918   return MP_MOVING;
14919 }
14920
14921 static boolean DigFieldByCE(int x, int y, int digging_element)
14922 {
14923   int element = Tile[x][y];
14924
14925   if (!IS_FREE(x, y))
14926   {
14927     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14928                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14929                   ACTION_BREAKING);
14930
14931     // no element can dig solid indestructible elements
14932     if (IS_INDESTRUCTIBLE(element) &&
14933         !IS_DIGGABLE(element) &&
14934         !IS_COLLECTIBLE(element))
14935       return FALSE;
14936
14937     if (AmoebaNr[x][y] &&
14938         (element == EL_AMOEBA_FULL ||
14939          element == EL_BD_AMOEBA ||
14940          element == EL_AMOEBA_GROWING))
14941     {
14942       AmoebaCnt[AmoebaNr[x][y]]--;
14943       AmoebaCnt2[AmoebaNr[x][y]]--;
14944     }
14945
14946     if (IS_MOVING(x, y))
14947       RemoveMovingField(x, y);
14948     else
14949     {
14950       RemoveField(x, y);
14951       TEST_DrawLevelField(x, y);
14952     }
14953
14954     // if digged element was about to explode, prevent the explosion
14955     ExplodeField[x][y] = EX_TYPE_NONE;
14956
14957     PlayLevelSoundAction(x, y, action);
14958   }
14959
14960   Store[x][y] = EL_EMPTY;
14961
14962   // this makes it possible to leave the removed element again
14963   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14964     Store[x][y] = element;
14965
14966   return TRUE;
14967 }
14968
14969 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14970 {
14971   int jx = player->jx, jy = player->jy;
14972   int x = jx + dx, y = jy + dy;
14973   int snap_direction = (dx == -1 ? MV_LEFT  :
14974                         dx == +1 ? MV_RIGHT :
14975                         dy == -1 ? MV_UP    :
14976                         dy == +1 ? MV_DOWN  : MV_NONE);
14977   boolean can_continue_snapping = (level.continuous_snapping &&
14978                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14979
14980   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14981     return FALSE;
14982
14983   if (!player->active || !IN_LEV_FIELD(x, y))
14984     return FALSE;
14985
14986   if (dx && dy)
14987     return FALSE;
14988
14989   if (!dx && !dy)
14990   {
14991     if (player->MovPos == 0)
14992       player->is_pushing = FALSE;
14993
14994     player->is_snapping = FALSE;
14995
14996     if (player->MovPos == 0)
14997     {
14998       player->is_moving = FALSE;
14999       player->is_digging = FALSE;
15000       player->is_collecting = FALSE;
15001     }
15002
15003     return FALSE;
15004   }
15005
15006   // prevent snapping with already pressed snap key when not allowed
15007   if (player->is_snapping && !can_continue_snapping)
15008     return FALSE;
15009
15010   player->MovDir = snap_direction;
15011
15012   if (player->MovPos == 0)
15013   {
15014     player->is_moving = FALSE;
15015     player->is_digging = FALSE;
15016     player->is_collecting = FALSE;
15017   }
15018
15019   player->is_dropping = FALSE;
15020   player->is_dropping_pressed = FALSE;
15021   player->drop_pressed_delay = 0;
15022
15023   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15024     return FALSE;
15025
15026   player->is_snapping = TRUE;
15027   player->is_active = TRUE;
15028
15029   if (player->MovPos == 0)
15030   {
15031     player->is_moving = FALSE;
15032     player->is_digging = FALSE;
15033     player->is_collecting = FALSE;
15034   }
15035
15036   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15037     TEST_DrawLevelField(player->last_jx, player->last_jy);
15038
15039   TEST_DrawLevelField(x, y);
15040
15041   return TRUE;
15042 }
15043
15044 static boolean DropElement(struct PlayerInfo *player)
15045 {
15046   int old_element, new_element;
15047   int dropx = player->jx, dropy = player->jy;
15048   int drop_direction = player->MovDir;
15049   int drop_side = drop_direction;
15050   int drop_element = get_next_dropped_element(player);
15051
15052   /* do not drop an element on top of another element; when holding drop key
15053      pressed without moving, dropped element must move away before the next
15054      element can be dropped (this is especially important if the next element
15055      is dynamite, which can be placed on background for historical reasons) */
15056   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15057     return MP_ACTION;
15058
15059   if (IS_THROWABLE(drop_element))
15060   {
15061     dropx += GET_DX_FROM_DIR(drop_direction);
15062     dropy += GET_DY_FROM_DIR(drop_direction);
15063
15064     if (!IN_LEV_FIELD(dropx, dropy))
15065       return FALSE;
15066   }
15067
15068   old_element = Tile[dropx][dropy];     // old element at dropping position
15069   new_element = drop_element;           // default: no change when dropping
15070
15071   // check if player is active, not moving and ready to drop
15072   if (!player->active || player->MovPos || player->drop_delay > 0)
15073     return FALSE;
15074
15075   // check if player has anything that can be dropped
15076   if (new_element == EL_UNDEFINED)
15077     return FALSE;
15078
15079   // only set if player has anything that can be dropped
15080   player->is_dropping_pressed = TRUE;
15081
15082   // check if drop key was pressed long enough for EM style dynamite
15083   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15084     return FALSE;
15085
15086   // check if anything can be dropped at the current position
15087   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15088     return FALSE;
15089
15090   // collected custom elements can only be dropped on empty fields
15091   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15092     return FALSE;
15093
15094   if (old_element != EL_EMPTY)
15095     Back[dropx][dropy] = old_element;   // store old element on this field
15096
15097   ResetGfxAnimation(dropx, dropy);
15098   ResetRandomAnimationValue(dropx, dropy);
15099
15100   if (player->inventory_size > 0 ||
15101       player->inventory_infinite_element != EL_UNDEFINED)
15102   {
15103     if (player->inventory_size > 0)
15104     {
15105       player->inventory_size--;
15106
15107       DrawGameDoorValues();
15108
15109       if (new_element == EL_DYNAMITE)
15110         new_element = EL_DYNAMITE_ACTIVE;
15111       else if (new_element == EL_EM_DYNAMITE)
15112         new_element = EL_EM_DYNAMITE_ACTIVE;
15113       else if (new_element == EL_SP_DISK_RED)
15114         new_element = EL_SP_DISK_RED_ACTIVE;
15115     }
15116
15117     Tile[dropx][dropy] = new_element;
15118
15119     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15120       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15121                           el2img(Tile[dropx][dropy]), 0);
15122
15123     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15124
15125     // needed if previous element just changed to "empty" in the last frame
15126     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15127
15128     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15129                                player->index_bit, drop_side);
15130     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15131                                         CE_PLAYER_DROPS_X,
15132                                         player->index_bit, drop_side);
15133
15134     TestIfElementTouchesCustomElement(dropx, dropy);
15135   }
15136   else          // player is dropping a dyna bomb
15137   {
15138     player->dynabombs_left--;
15139
15140     Tile[dropx][dropy] = new_element;
15141
15142     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15143       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15144                           el2img(Tile[dropx][dropy]), 0);
15145
15146     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15147   }
15148
15149   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15150     InitField_WithBug1(dropx, dropy, FALSE);
15151
15152   new_element = Tile[dropx][dropy];     // element might have changed
15153
15154   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15155       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15156   {
15157     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15158       MovDir[dropx][dropy] = drop_direction;
15159
15160     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15161
15162     // do not cause impact style collision by dropping elements that can fall
15163     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15164   }
15165
15166   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15167   player->is_dropping = TRUE;
15168
15169   player->drop_pressed_delay = 0;
15170   player->is_dropping_pressed = FALSE;
15171
15172   player->drop_x = dropx;
15173   player->drop_y = dropy;
15174
15175   return TRUE;
15176 }
15177
15178 // ----------------------------------------------------------------------------
15179 // game sound playing functions
15180 // ----------------------------------------------------------------------------
15181
15182 static int *loop_sound_frame = NULL;
15183 static int *loop_sound_volume = NULL;
15184
15185 void InitPlayLevelSound(void)
15186 {
15187   int num_sounds = getSoundListSize();
15188
15189   checked_free(loop_sound_frame);
15190   checked_free(loop_sound_volume);
15191
15192   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15193   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15194 }
15195
15196 static void PlayLevelSound(int x, int y, int nr)
15197 {
15198   int sx = SCREENX(x), sy = SCREENY(y);
15199   int volume, stereo_position;
15200   int max_distance = 8;
15201   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15202
15203   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15204       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15205     return;
15206
15207   if (!IN_LEV_FIELD(x, y) ||
15208       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15209       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15210     return;
15211
15212   volume = SOUND_MAX_VOLUME;
15213
15214   if (!IN_SCR_FIELD(sx, sy))
15215   {
15216     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15217     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15218
15219     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15220   }
15221
15222   stereo_position = (SOUND_MAX_LEFT +
15223                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15224                      (SCR_FIELDX + 2 * max_distance));
15225
15226   if (IS_LOOP_SOUND(nr))
15227   {
15228     /* This assures that quieter loop sounds do not overwrite louder ones,
15229        while restarting sound volume comparison with each new game frame. */
15230
15231     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15232       return;
15233
15234     loop_sound_volume[nr] = volume;
15235     loop_sound_frame[nr] = FrameCounter;
15236   }
15237
15238   PlaySoundExt(nr, volume, stereo_position, type);
15239 }
15240
15241 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15242 {
15243   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15244                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15245                  y < LEVELY(BY1) ? LEVELY(BY1) :
15246                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15247                  sound_action);
15248 }
15249
15250 static void PlayLevelSoundAction(int x, int y, int action)
15251 {
15252   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15253 }
15254
15255 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15256 {
15257   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15258
15259   if (sound_effect != SND_UNDEFINED)
15260     PlayLevelSound(x, y, sound_effect);
15261 }
15262
15263 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15264                                               int action)
15265 {
15266   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15267
15268   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15269     PlayLevelSound(x, y, sound_effect);
15270 }
15271
15272 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15273 {
15274   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15275
15276   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15277     PlayLevelSound(x, y, sound_effect);
15278 }
15279
15280 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15281 {
15282   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15283
15284   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15285     StopSound(sound_effect);
15286 }
15287
15288 static int getLevelMusicNr(void)
15289 {
15290   if (levelset.music[level_nr] != MUS_UNDEFINED)
15291     return levelset.music[level_nr];            // from config file
15292   else
15293     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15294 }
15295
15296 static void FadeLevelSounds(void)
15297 {
15298   FadeSounds();
15299 }
15300
15301 static void FadeLevelMusic(void)
15302 {
15303   int music_nr = getLevelMusicNr();
15304   char *curr_music = getCurrentlyPlayingMusicFilename();
15305   char *next_music = getMusicInfoEntryFilename(music_nr);
15306
15307   if (!strEqual(curr_music, next_music))
15308     FadeMusic();
15309 }
15310
15311 void FadeLevelSoundsAndMusic(void)
15312 {
15313   FadeLevelSounds();
15314   FadeLevelMusic();
15315 }
15316
15317 static void PlayLevelMusic(void)
15318 {
15319   int music_nr = getLevelMusicNr();
15320   char *curr_music = getCurrentlyPlayingMusicFilename();
15321   char *next_music = getMusicInfoEntryFilename(music_nr);
15322
15323   if (!strEqual(curr_music, next_music))
15324     PlayMusicLoop(music_nr);
15325 }
15326
15327 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15328 {
15329   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15330   int offset = 0;
15331   int x = xx - offset;
15332   int y = yy - offset;
15333
15334   switch (sample)
15335   {
15336     case SOUND_blank:
15337       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15338       break;
15339
15340     case SOUND_roll:
15341       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15342       break;
15343
15344     case SOUND_stone:
15345       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15346       break;
15347
15348     case SOUND_nut:
15349       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15350       break;
15351
15352     case SOUND_crack:
15353       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15354       break;
15355
15356     case SOUND_bug:
15357       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15358       break;
15359
15360     case SOUND_tank:
15361       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15362       break;
15363
15364     case SOUND_android_clone:
15365       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15366       break;
15367
15368     case SOUND_android_move:
15369       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15370       break;
15371
15372     case SOUND_spring:
15373       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15374       break;
15375
15376     case SOUND_slurp:
15377       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15378       break;
15379
15380     case SOUND_eater:
15381       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15382       break;
15383
15384     case SOUND_eater_eat:
15385       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15386       break;
15387
15388     case SOUND_alien:
15389       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15390       break;
15391
15392     case SOUND_collect:
15393       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15394       break;
15395
15396     case SOUND_diamond:
15397       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15398       break;
15399
15400     case SOUND_squash:
15401       // !!! CHECK THIS !!!
15402 #if 1
15403       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15404 #else
15405       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15406 #endif
15407       break;
15408
15409     case SOUND_wonderfall:
15410       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15411       break;
15412
15413     case SOUND_drip:
15414       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15415       break;
15416
15417     case SOUND_push:
15418       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15419       break;
15420
15421     case SOUND_dirt:
15422       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15423       break;
15424
15425     case SOUND_acid:
15426       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15427       break;
15428
15429     case SOUND_ball:
15430       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15431       break;
15432
15433     case SOUND_slide:
15434       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15435       break;
15436
15437     case SOUND_wonder:
15438       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15439       break;
15440
15441     case SOUND_door:
15442       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15443       break;
15444
15445     case SOUND_exit_open:
15446       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15447       break;
15448
15449     case SOUND_exit_leave:
15450       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15451       break;
15452
15453     case SOUND_dynamite:
15454       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15455       break;
15456
15457     case SOUND_tick:
15458       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15459       break;
15460
15461     case SOUND_press:
15462       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15463       break;
15464
15465     case SOUND_wheel:
15466       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15467       break;
15468
15469     case SOUND_boom:
15470       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15471       break;
15472
15473     case SOUND_die:
15474       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15475       break;
15476
15477     case SOUND_time:
15478       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15479       break;
15480
15481     default:
15482       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15483       break;
15484   }
15485 }
15486
15487 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15488 {
15489   int element = map_element_SP_to_RND(element_sp);
15490   int action = map_action_SP_to_RND(action_sp);
15491   int offset = (setup.sp_show_border_elements ? 0 : 1);
15492   int x = xx - offset;
15493   int y = yy - offset;
15494
15495   PlayLevelSoundElementAction(x, y, element, action);
15496 }
15497
15498 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15499 {
15500   int element = map_element_MM_to_RND(element_mm);
15501   int action = map_action_MM_to_RND(action_mm);
15502   int offset = 0;
15503   int x = xx - offset;
15504   int y = yy - offset;
15505
15506   if (!IS_MM_ELEMENT(element))
15507     element = EL_MM_DEFAULT;
15508
15509   PlayLevelSoundElementAction(x, y, element, action);
15510 }
15511
15512 void PlaySound_MM(int sound_mm)
15513 {
15514   int sound = map_sound_MM_to_RND(sound_mm);
15515
15516   if (sound == SND_UNDEFINED)
15517     return;
15518
15519   PlaySound(sound);
15520 }
15521
15522 void PlaySoundLoop_MM(int sound_mm)
15523 {
15524   int sound = map_sound_MM_to_RND(sound_mm);
15525
15526   if (sound == SND_UNDEFINED)
15527     return;
15528
15529   PlaySoundLoop(sound);
15530 }
15531
15532 void StopSound_MM(int sound_mm)
15533 {
15534   int sound = map_sound_MM_to_RND(sound_mm);
15535
15536   if (sound == SND_UNDEFINED)
15537     return;
15538
15539   StopSound(sound);
15540 }
15541
15542 void RaiseScore(int value)
15543 {
15544   game.score += value;
15545
15546   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15547
15548   DisplayGameControlValues();
15549 }
15550
15551 void RaiseScoreElement(int element)
15552 {
15553   switch (element)
15554   {
15555     case EL_EMERALD:
15556     case EL_BD_DIAMOND:
15557     case EL_EMERALD_YELLOW:
15558     case EL_EMERALD_RED:
15559     case EL_EMERALD_PURPLE:
15560     case EL_SP_INFOTRON:
15561       RaiseScore(level.score[SC_EMERALD]);
15562       break;
15563     case EL_DIAMOND:
15564       RaiseScore(level.score[SC_DIAMOND]);
15565       break;
15566     case EL_CRYSTAL:
15567       RaiseScore(level.score[SC_CRYSTAL]);
15568       break;
15569     case EL_PEARL:
15570       RaiseScore(level.score[SC_PEARL]);
15571       break;
15572     case EL_BUG:
15573     case EL_BD_BUTTERFLY:
15574     case EL_SP_ELECTRON:
15575       RaiseScore(level.score[SC_BUG]);
15576       break;
15577     case EL_SPACESHIP:
15578     case EL_BD_FIREFLY:
15579     case EL_SP_SNIKSNAK:
15580       RaiseScore(level.score[SC_SPACESHIP]);
15581       break;
15582     case EL_YAMYAM:
15583     case EL_DARK_YAMYAM:
15584       RaiseScore(level.score[SC_YAMYAM]);
15585       break;
15586     case EL_ROBOT:
15587       RaiseScore(level.score[SC_ROBOT]);
15588       break;
15589     case EL_PACMAN:
15590       RaiseScore(level.score[SC_PACMAN]);
15591       break;
15592     case EL_NUT:
15593       RaiseScore(level.score[SC_NUT]);
15594       break;
15595     case EL_DYNAMITE:
15596     case EL_EM_DYNAMITE:
15597     case EL_SP_DISK_RED:
15598     case EL_DYNABOMB_INCREASE_NUMBER:
15599     case EL_DYNABOMB_INCREASE_SIZE:
15600     case EL_DYNABOMB_INCREASE_POWER:
15601       RaiseScore(level.score[SC_DYNAMITE]);
15602       break;
15603     case EL_SHIELD_NORMAL:
15604     case EL_SHIELD_DEADLY:
15605       RaiseScore(level.score[SC_SHIELD]);
15606       break;
15607     case EL_EXTRA_TIME:
15608       RaiseScore(level.extra_time_score);
15609       break;
15610     case EL_KEY_1:
15611     case EL_KEY_2:
15612     case EL_KEY_3:
15613     case EL_KEY_4:
15614     case EL_EM_KEY_1:
15615     case EL_EM_KEY_2:
15616     case EL_EM_KEY_3:
15617     case EL_EM_KEY_4:
15618     case EL_EMC_KEY_5:
15619     case EL_EMC_KEY_6:
15620     case EL_EMC_KEY_7:
15621     case EL_EMC_KEY_8:
15622     case EL_DC_KEY_WHITE:
15623       RaiseScore(level.score[SC_KEY]);
15624       break;
15625     default:
15626       RaiseScore(element_info[element].collect_score);
15627       break;
15628   }
15629 }
15630
15631 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15632 {
15633   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15634   {
15635     if (!quick_quit)
15636     {
15637       // prevent short reactivation of overlay buttons while closing door
15638       SetOverlayActive(FALSE);
15639
15640       // door may still be open due to skipped or envelope style request
15641       CloseDoor(DOOR_CLOSE_1);
15642     }
15643
15644     if (network.enabled)
15645       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15646     else
15647     {
15648       if (quick_quit)
15649         FadeSkipNextFadeIn();
15650
15651       SetGameStatus(GAME_MODE_MAIN);
15652
15653       DrawMainMenu();
15654     }
15655   }
15656   else          // continue playing the game
15657   {
15658     if (tape.playing && tape.deactivate_display)
15659       TapeDeactivateDisplayOff(TRUE);
15660
15661     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15662
15663     if (tape.playing && tape.deactivate_display)
15664       TapeDeactivateDisplayOn();
15665   }
15666 }
15667
15668 void RequestQuitGame(boolean escape_key_pressed)
15669 {
15670   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15671   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15672                         level_editor_test_game);
15673   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15674                           quick_quit);
15675
15676   RequestQuitGameExt(skip_request, quick_quit,
15677                      "Do you really want to quit the game?");
15678 }
15679
15680 void RequestRestartGame(char *message)
15681 {
15682   game.restart_game_message = NULL;
15683
15684   boolean has_started_game = hasStartedNetworkGame();
15685   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15686
15687   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15688   {
15689     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15690   }
15691   else
15692   {
15693     // needed in case of envelope request to close game panel
15694     CloseDoor(DOOR_CLOSE_1);
15695
15696     SetGameStatus(GAME_MODE_MAIN);
15697
15698     DrawMainMenu();
15699   }
15700 }
15701
15702 void CheckGameOver(void)
15703 {
15704   static boolean last_game_over = FALSE;
15705   static int game_over_delay = 0;
15706   int game_over_delay_value = 50;
15707   boolean game_over = checkGameFailed();
15708
15709   // do not handle game over if request dialog is already active
15710   if (game.request_active)
15711     return;
15712
15713   // do not ask to play again if game was never actually played
15714   if (!game.GamePlayed)
15715     return;
15716
15717   if (!game_over)
15718   {
15719     last_game_over = FALSE;
15720     game_over_delay = game_over_delay_value;
15721
15722     return;
15723   }
15724
15725   if (game_over_delay > 0)
15726   {
15727     game_over_delay--;
15728
15729     return;
15730   }
15731
15732   if (last_game_over != game_over)
15733     game.restart_game_message = (hasStartedNetworkGame() ?
15734                                  "Game over! Play it again?" :
15735                                  "Game over!");
15736
15737   last_game_over = game_over;
15738 }
15739
15740 boolean checkGameSolved(void)
15741 {
15742   // set for all game engines if level was solved
15743   return game.LevelSolved_GameEnd;
15744 }
15745
15746 boolean checkGameFailed(void)
15747 {
15748   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15749     return (game_em.game_over && !game_em.level_solved);
15750   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15751     return (game_sp.game_over && !game_sp.level_solved);
15752   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15753     return (game_mm.game_over && !game_mm.level_solved);
15754   else                          // GAME_ENGINE_TYPE_RND
15755     return (game.GameOver && !game.LevelSolved);
15756 }
15757
15758 boolean checkGameEnded(void)
15759 {
15760   return (checkGameSolved() || checkGameFailed());
15761 }
15762
15763
15764 // ----------------------------------------------------------------------------
15765 // random generator functions
15766 // ----------------------------------------------------------------------------
15767
15768 unsigned int InitEngineRandom_RND(int seed)
15769 {
15770   game.num_random_calls = 0;
15771
15772   return InitEngineRandom(seed);
15773 }
15774
15775 unsigned int RND(int max)
15776 {
15777   if (max > 0)
15778   {
15779     game.num_random_calls++;
15780
15781     return GetEngineRandom(max);
15782   }
15783
15784   return 0;
15785 }
15786
15787
15788 // ----------------------------------------------------------------------------
15789 // game engine snapshot handling functions
15790 // ----------------------------------------------------------------------------
15791
15792 struct EngineSnapshotInfo
15793 {
15794   // runtime values for custom element collect score
15795   int collect_score[NUM_CUSTOM_ELEMENTS];
15796
15797   // runtime values for group element choice position
15798   int choice_pos[NUM_GROUP_ELEMENTS];
15799
15800   // runtime values for belt position animations
15801   int belt_graphic[4][NUM_BELT_PARTS];
15802   int belt_anim_mode[4][NUM_BELT_PARTS];
15803 };
15804
15805 static struct EngineSnapshotInfo engine_snapshot_rnd;
15806 static char *snapshot_level_identifier = NULL;
15807 static int snapshot_level_nr = -1;
15808
15809 static void SaveEngineSnapshotValues_RND(void)
15810 {
15811   static int belt_base_active_element[4] =
15812   {
15813     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15814     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15815     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15816     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15817   };
15818   int i, j;
15819
15820   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15821   {
15822     int element = EL_CUSTOM_START + i;
15823
15824     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15825   }
15826
15827   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15828   {
15829     int element = EL_GROUP_START + i;
15830
15831     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15832   }
15833
15834   for (i = 0; i < 4; i++)
15835   {
15836     for (j = 0; j < NUM_BELT_PARTS; j++)
15837     {
15838       int element = belt_base_active_element[i] + j;
15839       int graphic = el2img(element);
15840       int anim_mode = graphic_info[graphic].anim_mode;
15841
15842       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15843       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15844     }
15845   }
15846 }
15847
15848 static void LoadEngineSnapshotValues_RND(void)
15849 {
15850   unsigned int num_random_calls = game.num_random_calls;
15851   int i, j;
15852
15853   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15854   {
15855     int element = EL_CUSTOM_START + i;
15856
15857     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15858   }
15859
15860   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15861   {
15862     int element = EL_GROUP_START + i;
15863
15864     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15865   }
15866
15867   for (i = 0; i < 4; i++)
15868   {
15869     for (j = 0; j < NUM_BELT_PARTS; j++)
15870     {
15871       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15872       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15873
15874       graphic_info[graphic].anim_mode = anim_mode;
15875     }
15876   }
15877
15878   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15879   {
15880     InitRND(tape.random_seed);
15881     for (i = 0; i < num_random_calls; i++)
15882       RND(1);
15883   }
15884
15885   if (game.num_random_calls != num_random_calls)
15886   {
15887     Error("number of random calls out of sync");
15888     Error("number of random calls should be %d", num_random_calls);
15889     Error("number of random calls is %d", game.num_random_calls);
15890
15891     Fail("this should not happen -- please debug");
15892   }
15893 }
15894
15895 void FreeEngineSnapshotSingle(void)
15896 {
15897   FreeSnapshotSingle();
15898
15899   setString(&snapshot_level_identifier, NULL);
15900   snapshot_level_nr = -1;
15901 }
15902
15903 void FreeEngineSnapshotList(void)
15904 {
15905   FreeSnapshotList();
15906 }
15907
15908 static ListNode *SaveEngineSnapshotBuffers(void)
15909 {
15910   ListNode *buffers = NULL;
15911
15912   // copy some special values to a structure better suited for the snapshot
15913
15914   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15915     SaveEngineSnapshotValues_RND();
15916   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15917     SaveEngineSnapshotValues_EM();
15918   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15919     SaveEngineSnapshotValues_SP(&buffers);
15920   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15921     SaveEngineSnapshotValues_MM(&buffers);
15922
15923   // save values stored in special snapshot structure
15924
15925   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15926     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15927   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15928     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15929   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15930     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15931   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15932     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15933
15934   // save further RND engine values
15935
15936   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15937   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15938   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15939
15940   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15941   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15942   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15944   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15945
15946   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15948   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15949
15950   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15951
15952   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15953   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15954
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15956   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15959   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15960   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15970   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15971   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15973
15974   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15976
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15979   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15980
15981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15983
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15986   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15990
15991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15993
15994 #if 0
15995   ListNode *node = engine_snapshot_list_rnd;
15996   int num_bytes = 0;
15997
15998   while (node != NULL)
15999   {
16000     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16001
16002     node = node->next;
16003   }
16004
16005   Debug("game:playing:SaveEngineSnapshotBuffers",
16006         "size of engine snapshot: %d bytes", num_bytes);
16007 #endif
16008
16009   return buffers;
16010 }
16011
16012 void SaveEngineSnapshotSingle(void)
16013 {
16014   ListNode *buffers = SaveEngineSnapshotBuffers();
16015
16016   // finally save all snapshot buffers to single snapshot
16017   SaveSnapshotSingle(buffers);
16018
16019   // save level identification information
16020   setString(&snapshot_level_identifier, leveldir_current->identifier);
16021   snapshot_level_nr = level_nr;
16022 }
16023
16024 boolean CheckSaveEngineSnapshotToList(void)
16025 {
16026   boolean save_snapshot =
16027     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16028      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16029       game.snapshot.changed_action) ||
16030      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16031       game.snapshot.collected_item));
16032
16033   game.snapshot.changed_action = FALSE;
16034   game.snapshot.collected_item = FALSE;
16035   game.snapshot.save_snapshot = save_snapshot;
16036
16037   return save_snapshot;
16038 }
16039
16040 void SaveEngineSnapshotToList(void)
16041 {
16042   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16043       tape.quick_resume)
16044     return;
16045
16046   ListNode *buffers = SaveEngineSnapshotBuffers();
16047
16048   // finally save all snapshot buffers to snapshot list
16049   SaveSnapshotToList(buffers);
16050 }
16051
16052 void SaveEngineSnapshotToListInitial(void)
16053 {
16054   FreeEngineSnapshotList();
16055
16056   SaveEngineSnapshotToList();
16057 }
16058
16059 static void LoadEngineSnapshotValues(void)
16060 {
16061   // restore special values from snapshot structure
16062
16063   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16064     LoadEngineSnapshotValues_RND();
16065   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16066     LoadEngineSnapshotValues_EM();
16067   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16068     LoadEngineSnapshotValues_SP();
16069   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16070     LoadEngineSnapshotValues_MM();
16071 }
16072
16073 void LoadEngineSnapshotSingle(void)
16074 {
16075   LoadSnapshotSingle();
16076
16077   LoadEngineSnapshotValues();
16078 }
16079
16080 static void LoadEngineSnapshot_Undo(int steps)
16081 {
16082   LoadSnapshotFromList_Older(steps);
16083
16084   LoadEngineSnapshotValues();
16085 }
16086
16087 static void LoadEngineSnapshot_Redo(int steps)
16088 {
16089   LoadSnapshotFromList_Newer(steps);
16090
16091   LoadEngineSnapshotValues();
16092 }
16093
16094 boolean CheckEngineSnapshotSingle(void)
16095 {
16096   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16097           snapshot_level_nr == level_nr);
16098 }
16099
16100 boolean CheckEngineSnapshotList(void)
16101 {
16102   return CheckSnapshotList();
16103 }
16104
16105
16106 // ---------- new game button stuff -------------------------------------------
16107
16108 static struct
16109 {
16110   int graphic;
16111   struct XY *pos;
16112   int gadget_id;
16113   boolean *setup_value;
16114   boolean allowed_on_tape;
16115   boolean is_touch_button;
16116   char *infotext;
16117 } gamebutton_info[NUM_GAME_BUTTONS] =
16118 {
16119   {
16120     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16121     GAME_CTRL_ID_STOP,                          NULL,
16122     TRUE, FALSE,                                "stop game"
16123   },
16124   {
16125     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16126     GAME_CTRL_ID_PAUSE,                         NULL,
16127     TRUE, FALSE,                                "pause game"
16128   },
16129   {
16130     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16131     GAME_CTRL_ID_PLAY,                          NULL,
16132     TRUE, FALSE,                                "play game"
16133   },
16134   {
16135     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16136     GAME_CTRL_ID_UNDO,                          NULL,
16137     TRUE, FALSE,                                "undo step"
16138   },
16139   {
16140     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16141     GAME_CTRL_ID_REDO,                          NULL,
16142     TRUE, FALSE,                                "redo step"
16143   },
16144   {
16145     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16146     GAME_CTRL_ID_SAVE,                          NULL,
16147     TRUE, FALSE,                                "save game"
16148   },
16149   {
16150     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16151     GAME_CTRL_ID_PAUSE2,                        NULL,
16152     TRUE, FALSE,                                "pause game"
16153   },
16154   {
16155     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16156     GAME_CTRL_ID_LOAD,                          NULL,
16157     TRUE, FALSE,                                "load game"
16158   },
16159   {
16160     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16161     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16162     FALSE, FALSE,                               "stop game"
16163   },
16164   {
16165     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16166     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16167     FALSE, FALSE,                               "pause game"
16168   },
16169   {
16170     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16171     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16172     FALSE, FALSE,                               "play game"
16173   },
16174   {
16175     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16176     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16177     FALSE, TRUE,                                "stop game"
16178   },
16179   {
16180     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16181     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16182     FALSE, TRUE,                                "pause game"
16183   },
16184   {
16185     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16186     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16187     TRUE, FALSE,                                "background music on/off"
16188   },
16189   {
16190     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16191     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16192     TRUE, FALSE,                                "sound loops on/off"
16193   },
16194   {
16195     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16196     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16197     TRUE, FALSE,                                "normal sounds on/off"
16198   },
16199   {
16200     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16201     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16202     FALSE, FALSE,                               "background music on/off"
16203   },
16204   {
16205     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16206     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16207     FALSE, FALSE,                               "sound loops on/off"
16208   },
16209   {
16210     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16211     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16212     FALSE, FALSE,                               "normal sounds on/off"
16213   }
16214 };
16215
16216 void CreateGameButtons(void)
16217 {
16218   int i;
16219
16220   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16221   {
16222     int graphic = gamebutton_info[i].graphic;
16223     struct GraphicInfo *gfx = &graphic_info[graphic];
16224     struct XY *pos = gamebutton_info[i].pos;
16225     struct GadgetInfo *gi;
16226     int button_type;
16227     boolean checked;
16228     unsigned int event_mask;
16229     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16230     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16231     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16232     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16233     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16234     int gd_x   = gfx->src_x;
16235     int gd_y   = gfx->src_y;
16236     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16237     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16238     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16239     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16240     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16241     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16242     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16243     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16244     int id = i;
16245
16246     if (gfx->bitmap == NULL)
16247     {
16248       game_gadget[id] = NULL;
16249
16250       continue;
16251     }
16252
16253     if (id == GAME_CTRL_ID_STOP ||
16254         id == GAME_CTRL_ID_PANEL_STOP ||
16255         id == GAME_CTRL_ID_TOUCH_STOP ||
16256         id == GAME_CTRL_ID_PLAY ||
16257         id == GAME_CTRL_ID_PANEL_PLAY ||
16258         id == GAME_CTRL_ID_SAVE ||
16259         id == GAME_CTRL_ID_LOAD)
16260     {
16261       button_type = GD_TYPE_NORMAL_BUTTON;
16262       checked = FALSE;
16263       event_mask = GD_EVENT_RELEASED;
16264     }
16265     else if (id == GAME_CTRL_ID_UNDO ||
16266              id == GAME_CTRL_ID_REDO)
16267     {
16268       button_type = GD_TYPE_NORMAL_BUTTON;
16269       checked = FALSE;
16270       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16271     }
16272     else
16273     {
16274       button_type = GD_TYPE_CHECK_BUTTON;
16275       checked = (gamebutton_info[i].setup_value != NULL ?
16276                  *gamebutton_info[i].setup_value : FALSE);
16277       event_mask = GD_EVENT_PRESSED;
16278     }
16279
16280     gi = CreateGadget(GDI_CUSTOM_ID, id,
16281                       GDI_IMAGE_ID, graphic,
16282                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16283                       GDI_X, base_x + x,
16284                       GDI_Y, base_y + y,
16285                       GDI_WIDTH, gfx->width,
16286                       GDI_HEIGHT, gfx->height,
16287                       GDI_TYPE, button_type,
16288                       GDI_STATE, GD_BUTTON_UNPRESSED,
16289                       GDI_CHECKED, checked,
16290                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16291                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16292                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16293                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16294                       GDI_DIRECT_DRAW, FALSE,
16295                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16296                       GDI_EVENT_MASK, event_mask,
16297                       GDI_CALLBACK_ACTION, HandleGameButtons,
16298                       GDI_END);
16299
16300     if (gi == NULL)
16301       Fail("cannot create gadget");
16302
16303     game_gadget[id] = gi;
16304   }
16305 }
16306
16307 void FreeGameButtons(void)
16308 {
16309   int i;
16310
16311   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16312     FreeGadget(game_gadget[i]);
16313 }
16314
16315 static void UnmapGameButtonsAtSamePosition(int id)
16316 {
16317   int i;
16318
16319   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16320     if (i != id &&
16321         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16322         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16323       UnmapGadget(game_gadget[i]);
16324 }
16325
16326 static void UnmapGameButtonsAtSamePosition_All(void)
16327 {
16328   if (setup.show_load_save_buttons)
16329   {
16330     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16331     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16332     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16333   }
16334   else if (setup.show_undo_redo_buttons)
16335   {
16336     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16337     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16338     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16339   }
16340   else
16341   {
16342     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16343     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16344     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16345
16346     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16347     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16348     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16349   }
16350 }
16351
16352 void MapLoadSaveButtons(void)
16353 {
16354   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16355   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16356
16357   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16358   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16359 }
16360
16361 void MapUndoRedoButtons(void)
16362 {
16363   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16364   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16365
16366   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16367   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16368 }
16369
16370 void ModifyPauseButtons(void)
16371 {
16372   static int ids[] =
16373   {
16374     GAME_CTRL_ID_PAUSE,
16375     GAME_CTRL_ID_PAUSE2,
16376     GAME_CTRL_ID_PANEL_PAUSE,
16377     GAME_CTRL_ID_TOUCH_PAUSE,
16378     -1
16379   };
16380   int i;
16381
16382   for (i = 0; ids[i] > -1; i++)
16383     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16384 }
16385
16386 static void MapGameButtonsExt(boolean on_tape)
16387 {
16388   int i;
16389
16390   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16391     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16392       MapGadget(game_gadget[i]);
16393
16394   UnmapGameButtonsAtSamePosition_All();
16395
16396   RedrawGameButtons();
16397 }
16398
16399 static void UnmapGameButtonsExt(boolean on_tape)
16400 {
16401   int i;
16402
16403   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16404     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16405       UnmapGadget(game_gadget[i]);
16406 }
16407
16408 static void RedrawGameButtonsExt(boolean on_tape)
16409 {
16410   int i;
16411
16412   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16413     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16414       RedrawGadget(game_gadget[i]);
16415 }
16416
16417 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16418 {
16419   if (gi == NULL)
16420     return;
16421
16422   gi->checked = state;
16423 }
16424
16425 static void RedrawSoundButtonGadget(int id)
16426 {
16427   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16428              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16429              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16430              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16431              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16432              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16433              id);
16434
16435   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16436   RedrawGadget(game_gadget[id2]);
16437 }
16438
16439 void MapGameButtons(void)
16440 {
16441   MapGameButtonsExt(FALSE);
16442 }
16443
16444 void UnmapGameButtons(void)
16445 {
16446   UnmapGameButtonsExt(FALSE);
16447 }
16448
16449 void RedrawGameButtons(void)
16450 {
16451   RedrawGameButtonsExt(FALSE);
16452 }
16453
16454 void MapGameButtonsOnTape(void)
16455 {
16456   MapGameButtonsExt(TRUE);
16457 }
16458
16459 void UnmapGameButtonsOnTape(void)
16460 {
16461   UnmapGameButtonsExt(TRUE);
16462 }
16463
16464 void RedrawGameButtonsOnTape(void)
16465 {
16466   RedrawGameButtonsExt(TRUE);
16467 }
16468
16469 static void GameUndoRedoExt(void)
16470 {
16471   ClearPlayerAction();
16472
16473   tape.pausing = TRUE;
16474
16475   RedrawPlayfield();
16476   UpdateAndDisplayGameControlValues();
16477
16478   DrawCompleteVideoDisplay();
16479   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16480   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16481   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16482
16483   ModifyPauseButtons();
16484
16485   BackToFront();
16486 }
16487
16488 static void GameUndo(int steps)
16489 {
16490   if (!CheckEngineSnapshotList())
16491     return;
16492
16493   int tape_property_bits = tape.property_bits;
16494
16495   LoadEngineSnapshot_Undo(steps);
16496
16497   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16498
16499   GameUndoRedoExt();
16500 }
16501
16502 static void GameRedo(int steps)
16503 {
16504   if (!CheckEngineSnapshotList())
16505     return;
16506
16507   int tape_property_bits = tape.property_bits;
16508
16509   LoadEngineSnapshot_Redo(steps);
16510
16511   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16512
16513   GameUndoRedoExt();
16514 }
16515
16516 static void HandleGameButtonsExt(int id, int button)
16517 {
16518   static boolean game_undo_executed = FALSE;
16519   int steps = BUTTON_STEPSIZE(button);
16520   boolean handle_game_buttons =
16521     (game_status == GAME_MODE_PLAYING ||
16522      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16523
16524   if (!handle_game_buttons)
16525     return;
16526
16527   switch (id)
16528   {
16529     case GAME_CTRL_ID_STOP:
16530     case GAME_CTRL_ID_PANEL_STOP:
16531     case GAME_CTRL_ID_TOUCH_STOP:
16532       if (game_status == GAME_MODE_MAIN)
16533         break;
16534
16535       if (tape.playing)
16536         TapeStop();
16537       else
16538         RequestQuitGame(FALSE);
16539
16540       break;
16541
16542     case GAME_CTRL_ID_PAUSE:
16543     case GAME_CTRL_ID_PAUSE2:
16544     case GAME_CTRL_ID_PANEL_PAUSE:
16545     case GAME_CTRL_ID_TOUCH_PAUSE:
16546       if (network.enabled && game_status == GAME_MODE_PLAYING)
16547       {
16548         if (tape.pausing)
16549           SendToServer_ContinuePlaying();
16550         else
16551           SendToServer_PausePlaying();
16552       }
16553       else
16554         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16555
16556       game_undo_executed = FALSE;
16557
16558       break;
16559
16560     case GAME_CTRL_ID_PLAY:
16561     case GAME_CTRL_ID_PANEL_PLAY:
16562       if (game_status == GAME_MODE_MAIN)
16563       {
16564         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16565       }
16566       else if (tape.pausing)
16567       {
16568         if (network.enabled)
16569           SendToServer_ContinuePlaying();
16570         else
16571           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16572       }
16573       break;
16574
16575     case GAME_CTRL_ID_UNDO:
16576       // Important: When using "save snapshot when collecting an item" mode,
16577       // load last (current) snapshot for first "undo" after pressing "pause"
16578       // (else the last-but-one snapshot would be loaded, because the snapshot
16579       // pointer already points to the last snapshot when pressing "pause",
16580       // which is fine for "every step/move" mode, but not for "every collect")
16581       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16582           !game_undo_executed)
16583         steps--;
16584
16585       game_undo_executed = TRUE;
16586
16587       GameUndo(steps);
16588       break;
16589
16590     case GAME_CTRL_ID_REDO:
16591       GameRedo(steps);
16592       break;
16593
16594     case GAME_CTRL_ID_SAVE:
16595       TapeQuickSave();
16596       break;
16597
16598     case GAME_CTRL_ID_LOAD:
16599       TapeQuickLoad();
16600       break;
16601
16602     case SOUND_CTRL_ID_MUSIC:
16603     case SOUND_CTRL_ID_PANEL_MUSIC:
16604       if (setup.sound_music)
16605       { 
16606         setup.sound_music = FALSE;
16607
16608         FadeMusic();
16609       }
16610       else if (audio.music_available)
16611       { 
16612         setup.sound = setup.sound_music = TRUE;
16613
16614         SetAudioMode(setup.sound);
16615
16616         if (game_status == GAME_MODE_PLAYING)
16617           PlayLevelMusic();
16618       }
16619
16620       RedrawSoundButtonGadget(id);
16621
16622       break;
16623
16624     case SOUND_CTRL_ID_LOOPS:
16625     case SOUND_CTRL_ID_PANEL_LOOPS:
16626       if (setup.sound_loops)
16627         setup.sound_loops = FALSE;
16628       else if (audio.loops_available)
16629       {
16630         setup.sound = setup.sound_loops = TRUE;
16631
16632         SetAudioMode(setup.sound);
16633       }
16634
16635       RedrawSoundButtonGadget(id);
16636
16637       break;
16638
16639     case SOUND_CTRL_ID_SIMPLE:
16640     case SOUND_CTRL_ID_PANEL_SIMPLE:
16641       if (setup.sound_simple)
16642         setup.sound_simple = FALSE;
16643       else if (audio.sound_available)
16644       {
16645         setup.sound = setup.sound_simple = TRUE;
16646
16647         SetAudioMode(setup.sound);
16648       }
16649
16650       RedrawSoundButtonGadget(id);
16651
16652       break;
16653
16654     default:
16655       break;
16656   }
16657 }
16658
16659 static void HandleGameButtons(struct GadgetInfo *gi)
16660 {
16661   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16662 }
16663
16664 void HandleSoundButtonKeys(Key key)
16665 {
16666   if (key == setup.shortcut.sound_simple)
16667     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16668   else if (key == setup.shortcut.sound_loops)
16669     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16670   else if (key == setup.shortcut.sound_music)
16671     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16672 }