added showing mouse cursor for levels with mouse click custom elements
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1558
1559 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1560 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1561 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1562                                  IS_JUST_CHANGING(x, y))
1563
1564 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1565
1566 // static variables for playfield scan mode (scanning forward or backward)
1567 static int playfield_scan_start_x = 0;
1568 static int playfield_scan_start_y = 0;
1569 static int playfield_scan_delta_x = 1;
1570 static int playfield_scan_delta_y = 1;
1571
1572 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1573                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1574                                      (y) += playfield_scan_delta_y)     \
1575                                 for ((x) = playfield_scan_start_x;      \
1576                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1577                                      (x) += playfield_scan_delta_x)
1578
1579 #ifdef DEBUG
1580 void DEBUG_SetMaximumDynamite(void)
1581 {
1582   int i;
1583
1584   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1585     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1586       local_player->inventory_element[local_player->inventory_size++] =
1587         EL_DYNAMITE;
1588 }
1589 #endif
1590
1591 static void InitPlayfieldScanModeVars(void)
1592 {
1593   if (game.use_reverse_scan_direction)
1594   {
1595     playfield_scan_start_x = lev_fieldx - 1;
1596     playfield_scan_start_y = lev_fieldy - 1;
1597
1598     playfield_scan_delta_x = -1;
1599     playfield_scan_delta_y = -1;
1600   }
1601   else
1602   {
1603     playfield_scan_start_x = 0;
1604     playfield_scan_start_y = 0;
1605
1606     playfield_scan_delta_x = 1;
1607     playfield_scan_delta_y = 1;
1608   }
1609 }
1610
1611 static void InitPlayfieldScanMode(int mode)
1612 {
1613   game.use_reverse_scan_direction =
1614     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1615
1616   InitPlayfieldScanModeVars();
1617 }
1618
1619 static int get_move_delay_from_stepsize(int move_stepsize)
1620 {
1621   move_stepsize =
1622     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1623
1624   // make sure that stepsize value is always a power of 2
1625   move_stepsize = (1 << log_2(move_stepsize));
1626
1627   return TILEX / move_stepsize;
1628 }
1629
1630 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1631                                boolean init_game)
1632 {
1633   int player_nr = player->index_nr;
1634   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1635   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1636
1637   // do no immediately change move delay -- the player might just be moving
1638   player->move_delay_value_next = move_delay;
1639
1640   // information if player can move must be set separately
1641   player->cannot_move = cannot_move;
1642
1643   if (init_game)
1644   {
1645     player->move_delay       = game.initial_move_delay[player_nr];
1646     player->move_delay_value = game.initial_move_delay_value[player_nr];
1647
1648     player->move_delay_value_next = -1;
1649
1650     player->move_delay_reset_counter = 0;
1651   }
1652 }
1653
1654 void GetPlayerConfig(void)
1655 {
1656   GameFrameDelay = setup.game_frame_delay;
1657
1658   if (!audio.sound_available)
1659     setup.sound_simple = FALSE;
1660
1661   if (!audio.loops_available)
1662     setup.sound_loops = FALSE;
1663
1664   if (!audio.music_available)
1665     setup.sound_music = FALSE;
1666
1667   if (!video.fullscreen_available)
1668     setup.fullscreen = FALSE;
1669
1670   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1671
1672   SetAudioMode(setup.sound);
1673 }
1674
1675 int GetElementFromGroupElement(int element)
1676 {
1677   if (IS_GROUP_ELEMENT(element))
1678   {
1679     struct ElementGroupInfo *group = element_info[element].group;
1680     int last_anim_random_frame = gfx.anim_random_frame;
1681     int element_pos;
1682
1683     if (group->choice_mode == ANIM_RANDOM)
1684       gfx.anim_random_frame = RND(group->num_elements_resolved);
1685
1686     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1687                                     group->choice_mode, 0,
1688                                     group->choice_pos);
1689
1690     if (group->choice_mode == ANIM_RANDOM)
1691       gfx.anim_random_frame = last_anim_random_frame;
1692
1693     group->choice_pos++;
1694
1695     element = group->element_resolved[element_pos];
1696   }
1697
1698   return element;
1699 }
1700
1701 static void IncrementSokobanFieldsNeeded(void)
1702 {
1703   if (level.sb_fields_needed)
1704     game.sokoban_fields_still_needed++;
1705 }
1706
1707 static void IncrementSokobanObjectsNeeded(void)
1708 {
1709   if (level.sb_objects_needed)
1710     game.sokoban_objects_still_needed++;
1711 }
1712
1713 static void DecrementSokobanFieldsNeeded(void)
1714 {
1715   if (game.sokoban_fields_still_needed > 0)
1716     game.sokoban_fields_still_needed--;
1717 }
1718
1719 static void DecrementSokobanObjectsNeeded(void)
1720 {
1721   if (game.sokoban_objects_still_needed > 0)
1722     game.sokoban_objects_still_needed--;
1723 }
1724
1725 static void InitPlayerField(int x, int y, int element, boolean init_game)
1726 {
1727   if (element == EL_SP_MURPHY)
1728   {
1729     if (init_game)
1730     {
1731       if (stored_player[0].present)
1732       {
1733         Tile[x][y] = EL_SP_MURPHY_CLONE;
1734
1735         return;
1736       }
1737       else
1738       {
1739         stored_player[0].initial_element = element;
1740         stored_player[0].use_murphy = TRUE;
1741
1742         if (!level.use_artwork_element[0])
1743           stored_player[0].artwork_element = EL_SP_MURPHY;
1744       }
1745
1746       Tile[x][y] = EL_PLAYER_1;
1747     }
1748   }
1749
1750   if (init_game)
1751   {
1752     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1753     int jx = player->jx, jy = player->jy;
1754
1755     player->present = TRUE;
1756
1757     player->block_last_field = (element == EL_SP_MURPHY ?
1758                                 level.sp_block_last_field :
1759                                 level.block_last_field);
1760
1761     // ---------- initialize player's last field block delay ------------------
1762
1763     // always start with reliable default value (no adjustment needed)
1764     player->block_delay_adjustment = 0;
1765
1766     // special case 1: in Supaplex, Murphy blocks last field one more frame
1767     if (player->block_last_field && element == EL_SP_MURPHY)
1768       player->block_delay_adjustment = 1;
1769
1770     // special case 2: in game engines before 3.1.1, blocking was different
1771     if (game.use_block_last_field_bug)
1772       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1773
1774     if (!network.enabled || player->connected_network)
1775     {
1776       player->active = TRUE;
1777
1778       // remove potentially duplicate players
1779       if (StorePlayer[jx][jy] == Tile[x][y])
1780         StorePlayer[jx][jy] = 0;
1781
1782       StorePlayer[x][y] = Tile[x][y];
1783
1784 #if DEBUG_INIT_PLAYER
1785       Debug("game:init:player", "- player element %d activated",
1786             player->element_nr);
1787       Debug("game:init:player", "  (local player is %d and currently %s)",
1788             local_player->element_nr,
1789             local_player->active ? "active" : "not active");
1790     }
1791 #endif
1792
1793     Tile[x][y] = EL_EMPTY;
1794
1795     player->jx = player->last_jx = x;
1796     player->jy = player->last_jy = y;
1797   }
1798
1799   // always check if player was just killed and should be reanimated
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; // if player was just killed, reanimate him
1806   }
1807 }
1808
1809 static void InitField(int x, int y, boolean init_game)
1810 {
1811   int element = Tile[x][y];
1812
1813   switch (element)
1814   {
1815     case EL_SP_MURPHY:
1816     case EL_PLAYER_1:
1817     case EL_PLAYER_2:
1818     case EL_PLAYER_3:
1819     case EL_PLAYER_4:
1820       InitPlayerField(x, y, element, init_game);
1821       break;
1822
1823     case EL_SOKOBAN_FIELD_PLAYER:
1824       element = Tile[x][y] = EL_PLAYER_1;
1825       InitField(x, y, init_game);
1826
1827       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1828       InitField(x, y, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_EMPTY:
1832       IncrementSokobanFieldsNeeded();
1833       break;
1834
1835     case EL_SOKOBAN_OBJECT:
1836       IncrementSokobanObjectsNeeded();
1837       break;
1838
1839     case EL_STONEBLOCK:
1840       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1841         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1842       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1843         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1844       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1845         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1846       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1847         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1848       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1849         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1850       break;
1851
1852     case EL_BUG:
1853     case EL_BUG_RIGHT:
1854     case EL_BUG_UP:
1855     case EL_BUG_LEFT:
1856     case EL_BUG_DOWN:
1857     case EL_SPACESHIP:
1858     case EL_SPACESHIP_RIGHT:
1859     case EL_SPACESHIP_UP:
1860     case EL_SPACESHIP_LEFT:
1861     case EL_SPACESHIP_DOWN:
1862     case EL_BD_BUTTERFLY:
1863     case EL_BD_BUTTERFLY_RIGHT:
1864     case EL_BD_BUTTERFLY_UP:
1865     case EL_BD_BUTTERFLY_LEFT:
1866     case EL_BD_BUTTERFLY_DOWN:
1867     case EL_BD_FIREFLY:
1868     case EL_BD_FIREFLY_RIGHT:
1869     case EL_BD_FIREFLY_UP:
1870     case EL_BD_FIREFLY_LEFT:
1871     case EL_BD_FIREFLY_DOWN:
1872     case EL_PACMAN_RIGHT:
1873     case EL_PACMAN_UP:
1874     case EL_PACMAN_LEFT:
1875     case EL_PACMAN_DOWN:
1876     case EL_YAMYAM:
1877     case EL_YAMYAM_LEFT:
1878     case EL_YAMYAM_RIGHT:
1879     case EL_YAMYAM_UP:
1880     case EL_YAMYAM_DOWN:
1881     case EL_DARK_YAMYAM:
1882     case EL_ROBOT:
1883     case EL_PACMAN:
1884     case EL_SP_SNIKSNAK:
1885     case EL_SP_ELECTRON:
1886     case EL_MOLE:
1887     case EL_MOLE_LEFT:
1888     case EL_MOLE_RIGHT:
1889     case EL_MOLE_UP:
1890     case EL_MOLE_DOWN:
1891     case EL_SPRING_LEFT:
1892     case EL_SPRING_RIGHT:
1893       InitMovDir(x, y);
1894       break;
1895
1896     case EL_AMOEBA_FULL:
1897     case EL_BD_AMOEBA:
1898       InitAmoebaNr(x, y);
1899       break;
1900
1901     case EL_AMOEBA_DROP:
1902       if (y == lev_fieldy - 1)
1903       {
1904         Tile[x][y] = EL_AMOEBA_GROWING;
1905         Store[x][y] = EL_AMOEBA_WET;
1906       }
1907       break;
1908
1909     case EL_DYNAMITE_ACTIVE:
1910     case EL_SP_DISK_RED_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1912     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1913     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1914     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1915       MovDelay[x][y] = 96;
1916       break;
1917
1918     case EL_EM_DYNAMITE_ACTIVE:
1919       MovDelay[x][y] = 32;
1920       break;
1921
1922     case EL_LAMP:
1923       game.lights_still_needed++;
1924       break;
1925
1926     case EL_PENGUIN:
1927       game.friends_still_needed++;
1928       break;
1929
1930     case EL_PIG:
1931     case EL_DRAGON:
1932       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1933       break;
1934
1935     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1944     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1945     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1946     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1947       if (init_game)
1948       {
1949         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1950         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1951         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1952
1953         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1954         {
1955           game.belt_dir[belt_nr] = belt_dir;
1956           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1957         }
1958         else    // more than one switch -- set it like the first switch
1959         {
1960           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1961         }
1962       }
1963       break;
1964
1965     case EL_LIGHT_SWITCH_ACTIVE:
1966       if (init_game)
1967         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1968       break;
1969
1970     case EL_INVISIBLE_STEELWALL:
1971     case EL_INVISIBLE_WALL:
1972     case EL_INVISIBLE_SAND:
1973       if (game.light_time_left > 0 ||
1974           game.lenses_time_left > 0)
1975         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1976       break;
1977
1978     case EL_EMC_MAGIC_BALL:
1979       if (game.ball_active)
1980         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1981       break;
1982
1983     case EL_EMC_MAGIC_BALL_SWITCH:
1984       if (game.ball_active)
1985         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1986       break;
1987
1988     case EL_TRIGGER_PLAYER:
1989     case EL_TRIGGER_ELEMENT:
1990     case EL_TRIGGER_CE_VALUE:
1991     case EL_TRIGGER_CE_SCORE:
1992     case EL_SELF:
1993     case EL_ANY_ELEMENT:
1994     case EL_CURRENT_CE_VALUE:
1995     case EL_CURRENT_CE_SCORE:
1996     case EL_PREV_CE_1:
1997     case EL_PREV_CE_2:
1998     case EL_PREV_CE_3:
1999     case EL_PREV_CE_4:
2000     case EL_PREV_CE_5:
2001     case EL_PREV_CE_6:
2002     case EL_PREV_CE_7:
2003     case EL_PREV_CE_8:
2004     case EL_NEXT_CE_1:
2005     case EL_NEXT_CE_2:
2006     case EL_NEXT_CE_3:
2007     case EL_NEXT_CE_4:
2008     case EL_NEXT_CE_5:
2009     case EL_NEXT_CE_6:
2010     case EL_NEXT_CE_7:
2011     case EL_NEXT_CE_8:
2012       // reference elements should not be used on the playfield
2013       Tile[x][y] = EL_EMPTY;
2014       break;
2015
2016     default:
2017       if (IS_CUSTOM_ELEMENT(element))
2018       {
2019         if (CAN_MOVE(element))
2020           InitMovDir(x, y);
2021
2022         if (!element_info[element].use_last_ce_value || init_game)
2023           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2024       }
2025       else if (IS_GROUP_ELEMENT(element))
2026       {
2027         Tile[x][y] = GetElementFromGroupElement(element);
2028
2029         InitField(x, y, init_game);
2030       }
2031
2032       break;
2033   }
2034
2035   if (!init_game)
2036     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2037 }
2038
2039 static void InitField_WithBug1(int x, int y, boolean init_game)
2040 {
2041   InitField(x, y, init_game);
2042
2043   // not needed to call InitMovDir() -- already done by InitField()!
2044   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2045       CAN_MOVE(Tile[x][y]))
2046     InitMovDir(x, y);
2047 }
2048
2049 static void InitField_WithBug2(int x, int y, boolean init_game)
2050 {
2051   int old_element = Tile[x][y];
2052
2053   InitField(x, y, init_game);
2054
2055   // not needed to call InitMovDir() -- already done by InitField()!
2056   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2057       CAN_MOVE(old_element) &&
2058       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2059     InitMovDir(x, y);
2060
2061   /* this case is in fact a combination of not less than three bugs:
2062      first, it calls InitMovDir() for elements that can move, although this is
2063      already done by InitField(); then, it checks the element that was at this
2064      field _before_ the call to InitField() (which can change it); lastly, it
2065      was not called for "mole with direction" elements, which were treated as
2066      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2067   */
2068 }
2069
2070 static int get_key_element_from_nr(int key_nr)
2071 {
2072   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2073                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2074                           EL_EM_KEY_1 : EL_KEY_1);
2075
2076   return key_base_element + key_nr;
2077 }
2078
2079 static int get_next_dropped_element(struct PlayerInfo *player)
2080 {
2081   return (player->inventory_size > 0 ?
2082           player->inventory_element[player->inventory_size - 1] :
2083           player->inventory_infinite_element != EL_UNDEFINED ?
2084           player->inventory_infinite_element :
2085           player->dynabombs_left > 0 ?
2086           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2087           EL_UNDEFINED);
2088 }
2089
2090 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2091 {
2092   // pos >= 0: get element from bottom of the stack;
2093   // pos <  0: get element from top of the stack
2094
2095   if (pos < 0)
2096   {
2097     int min_inventory_size = -pos;
2098     int inventory_pos = player->inventory_size - min_inventory_size;
2099     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2100
2101     return (player->inventory_size >= min_inventory_size ?
2102             player->inventory_element[inventory_pos] :
2103             player->inventory_infinite_element != EL_UNDEFINED ?
2104             player->inventory_infinite_element :
2105             player->dynabombs_left >= min_dynabombs_left ?
2106             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2107             EL_UNDEFINED);
2108   }
2109   else
2110   {
2111     int min_dynabombs_left = pos + 1;
2112     int min_inventory_size = pos + 1 - player->dynabombs_left;
2113     int inventory_pos = pos - player->dynabombs_left;
2114
2115     return (player->inventory_infinite_element != EL_UNDEFINED ?
2116             player->inventory_infinite_element :
2117             player->dynabombs_left >= min_dynabombs_left ?
2118             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2119             player->inventory_size >= min_inventory_size ?
2120             player->inventory_element[inventory_pos] :
2121             EL_UNDEFINED);
2122   }
2123 }
2124
2125 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2126 {
2127   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2128   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2129   int compare_result;
2130
2131   if (gpo1->sort_priority != gpo2->sort_priority)
2132     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2133   else
2134     compare_result = gpo1->nr - gpo2->nr;
2135
2136   return compare_result;
2137 }
2138
2139 int getPlayerInventorySize(int player_nr)
2140 {
2141   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2142     return game_em.ply[player_nr]->dynamite;
2143   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2144     return game_sp.red_disk_count;
2145   else
2146     return stored_player[player_nr].inventory_size;
2147 }
2148
2149 static void InitGameControlValues(void)
2150 {
2151   int i;
2152
2153   for (i = 0; game_panel_controls[i].nr != -1; i++)
2154   {
2155     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2156     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2157     struct TextPosInfo *pos = gpc->pos;
2158     int nr = gpc->nr;
2159     int type = gpc->type;
2160
2161     if (nr != i)
2162     {
2163       Error("'game_panel_controls' structure corrupted at %d", i);
2164
2165       Fail("this should not happen -- please debug");
2166     }
2167
2168     // force update of game controls after initialization
2169     gpc->value = gpc->last_value = -1;
2170     gpc->frame = gpc->last_frame = -1;
2171     gpc->gfx_frame = -1;
2172
2173     // determine panel value width for later calculation of alignment
2174     if (type == TYPE_INTEGER || type == TYPE_STRING)
2175     {
2176       pos->width = pos->size * getFontWidth(pos->font);
2177       pos->height = getFontHeight(pos->font);
2178     }
2179     else if (type == TYPE_ELEMENT)
2180     {
2181       pos->width = pos->size;
2182       pos->height = pos->size;
2183     }
2184
2185     // fill structure for game panel draw order
2186     gpo->nr = gpc->nr;
2187     gpo->sort_priority = pos->sort_priority;
2188   }
2189
2190   // sort game panel controls according to sort_priority and control number
2191   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2192         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2193 }
2194
2195 static void UpdatePlayfieldElementCount(void)
2196 {
2197   boolean use_element_count = FALSE;
2198   int i, j, x, y;
2199
2200   // first check if it is needed at all to calculate playfield element count
2201   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2202     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2203       use_element_count = TRUE;
2204
2205   if (!use_element_count)
2206     return;
2207
2208   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2209     element_info[i].element_count = 0;
2210
2211   SCAN_PLAYFIELD(x, y)
2212   {
2213     element_info[Tile[x][y]].element_count++;
2214   }
2215
2216   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2217     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2218       if (IS_IN_GROUP(j, i))
2219         element_info[EL_GROUP_START + i].element_count +=
2220           element_info[j].element_count;
2221 }
2222
2223 static void UpdateGameControlValues(void)
2224 {
2225   int i, k;
2226   int time = (game.LevelSolved ?
2227               game.LevelSolved_CountingTime :
2228               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               game_em.lev->time :
2230               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231               game_sp.time_played :
2232               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2233               game_mm.energy_left :
2234               game.no_time_limit ? TimePlayed : TimeLeft);
2235   int score = (game.LevelSolved ?
2236                game.LevelSolved_CountingScore :
2237                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238                game_em.lev->score :
2239                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240                game_sp.score :
2241                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2242                game_mm.score :
2243                game.score);
2244   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->gems_needed :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.infotrons_still_needed :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.kettles_still_needed :
2250               game.gems_still_needed);
2251   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2252                      game_em.lev->gems_needed > 0 :
2253                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2254                      game_sp.infotrons_still_needed > 0 :
2255                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2256                      game_mm.kettles_still_needed > 0 ||
2257                      game_mm.lights_still_needed > 0 :
2258                      game.gems_still_needed > 0 ||
2259                      game.sokoban_fields_still_needed > 0 ||
2260                      game.sokoban_objects_still_needed > 0 ||
2261                      game.lights_still_needed > 0);
2262   int health = (game.LevelSolved ?
2263                 game.LevelSolved_CountingHealth :
2264                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265                 MM_HEALTH(game_mm.laser_overload_value) :
2266                 game.health);
2267   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2268
2269   UpdatePlayfieldElementCount();
2270
2271   // update game panel control values
2272
2273   // used instead of "level_nr" (for network games)
2274   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2275   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2276
2277   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2278   for (i = 0; i < MAX_NUM_KEYS; i++)
2279     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2280   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2281   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2282
2283   if (game.centered_player_nr == -1)
2284   {
2285     for (i = 0; i < MAX_PLAYERS; i++)
2286     {
2287       // only one player in Supaplex game engine
2288       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2289         break;
2290
2291       for (k = 0; k < MAX_NUM_KEYS; k++)
2292       {
2293         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2294         {
2295           if (game_em.ply[i]->keys & (1 << k))
2296             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2297               get_key_element_from_nr(k);
2298         }
2299         else if (stored_player[i].key[k])
2300           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2301             get_key_element_from_nr(k);
2302       }
2303
2304       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2305         getPlayerInventorySize(i);
2306
2307       if (stored_player[i].num_white_keys > 0)
2308         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2309           EL_DC_KEY_WHITE;
2310
2311       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2312         stored_player[i].num_white_keys;
2313     }
2314   }
2315   else
2316   {
2317     int player_nr = game.centered_player_nr;
2318
2319     for (k = 0; k < MAX_NUM_KEYS; k++)
2320     {
2321       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2322       {
2323         if (game_em.ply[player_nr]->keys & (1 << k))
2324           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2325             get_key_element_from_nr(k);
2326       }
2327       else if (stored_player[player_nr].key[k])
2328         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2329           get_key_element_from_nr(k);
2330     }
2331
2332     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2333       getPlayerInventorySize(player_nr);
2334
2335     if (stored_player[player_nr].num_white_keys > 0)
2336       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2337
2338     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2339       stored_player[player_nr].num_white_keys;
2340   }
2341
2342   // re-arrange keys on game panel, if needed or if defined by style settings
2343   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2344   {
2345     int nr = GAME_PANEL_KEY_1 + i;
2346     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2347     struct TextPosInfo *pos = gpc->pos;
2348
2349     // skip check if key is not in the player's inventory
2350     if (gpc->value == EL_EMPTY)
2351       continue;
2352
2353     // check if keys should be arranged on panel from left to right
2354     if (pos->style == STYLE_LEFTMOST_POSITION)
2355     {
2356       // check previous key positions (left from current key)
2357       for (k = 0; k < i; k++)
2358       {
2359         int nr_new = GAME_PANEL_KEY_1 + k;
2360
2361         if (game_panel_controls[nr_new].value == EL_EMPTY)
2362         {
2363           game_panel_controls[nr_new].value = gpc->value;
2364           gpc->value = EL_EMPTY;
2365
2366           break;
2367         }
2368       }
2369     }
2370
2371     // check if "undefined" keys can be placed at some other position
2372     if (pos->x == -1 && pos->y == -1)
2373     {
2374       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2375
2376       // 1st try: display key at the same position as normal or EM keys
2377       if (game_panel_controls[nr_new].value == EL_EMPTY)
2378       {
2379         game_panel_controls[nr_new].value = gpc->value;
2380       }
2381       else
2382       {
2383         // 2nd try: display key at the next free position in the key panel
2384         for (k = 0; k < STD_NUM_KEYS; k++)
2385         {
2386           nr_new = GAME_PANEL_KEY_1 + k;
2387
2388           if (game_panel_controls[nr_new].value == EL_EMPTY)
2389           {
2390             game_panel_controls[nr_new].value = gpc->value;
2391
2392             break;
2393           }
2394         }
2395       }
2396     }
2397   }
2398
2399   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2400   {
2401     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2402       get_inventory_element_from_pos(local_player, i);
2403     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2404       get_inventory_element_from_pos(local_player, -i - 1);
2405   }
2406
2407   game_panel_controls[GAME_PANEL_SCORE].value = score;
2408   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2409
2410   game_panel_controls[GAME_PANEL_TIME].value = time;
2411
2412   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2413   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2414   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2415
2416   if (level.time == 0)
2417     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2418   else
2419     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2420
2421   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2422   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2423
2424   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2425
2426   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2427     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2428      EL_EMPTY);
2429   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2430     local_player->shield_normal_time_left;
2431   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2432     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2433      EL_EMPTY);
2434   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2435     local_player->shield_deadly_time_left;
2436
2437   game_panel_controls[GAME_PANEL_EXIT].value =
2438     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2439
2440   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2441     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2442   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2443     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2444      EL_EMC_MAGIC_BALL_SWITCH);
2445
2446   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2447     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2448   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2449     game.light_time_left;
2450
2451   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2452     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2453   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2454     game.timegate_time_left;
2455
2456   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2457     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2458
2459   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2460     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2461   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2462     game.lenses_time_left;
2463
2464   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2465     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2466   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2467     game.magnify_time_left;
2468
2469   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2470     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2471      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2472      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2473      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2474      EL_BALLOON_SWITCH_NONE);
2475
2476   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2477     local_player->dynabomb_count;
2478   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2479     local_player->dynabomb_size;
2480   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2481     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2482
2483   game_panel_controls[GAME_PANEL_PENGUINS].value =
2484     game.friends_still_needed;
2485
2486   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2487     game.sokoban_objects_still_needed;
2488   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2489     game.sokoban_fields_still_needed;
2490
2491   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2492     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2493
2494   for (i = 0; i < NUM_BELTS; i++)
2495   {
2496     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2497       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2498        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2499     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2500       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2501   }
2502
2503   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2504     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2505   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2506     game.magic_wall_time_left;
2507
2508   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2509     local_player->gravity;
2510
2511   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2512     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2513
2514   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2515     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2516       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2517        game.panel.element[i].id : EL_UNDEFINED);
2518
2519   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2520     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2521       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2522        element_info[game.panel.element_count[i].id].element_count : 0);
2523
2524   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2525     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2526       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2527        element_info[game.panel.ce_score[i].id].collect_score : 0);
2528
2529   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2530     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2531       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2532        element_info[game.panel.ce_score_element[i].id].collect_score :
2533        EL_UNDEFINED);
2534
2535   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2536   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2537   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2538
2539   // update game panel control frames
2540
2541   for (i = 0; game_panel_controls[i].nr != -1; i++)
2542   {
2543     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2544
2545     if (gpc->type == TYPE_ELEMENT)
2546     {
2547       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2548       {
2549         int last_anim_random_frame = gfx.anim_random_frame;
2550         int element = gpc->value;
2551         int graphic = el2panelimg(element);
2552         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2553                                sync_random_frame : INIT_GFX_RANDOM());
2554
2555         if (gpc->value != gpc->last_value)
2556         {
2557           gpc->gfx_frame = 0;
2558           gpc->gfx_random = init_gfx_random;
2559         }
2560         else
2561         {
2562           gpc->gfx_frame++;
2563
2564           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2565               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2566             gpc->gfx_random = init_gfx_random;
2567         }
2568
2569         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2570           gfx.anim_random_frame = gpc->gfx_random;
2571
2572         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2573           gpc->gfx_frame = element_info[element].collect_score;
2574
2575         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2576
2577         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2578           gfx.anim_random_frame = last_anim_random_frame;
2579       }
2580     }
2581     else if (gpc->type == TYPE_GRAPHIC)
2582     {
2583       if (gpc->graphic != IMG_UNDEFINED)
2584       {
2585         int last_anim_random_frame = gfx.anim_random_frame;
2586         int graphic = gpc->graphic;
2587         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2588                                sync_random_frame : INIT_GFX_RANDOM());
2589
2590         if (gpc->value != gpc->last_value)
2591         {
2592           gpc->gfx_frame = 0;
2593           gpc->gfx_random = init_gfx_random;
2594         }
2595         else
2596         {
2597           gpc->gfx_frame++;
2598
2599           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2600               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2601             gpc->gfx_random = init_gfx_random;
2602         }
2603
2604         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2605           gfx.anim_random_frame = gpc->gfx_random;
2606
2607         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2608
2609         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2610           gfx.anim_random_frame = last_anim_random_frame;
2611       }
2612     }
2613   }
2614 }
2615
2616 static void DisplayGameControlValues(void)
2617 {
2618   boolean redraw_panel = FALSE;
2619   int i;
2620
2621   for (i = 0; game_panel_controls[i].nr != -1; i++)
2622   {
2623     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2624
2625     if (PANEL_DEACTIVATED(gpc->pos))
2626       continue;
2627
2628     if (gpc->value == gpc->last_value &&
2629         gpc->frame == gpc->last_frame)
2630       continue;
2631
2632     redraw_panel = TRUE;
2633   }
2634
2635   if (!redraw_panel)
2636     return;
2637
2638   // copy default game door content to main double buffer
2639
2640   // !!! CHECK AGAIN !!!
2641   SetPanelBackground();
2642   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2643   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2644
2645   // redraw game control buttons
2646   RedrawGameButtons();
2647
2648   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2649
2650   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2651   {
2652     int nr = game_panel_order[i].nr;
2653     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2654     struct TextPosInfo *pos = gpc->pos;
2655     int type = gpc->type;
2656     int value = gpc->value;
2657     int frame = gpc->frame;
2658     int size = pos->size;
2659     int font = pos->font;
2660     boolean draw_masked = pos->draw_masked;
2661     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2662
2663     if (PANEL_DEACTIVATED(pos))
2664       continue;
2665
2666     if (pos->class == get_hash_from_key("extra_panel_items") &&
2667         !setup.prefer_extra_panel_items)
2668       continue;
2669
2670     gpc->last_value = value;
2671     gpc->last_frame = frame;
2672
2673     if (type == TYPE_INTEGER)
2674     {
2675       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2676           nr == GAME_PANEL_TIME)
2677       {
2678         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2679
2680         if (use_dynamic_size)           // use dynamic number of digits
2681         {
2682           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2683           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2684           int size2 = size1 + 1;
2685           int font1 = pos->font;
2686           int font2 = pos->font_alt;
2687
2688           size = (value < value_change ? size1 : size2);
2689           font = (value < value_change ? font1 : font2);
2690         }
2691       }
2692
2693       // correct text size if "digits" is zero or less
2694       if (size <= 0)
2695         size = strlen(int2str(value, size));
2696
2697       // dynamically correct text alignment
2698       pos->width = size * getFontWidth(font);
2699
2700       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2701                   int2str(value, size), font, mask_mode);
2702     }
2703     else if (type == TYPE_ELEMENT)
2704     {
2705       int element, graphic;
2706       Bitmap *src_bitmap;
2707       int src_x, src_y;
2708       int width, height;
2709       int dst_x = PANEL_XPOS(pos);
2710       int dst_y = PANEL_YPOS(pos);
2711
2712       if (value != EL_UNDEFINED && value != EL_EMPTY)
2713       {
2714         element = value;
2715         graphic = el2panelimg(value);
2716
2717 #if 0
2718         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2719               element, EL_NAME(element), size);
2720 #endif
2721
2722         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2723           size = TILESIZE;
2724
2725         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2726                               &src_x, &src_y);
2727
2728         width  = graphic_info[graphic].width  * size / TILESIZE;
2729         height = graphic_info[graphic].height * size / TILESIZE;
2730
2731         if (draw_masked)
2732           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2733                            dst_x, dst_y);
2734         else
2735           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2736                      dst_x, dst_y);
2737       }
2738     }
2739     else if (type == TYPE_GRAPHIC)
2740     {
2741       int graphic        = gpc->graphic;
2742       int graphic_active = gpc->graphic_active;
2743       Bitmap *src_bitmap;
2744       int src_x, src_y;
2745       int width, height;
2746       int dst_x = PANEL_XPOS(pos);
2747       int dst_y = PANEL_YPOS(pos);
2748       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2749                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2750
2751       if (graphic != IMG_UNDEFINED && !skip)
2752       {
2753         if (pos->style == STYLE_REVERSE)
2754           value = 100 - value;
2755
2756         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2757
2758         if (pos->direction & MV_HORIZONTAL)
2759         {
2760           width  = graphic_info[graphic_active].width * value / 100;
2761           height = graphic_info[graphic_active].height;
2762
2763           if (pos->direction == MV_LEFT)
2764           {
2765             src_x += graphic_info[graphic_active].width - width;
2766             dst_x += graphic_info[graphic_active].width - width;
2767           }
2768         }
2769         else
2770         {
2771           width  = graphic_info[graphic_active].width;
2772           height = graphic_info[graphic_active].height * value / 100;
2773
2774           if (pos->direction == MV_UP)
2775           {
2776             src_y += graphic_info[graphic_active].height - height;
2777             dst_y += graphic_info[graphic_active].height - height;
2778           }
2779         }
2780
2781         if (draw_masked)
2782           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2783                            dst_x, dst_y);
2784         else
2785           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2786                      dst_x, dst_y);
2787
2788         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2789
2790         if (pos->direction & MV_HORIZONTAL)
2791         {
2792           if (pos->direction == MV_RIGHT)
2793           {
2794             src_x += width;
2795             dst_x += width;
2796           }
2797           else
2798           {
2799             dst_x = PANEL_XPOS(pos);
2800           }
2801
2802           width = graphic_info[graphic].width - width;
2803         }
2804         else
2805         {
2806           if (pos->direction == MV_DOWN)
2807           {
2808             src_y += height;
2809             dst_y += height;
2810           }
2811           else
2812           {
2813             dst_y = PANEL_YPOS(pos);
2814           }
2815
2816           height = graphic_info[graphic].height - height;
2817         }
2818
2819         if (draw_masked)
2820           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2821                            dst_x, dst_y);
2822         else
2823           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2824                      dst_x, dst_y);
2825       }
2826     }
2827     else if (type == TYPE_STRING)
2828     {
2829       boolean active = (value != 0);
2830       char *state_normal = "off";
2831       char *state_active = "on";
2832       char *state = (active ? state_active : state_normal);
2833       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2834                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2835                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2836                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2837
2838       if (nr == GAME_PANEL_GRAVITY_STATE)
2839       {
2840         int font1 = pos->font;          // (used for normal state)
2841         int font2 = pos->font_alt;      // (used for active state)
2842
2843         font = (active ? font2 : font1);
2844       }
2845
2846       if (s != NULL)
2847       {
2848         char *s_cut;
2849
2850         if (size <= 0)
2851         {
2852           // don't truncate output if "chars" is zero or less
2853           size = strlen(s);
2854
2855           // dynamically correct text alignment
2856           pos->width = size * getFontWidth(font);
2857         }
2858
2859         s_cut = getStringCopyN(s, size);
2860
2861         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2862                     s_cut, font, mask_mode);
2863
2864         free(s_cut);
2865       }
2866     }
2867
2868     redraw_mask |= REDRAW_DOOR_1;
2869   }
2870
2871   SetGameStatus(GAME_MODE_PLAYING);
2872 }
2873
2874 void UpdateAndDisplayGameControlValues(void)
2875 {
2876   if (tape.deactivate_display)
2877     return;
2878
2879   UpdateGameControlValues();
2880   DisplayGameControlValues();
2881 }
2882
2883 void UpdateGameDoorValues(void)
2884 {
2885   UpdateGameControlValues();
2886 }
2887
2888 void DrawGameDoorValues(void)
2889 {
2890   DisplayGameControlValues();
2891 }
2892
2893
2894 // ============================================================================
2895 // InitGameEngine()
2896 // ----------------------------------------------------------------------------
2897 // initialize game engine due to level / tape version number
2898 // ============================================================================
2899
2900 static void InitGameEngine(void)
2901 {
2902   int i, j, k, l, x, y;
2903
2904   // set game engine from tape file when re-playing, else from level file
2905   game.engine_version = (tape.playing ? tape.engine_version :
2906                          level.game_version);
2907
2908   // set single or multi-player game mode (needed for re-playing tapes)
2909   game.team_mode = setup.team_mode;
2910
2911   if (tape.playing)
2912   {
2913     int num_players = 0;
2914
2915     for (i = 0; i < MAX_PLAYERS; i++)
2916       if (tape.player_participates[i])
2917         num_players++;
2918
2919     // multi-player tapes contain input data for more than one player
2920     game.team_mode = (num_players > 1);
2921   }
2922
2923 #if 0
2924   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2925         level.game_version);
2926   Debug("game:init:level", "          tape.file_version   == %06d",
2927         tape.file_version);
2928   Debug("game:init:level", "          tape.game_version   == %06d",
2929         tape.game_version);
2930   Debug("game:init:level", "          tape.engine_version == %06d",
2931         tape.engine_version);
2932   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2933         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2934 #endif
2935
2936   // --------------------------------------------------------------------------
2937   // set flags for bugs and changes according to active game engine version
2938   // --------------------------------------------------------------------------
2939
2940   /*
2941     Summary of bugfix:
2942     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2943
2944     Bug was introduced in version:
2945     2.0.1
2946
2947     Bug was fixed in version:
2948     4.2.0.0
2949
2950     Description:
2951     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2952     but the property "can fall" was missing, which caused some levels to be
2953     unsolvable. This was fixed in version 4.2.0.0.
2954
2955     Affected levels/tapes:
2956     An example for a tape that was fixed by this bugfix is tape 029 from the
2957     level set "rnd_sam_bateman".
2958     The wrong behaviour will still be used for all levels or tapes that were
2959     created/recorded with it. An example for this is tape 023 from the level
2960     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2961   */
2962
2963   boolean use_amoeba_dropping_cannot_fall_bug =
2964     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2965       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2966      (tape.playing &&
2967       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2968       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2969
2970   /*
2971     Summary of bugfix/change:
2972     Fixed move speed of elements entering or leaving magic wall.
2973
2974     Fixed/changed in version:
2975     2.0.1
2976
2977     Description:
2978     Before 2.0.1, move speed of elements entering or leaving magic wall was
2979     twice as fast as it is now.
2980     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2981
2982     Affected levels/tapes:
2983     The first condition is generally needed for all levels/tapes before version
2984     2.0.1, which might use the old behaviour before it was changed; known tapes
2985     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2986     The second condition is an exception from the above case and is needed for
2987     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2988     above, but before it was known that this change would break tapes like the
2989     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2990     although the engine version while recording maybe was before 2.0.1. There
2991     are a lot of tapes that are affected by this exception, like tape 006 from
2992     the level set "rnd_conor_mancone".
2993   */
2994
2995   boolean use_old_move_stepsize_for_magic_wall =
2996     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2997      !(tape.playing &&
2998        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2999        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3000
3001   /*
3002     Summary of bugfix/change:
3003     Fixed handling for custom elements that change when pushed by the player.
3004
3005     Fixed/changed in version:
3006     3.1.0
3007
3008     Description:
3009     Before 3.1.0, custom elements that "change when pushing" changed directly
3010     after the player started pushing them (until then handled in "DigField()").
3011     Since 3.1.0, these custom elements are not changed until the "pushing"
3012     move of the element is finished (now handled in "ContinueMoving()").
3013
3014     Affected levels/tapes:
3015     The first condition is generally needed for all levels/tapes before version
3016     3.1.0, which might use the old behaviour before it was changed; known tapes
3017     that are affected are some tapes from the level set "Walpurgis Gardens" by
3018     Jamie Cullen.
3019     The second condition is an exception from the above case and is needed for
3020     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3021     above (including some development versions of 3.1.0), but before it was
3022     known that this change would break tapes like the above and was fixed in
3023     3.1.1, so that the changed behaviour was active although the engine version
3024     while recording maybe was before 3.1.0. There is at least one tape that is
3025     affected by this exception, which is the tape for the one-level set "Bug
3026     Machine" by Juergen Bonhagen.
3027   */
3028
3029   game.use_change_when_pushing_bug =
3030     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3031      !(tape.playing &&
3032        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3033        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3034
3035   /*
3036     Summary of bugfix/change:
3037     Fixed handling for blocking the field the player leaves when moving.
3038
3039     Fixed/changed in version:
3040     3.1.1
3041
3042     Description:
3043     Before 3.1.1, when "block last field when moving" was enabled, the field
3044     the player is leaving when moving was blocked for the time of the move,
3045     and was directly unblocked afterwards. This resulted in the last field
3046     being blocked for exactly one less than the number of frames of one player
3047     move. Additionally, even when blocking was disabled, the last field was
3048     blocked for exactly one frame.
3049     Since 3.1.1, due to changes in player movement handling, the last field
3050     is not blocked at all when blocking is disabled. When blocking is enabled,
3051     the last field is blocked for exactly the number of frames of one player
3052     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3053     last field is blocked for exactly one more than the number of frames of
3054     one player move.
3055
3056     Affected levels/tapes:
3057     (!!! yet to be determined -- probably many !!!)
3058   */
3059
3060   game.use_block_last_field_bug =
3061     (game.engine_version < VERSION_IDENT(3,1,1,0));
3062
3063   /* various special flags and settings for native Emerald Mine game engine */
3064
3065   game_em.use_single_button =
3066     (game.engine_version > VERSION_IDENT(4,0,0,2));
3067
3068   game_em.use_snap_key_bug =
3069     (game.engine_version < VERSION_IDENT(4,0,1,0));
3070
3071   game_em.use_random_bug =
3072     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3073
3074   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3075
3076   game_em.use_old_explosions            = use_old_em_engine;
3077   game_em.use_old_android               = use_old_em_engine;
3078   game_em.use_old_push_elements         = use_old_em_engine;
3079   game_em.use_old_push_into_acid        = use_old_em_engine;
3080
3081   game_em.use_wrap_around               = !use_old_em_engine;
3082
3083   // --------------------------------------------------------------------------
3084
3085   // set maximal allowed number of custom element changes per game frame
3086   game.max_num_changes_per_frame = 1;
3087
3088   // default scan direction: scan playfield from top/left to bottom/right
3089   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3090
3091   // dynamically adjust element properties according to game engine version
3092   InitElementPropertiesEngine(game.engine_version);
3093
3094   // ---------- initialize special element properties -------------------------
3095
3096   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3097   if (use_amoeba_dropping_cannot_fall_bug)
3098     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3099
3100   // ---------- initialize player's initial move delay ------------------------
3101
3102   // dynamically adjust player properties according to level information
3103   for (i = 0; i < MAX_PLAYERS; i++)
3104     game.initial_move_delay_value[i] =
3105       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3106
3107   // dynamically adjust player properties according to game engine version
3108   for (i = 0; i < MAX_PLAYERS; i++)
3109     game.initial_move_delay[i] =
3110       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3111        game.initial_move_delay_value[i] : 0);
3112
3113   // ---------- initialize player's initial push delay ------------------------
3114
3115   // dynamically adjust player properties according to game engine version
3116   game.initial_push_delay_value =
3117     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3118
3119   // ---------- initialize changing elements ----------------------------------
3120
3121   // initialize changing elements information
3122   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3123   {
3124     struct ElementInfo *ei = &element_info[i];
3125
3126     // this pointer might have been changed in the level editor
3127     ei->change = &ei->change_page[0];
3128
3129     if (!IS_CUSTOM_ELEMENT(i))
3130     {
3131       ei->change->target_element = EL_EMPTY_SPACE;
3132       ei->change->delay_fixed = 0;
3133       ei->change->delay_random = 0;
3134       ei->change->delay_frames = 1;
3135     }
3136
3137     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3138     {
3139       ei->has_change_event[j] = FALSE;
3140
3141       ei->event_page_nr[j] = 0;
3142       ei->event_page[j] = &ei->change_page[0];
3143     }
3144   }
3145
3146   // add changing elements from pre-defined list
3147   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3148   {
3149     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3150     struct ElementInfo *ei = &element_info[ch_delay->element];
3151
3152     ei->change->target_element       = ch_delay->target_element;
3153     ei->change->delay_fixed          = ch_delay->change_delay;
3154
3155     ei->change->pre_change_function  = ch_delay->pre_change_function;
3156     ei->change->change_function      = ch_delay->change_function;
3157     ei->change->post_change_function = ch_delay->post_change_function;
3158
3159     ei->change->can_change = TRUE;
3160     ei->change->can_change_or_has_action = TRUE;
3161
3162     ei->has_change_event[CE_DELAY] = TRUE;
3163
3164     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3165     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3166   }
3167
3168   // ---------- initialize internal run-time variables ------------------------
3169
3170   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3171   {
3172     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3173
3174     for (j = 0; j < ei->num_change_pages; j++)
3175     {
3176       ei->change_page[j].can_change_or_has_action =
3177         (ei->change_page[j].can_change |
3178          ei->change_page[j].has_action);
3179     }
3180   }
3181
3182   // add change events from custom element configuration
3183   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3184   {
3185     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3186
3187     for (j = 0; j < ei->num_change_pages; j++)
3188     {
3189       if (!ei->change_page[j].can_change_or_has_action)
3190         continue;
3191
3192       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3193       {
3194         // only add event page for the first page found with this event
3195         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3196         {
3197           ei->has_change_event[k] = TRUE;
3198
3199           ei->event_page_nr[k] = j;
3200           ei->event_page[k] = &ei->change_page[j];
3201         }
3202       }
3203     }
3204   }
3205
3206   // ---------- initialize reference elements in change conditions ------------
3207
3208   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3209   {
3210     int element = EL_CUSTOM_START + i;
3211     struct ElementInfo *ei = &element_info[element];
3212
3213     for (j = 0; j < ei->num_change_pages; j++)
3214     {
3215       int trigger_element = ei->change_page[j].initial_trigger_element;
3216
3217       if (trigger_element >= EL_PREV_CE_8 &&
3218           trigger_element <= EL_NEXT_CE_8)
3219         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3220
3221       ei->change_page[j].trigger_element = trigger_element;
3222     }
3223   }
3224
3225   // ---------- initialize run-time trigger player and element ----------------
3226
3227   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3228   {
3229     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3230
3231     for (j = 0; j < ei->num_change_pages; j++)
3232     {
3233       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3234       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3235       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3236       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3237       ei->change_page[j].actual_trigger_ce_value = 0;
3238       ei->change_page[j].actual_trigger_ce_score = 0;
3239     }
3240   }
3241
3242   // ---------- initialize trigger events -------------------------------------
3243
3244   // initialize trigger events information
3245   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3246     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3247       trigger_events[i][j] = FALSE;
3248
3249   // add trigger events from element change event properties
3250   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3251   {
3252     struct ElementInfo *ei = &element_info[i];
3253
3254     for (j = 0; j < ei->num_change_pages; j++)
3255     {
3256       if (!ei->change_page[j].can_change_or_has_action)
3257         continue;
3258
3259       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3260       {
3261         int trigger_element = ei->change_page[j].trigger_element;
3262
3263         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3264         {
3265           if (ei->change_page[j].has_event[k])
3266           {
3267             if (IS_GROUP_ELEMENT(trigger_element))
3268             {
3269               struct ElementGroupInfo *group =
3270                 element_info[trigger_element].group;
3271
3272               for (l = 0; l < group->num_elements_resolved; l++)
3273                 trigger_events[group->element_resolved[l]][k] = TRUE;
3274             }
3275             else if (trigger_element == EL_ANY_ELEMENT)
3276               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3277                 trigger_events[l][k] = TRUE;
3278             else
3279               trigger_events[trigger_element][k] = TRUE;
3280           }
3281         }
3282       }
3283     }
3284   }
3285
3286   // ---------- initialize push delay -----------------------------------------
3287
3288   // initialize push delay values to default
3289   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3290   {
3291     if (!IS_CUSTOM_ELEMENT(i))
3292     {
3293       // set default push delay values (corrected since version 3.0.7-1)
3294       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3295       {
3296         element_info[i].push_delay_fixed = 2;
3297         element_info[i].push_delay_random = 8;
3298       }
3299       else
3300       {
3301         element_info[i].push_delay_fixed = 8;
3302         element_info[i].push_delay_random = 8;
3303       }
3304     }
3305   }
3306
3307   // set push delay value for certain elements from pre-defined list
3308   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3309   {
3310     int e = push_delay_list[i].element;
3311
3312     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3313     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3314   }
3315
3316   // set push delay value for Supaplex elements for newer engine versions
3317   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3318   {
3319     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3320     {
3321       if (IS_SP_ELEMENT(i))
3322       {
3323         // set SP push delay to just enough to push under a falling zonk
3324         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3325
3326         element_info[i].push_delay_fixed  = delay;
3327         element_info[i].push_delay_random = 0;
3328       }
3329     }
3330   }
3331
3332   // ---------- initialize move stepsize --------------------------------------
3333
3334   // initialize move stepsize values to default
3335   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3336     if (!IS_CUSTOM_ELEMENT(i))
3337       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3338
3339   // set move stepsize value for certain elements from pre-defined list
3340   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3341   {
3342     int e = move_stepsize_list[i].element;
3343
3344     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3345
3346     // set move stepsize value for certain elements for older engine versions
3347     if (use_old_move_stepsize_for_magic_wall)
3348     {
3349       if (e == EL_MAGIC_WALL_FILLING ||
3350           e == EL_MAGIC_WALL_EMPTYING ||
3351           e == EL_BD_MAGIC_WALL_FILLING ||
3352           e == EL_BD_MAGIC_WALL_EMPTYING)
3353         element_info[e].move_stepsize *= 2;
3354     }
3355   }
3356
3357   // ---------- initialize collect score --------------------------------------
3358
3359   // initialize collect score values for custom elements from initial value
3360   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3361     if (IS_CUSTOM_ELEMENT(i))
3362       element_info[i].collect_score = element_info[i].collect_score_initial;
3363
3364   // ---------- initialize collect count --------------------------------------
3365
3366   // initialize collect count values for non-custom elements
3367   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3368     if (!IS_CUSTOM_ELEMENT(i))
3369       element_info[i].collect_count_initial = 0;
3370
3371   // add collect count values for all elements from pre-defined list
3372   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3373     element_info[collect_count_list[i].element].collect_count_initial =
3374       collect_count_list[i].count;
3375
3376   // ---------- initialize access direction -----------------------------------
3377
3378   // initialize access direction values to default (access from every side)
3379   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3380     if (!IS_CUSTOM_ELEMENT(i))
3381       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3382
3383   // set access direction value for certain elements from pre-defined list
3384   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3385     element_info[access_direction_list[i].element].access_direction =
3386       access_direction_list[i].direction;
3387
3388   // ---------- initialize explosion content ----------------------------------
3389   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3390   {
3391     if (IS_CUSTOM_ELEMENT(i))
3392       continue;
3393
3394     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3395     {
3396       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3397
3398       element_info[i].content.e[x][y] =
3399         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3400          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3401          i == EL_PLAYER_3 ? EL_EMERALD :
3402          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3403          i == EL_MOLE ? EL_EMERALD_RED :
3404          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3405          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3406          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3407          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3408          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3409          i == EL_WALL_EMERALD ? EL_EMERALD :
3410          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3411          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3412          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3413          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3414          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3415          i == EL_WALL_PEARL ? EL_PEARL :
3416          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3417          EL_EMPTY);
3418     }
3419   }
3420
3421   // ---------- initialize recursion detection --------------------------------
3422   recursion_loop_depth = 0;
3423   recursion_loop_detected = FALSE;
3424   recursion_loop_element = EL_UNDEFINED;
3425
3426   // ---------- initialize graphics engine ------------------------------------
3427   game.scroll_delay_value =
3428     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3429      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3430      !setup.forced_scroll_delay           ? 0 :
3431      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3432   game.scroll_delay_value =
3433     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3434
3435   // ---------- initialize game engine snapshots ------------------------------
3436   for (i = 0; i < MAX_PLAYERS; i++)
3437     game.snapshot.last_action[i] = 0;
3438   game.snapshot.changed_action = FALSE;
3439   game.snapshot.collected_item = FALSE;
3440   game.snapshot.mode =
3441     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3442      SNAPSHOT_MODE_EVERY_STEP :
3443      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3444      SNAPSHOT_MODE_EVERY_MOVE :
3445      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3446      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3447   game.snapshot.save_snapshot = FALSE;
3448
3449   // ---------- initialize level time for Supaplex engine ---------------------
3450   // Supaplex levels with time limit currently unsupported -- should be added
3451   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3452     level.time = 0;
3453
3454   // ---------- initialize flags for handling game actions --------------------
3455
3456   // set flags for game actions to default values
3457   game.use_key_actions = TRUE;
3458   game.use_mouse_actions = FALSE;
3459
3460   // when using Mirror Magic game engine, handle mouse events only
3461   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3462   {
3463     game.use_key_actions = FALSE;
3464     game.use_mouse_actions = TRUE;
3465   }
3466
3467   // check for custom elements with mouse click events
3468   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3469   {
3470     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3471     {
3472       int element = EL_CUSTOM_START + i;
3473
3474       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3475           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3476           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3477           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3478         game.use_mouse_actions = TRUE;
3479     }
3480   }
3481 }
3482
3483 static int get_num_special_action(int element, int action_first,
3484                                   int action_last)
3485 {
3486   int num_special_action = 0;
3487   int i, j;
3488
3489   for (i = action_first; i <= action_last; i++)
3490   {
3491     boolean found = FALSE;
3492
3493     for (j = 0; j < NUM_DIRECTIONS; j++)
3494       if (el_act_dir2img(element, i, j) !=
3495           el_act_dir2img(element, ACTION_DEFAULT, j))
3496         found = TRUE;
3497
3498     if (found)
3499       num_special_action++;
3500     else
3501       break;
3502   }
3503
3504   return num_special_action;
3505 }
3506
3507
3508 // ============================================================================
3509 // InitGame()
3510 // ----------------------------------------------------------------------------
3511 // initialize and start new game
3512 // ============================================================================
3513
3514 #if DEBUG_INIT_PLAYER
3515 static void DebugPrintPlayerStatus(char *message)
3516 {
3517   int i;
3518
3519   if (!options.debug)
3520     return;
3521
3522   Debug("game:init:player", "%s:", message);
3523
3524   for (i = 0; i < MAX_PLAYERS; i++)
3525   {
3526     struct PlayerInfo *player = &stored_player[i];
3527
3528     Debug("game:init:player",
3529           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3530           i + 1,
3531           player->present,
3532           player->connected,
3533           player->connected_locally,
3534           player->connected_network,
3535           player->active,
3536           (local_player == player ? " (local player)" : ""));
3537   }
3538 }
3539 #endif
3540
3541 void InitGame(void)
3542 {
3543   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3544   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3545   int fade_mask = REDRAW_FIELD;
3546
3547   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3548   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3549   int initial_move_dir = MV_DOWN;
3550   int i, j, x, y;
3551
3552   // required here to update video display before fading (FIX THIS)
3553   DrawMaskedBorder(REDRAW_DOOR_2);
3554
3555   if (!game.restart_level)
3556     CloseDoor(DOOR_CLOSE_1);
3557
3558   SetGameStatus(GAME_MODE_PLAYING);
3559
3560   if (level_editor_test_game)
3561     FadeSkipNextFadeOut();
3562   else
3563     FadeSetEnterScreen();
3564
3565   if (CheckFadeAll())
3566     fade_mask = REDRAW_ALL;
3567
3568   FadeLevelSoundsAndMusic();
3569
3570   ExpireSoundLoops(TRUE);
3571
3572   FadeOut(fade_mask);
3573
3574   if (level_editor_test_game)
3575     FadeSkipNextFadeIn();
3576
3577   // needed if different viewport properties defined for playing
3578   ChangeViewportPropertiesIfNeeded();
3579
3580   ClearField();
3581
3582   DrawCompleteVideoDisplay();
3583
3584   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3585
3586   InitGameEngine();
3587   InitGameControlValues();
3588
3589   if (tape.recording)
3590   {
3591     // initialize tape actions from game when recording tape
3592     tape.use_key_actions   = game.use_key_actions;
3593     tape.use_mouse_actions = game.use_mouse_actions;
3594
3595     // initialize visible playfield size when recording tape (for team mode)
3596     tape.scr_fieldx = SCR_FIELDX;
3597     tape.scr_fieldy = SCR_FIELDY;
3598   }
3599
3600   // don't play tapes over network
3601   network_playing = (network.enabled && !tape.playing);
3602
3603   for (i = 0; i < MAX_PLAYERS; i++)
3604   {
3605     struct PlayerInfo *player = &stored_player[i];
3606
3607     player->index_nr = i;
3608     player->index_bit = (1 << i);
3609     player->element_nr = EL_PLAYER_1 + i;
3610
3611     player->present = FALSE;
3612     player->active = FALSE;
3613     player->mapped = FALSE;
3614
3615     player->killed = FALSE;
3616     player->reanimated = FALSE;
3617     player->buried = FALSE;
3618
3619     player->action = 0;
3620     player->effective_action = 0;
3621     player->programmed_action = 0;
3622     player->snap_action = 0;
3623
3624     player->mouse_action.lx = 0;
3625     player->mouse_action.ly = 0;
3626     player->mouse_action.button = 0;
3627     player->mouse_action.button_hint = 0;
3628
3629     player->effective_mouse_action.lx = 0;
3630     player->effective_mouse_action.ly = 0;
3631     player->effective_mouse_action.button = 0;
3632     player->effective_mouse_action.button_hint = 0;
3633
3634     for (j = 0; j < MAX_NUM_KEYS; j++)
3635       player->key[j] = FALSE;
3636
3637     player->num_white_keys = 0;
3638
3639     player->dynabomb_count = 0;
3640     player->dynabomb_size = 1;
3641     player->dynabombs_left = 0;
3642     player->dynabomb_xl = FALSE;
3643
3644     player->MovDir = initial_move_dir;
3645     player->MovPos = 0;
3646     player->GfxPos = 0;
3647     player->GfxDir = initial_move_dir;
3648     player->GfxAction = ACTION_DEFAULT;
3649     player->Frame = 0;
3650     player->StepFrame = 0;
3651
3652     player->initial_element = player->element_nr;
3653     player->artwork_element =
3654       (level.use_artwork_element[i] ? level.artwork_element[i] :
3655        player->element_nr);
3656     player->use_murphy = FALSE;
3657
3658     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3659     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3660
3661     player->gravity = level.initial_player_gravity[i];
3662
3663     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3664
3665     player->actual_frame_counter = 0;
3666
3667     player->step_counter = 0;
3668
3669     player->last_move_dir = initial_move_dir;
3670
3671     player->is_active = FALSE;
3672
3673     player->is_waiting = FALSE;
3674     player->is_moving = FALSE;
3675     player->is_auto_moving = FALSE;
3676     player->is_digging = FALSE;
3677     player->is_snapping = FALSE;
3678     player->is_collecting = FALSE;
3679     player->is_pushing = FALSE;
3680     player->is_switching = FALSE;
3681     player->is_dropping = FALSE;
3682     player->is_dropping_pressed = FALSE;
3683
3684     player->is_bored = FALSE;
3685     player->is_sleeping = FALSE;
3686
3687     player->was_waiting = TRUE;
3688     player->was_moving = FALSE;
3689     player->was_snapping = FALSE;
3690     player->was_dropping = FALSE;
3691
3692     player->force_dropping = FALSE;
3693
3694     player->frame_counter_bored = -1;
3695     player->frame_counter_sleeping = -1;
3696
3697     player->anim_delay_counter = 0;
3698     player->post_delay_counter = 0;
3699
3700     player->dir_waiting = initial_move_dir;
3701     player->action_waiting = ACTION_DEFAULT;
3702     player->last_action_waiting = ACTION_DEFAULT;
3703     player->special_action_bored = ACTION_DEFAULT;
3704     player->special_action_sleeping = ACTION_DEFAULT;
3705
3706     player->switch_x = -1;
3707     player->switch_y = -1;
3708
3709     player->drop_x = -1;
3710     player->drop_y = -1;
3711
3712     player->show_envelope = 0;
3713
3714     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3715
3716     player->push_delay       = -1;      // initialized when pushing starts
3717     player->push_delay_value = game.initial_push_delay_value;
3718
3719     player->drop_delay = 0;
3720     player->drop_pressed_delay = 0;
3721
3722     player->last_jx = -1;
3723     player->last_jy = -1;
3724     player->jx = -1;
3725     player->jy = -1;
3726
3727     player->shield_normal_time_left = 0;
3728     player->shield_deadly_time_left = 0;
3729
3730     player->last_removed_element = EL_UNDEFINED;
3731
3732     player->inventory_infinite_element = EL_UNDEFINED;
3733     player->inventory_size = 0;
3734
3735     if (level.use_initial_inventory[i])
3736     {
3737       for (j = 0; j < level.initial_inventory_size[i]; j++)
3738       {
3739         int element = level.initial_inventory_content[i][j];
3740         int collect_count = element_info[element].collect_count_initial;
3741         int k;
3742
3743         if (!IS_CUSTOM_ELEMENT(element))
3744           collect_count = 1;
3745
3746         if (collect_count == 0)
3747           player->inventory_infinite_element = element;
3748         else
3749           for (k = 0; k < collect_count; k++)
3750             if (player->inventory_size < MAX_INVENTORY_SIZE)
3751               player->inventory_element[player->inventory_size++] = element;
3752       }
3753     }
3754
3755     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3756     SnapField(player, 0, 0);
3757
3758     map_player_action[i] = i;
3759   }
3760
3761   network_player_action_received = FALSE;
3762
3763   // initial null action
3764   if (network_playing)
3765     SendToServer_MovePlayer(MV_NONE);
3766
3767   FrameCounter = 0;
3768   TimeFrames = 0;
3769   TimePlayed = 0;
3770   TimeLeft = level.time;
3771   TapeTime = 0;
3772
3773   ScreenMovDir = MV_NONE;
3774   ScreenMovPos = 0;
3775   ScreenGfxPos = 0;
3776
3777   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3778
3779   game.robot_wheel_x = -1;
3780   game.robot_wheel_y = -1;
3781
3782   game.exit_x = -1;
3783   game.exit_y = -1;
3784
3785   game.all_players_gone = FALSE;
3786
3787   game.LevelSolved = FALSE;
3788   game.GameOver = FALSE;
3789
3790   game.GamePlayed = !tape.playing;
3791
3792   game.LevelSolved_GameWon = FALSE;
3793   game.LevelSolved_GameEnd = FALSE;
3794   game.LevelSolved_SaveTape = FALSE;
3795   game.LevelSolved_SaveScore = FALSE;
3796
3797   game.LevelSolved_CountingTime = 0;
3798   game.LevelSolved_CountingScore = 0;
3799   game.LevelSolved_CountingHealth = 0;
3800
3801   game.panel.active = TRUE;
3802
3803   game.no_time_limit = (level.time == 0);
3804
3805   game.yamyam_content_nr = 0;
3806   game.robot_wheel_active = FALSE;
3807   game.magic_wall_active = FALSE;
3808   game.magic_wall_time_left = 0;
3809   game.light_time_left = 0;
3810   game.timegate_time_left = 0;
3811   game.switchgate_pos = 0;
3812   game.wind_direction = level.wind_direction_initial;
3813
3814   game.time_final = 0;
3815   game.score_time_final = 0;
3816
3817   game.score = 0;
3818   game.score_final = 0;
3819
3820   game.health = MAX_HEALTH;
3821   game.health_final = MAX_HEALTH;
3822
3823   game.gems_still_needed = level.gems_needed;
3824   game.sokoban_fields_still_needed = 0;
3825   game.sokoban_objects_still_needed = 0;
3826   game.lights_still_needed = 0;
3827   game.players_still_needed = 0;
3828   game.friends_still_needed = 0;
3829
3830   game.lenses_time_left = 0;
3831   game.magnify_time_left = 0;
3832
3833   game.ball_active = level.ball_active_initial;
3834   game.ball_content_nr = 0;
3835
3836   game.explosions_delayed = TRUE;
3837
3838   game.envelope_active = FALSE;
3839
3840   for (i = 0; i < NUM_BELTS; i++)
3841   {
3842     game.belt_dir[i] = MV_NONE;
3843     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3844   }
3845
3846   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3847     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3848
3849 #if DEBUG_INIT_PLAYER
3850   DebugPrintPlayerStatus("Player status at level initialization");
3851 #endif
3852
3853   SCAN_PLAYFIELD(x, y)
3854   {
3855     Tile[x][y] = Last[x][y] = level.field[x][y];
3856     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3857     ChangeDelay[x][y] = 0;
3858     ChangePage[x][y] = -1;
3859     CustomValue[x][y] = 0;              // initialized in InitField()
3860     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3861     AmoebaNr[x][y] = 0;
3862     WasJustMoving[x][y] = 0;
3863     WasJustFalling[x][y] = 0;
3864     CheckCollision[x][y] = 0;
3865     CheckImpact[x][y] = 0;
3866     Stop[x][y] = FALSE;
3867     Pushed[x][y] = FALSE;
3868
3869     ChangeCount[x][y] = 0;
3870     ChangeEvent[x][y] = -1;
3871
3872     ExplodePhase[x][y] = 0;
3873     ExplodeDelay[x][y] = 0;
3874     ExplodeField[x][y] = EX_TYPE_NONE;
3875
3876     RunnerVisit[x][y] = 0;
3877     PlayerVisit[x][y] = 0;
3878
3879     GfxFrame[x][y] = 0;
3880     GfxRandom[x][y] = INIT_GFX_RANDOM();
3881     GfxElement[x][y] = EL_UNDEFINED;
3882     GfxAction[x][y] = ACTION_DEFAULT;
3883     GfxDir[x][y] = MV_NONE;
3884     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3885   }
3886
3887   SCAN_PLAYFIELD(x, y)
3888   {
3889     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3890       emulate_bd = FALSE;
3891     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3892       emulate_sp = FALSE;
3893
3894     InitField(x, y, TRUE);
3895
3896     ResetGfxAnimation(x, y);
3897   }
3898
3899   InitBeltMovement();
3900
3901   for (i = 0; i < MAX_PLAYERS; i++)
3902   {
3903     struct PlayerInfo *player = &stored_player[i];
3904
3905     // set number of special actions for bored and sleeping animation
3906     player->num_special_action_bored =
3907       get_num_special_action(player->artwork_element,
3908                              ACTION_BORING_1, ACTION_BORING_LAST);
3909     player->num_special_action_sleeping =
3910       get_num_special_action(player->artwork_element,
3911                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3912   }
3913
3914   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3915                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3916
3917   // initialize type of slippery elements
3918   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3919   {
3920     if (!IS_CUSTOM_ELEMENT(i))
3921     {
3922       // default: elements slip down either to the left or right randomly
3923       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3924
3925       // SP style elements prefer to slip down on the left side
3926       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3927         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3928
3929       // BD style elements prefer to slip down on the left side
3930       if (game.emulation == EMU_BOULDERDASH)
3931         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3932     }
3933   }
3934
3935   // initialize explosion and ignition delay
3936   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3937   {
3938     if (!IS_CUSTOM_ELEMENT(i))
3939     {
3940       int num_phase = 8;
3941       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3942                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3943                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3944       int last_phase = (num_phase + 1) * delay;
3945       int half_phase = (num_phase / 2) * delay;
3946
3947       element_info[i].explosion_delay = last_phase - 1;
3948       element_info[i].ignition_delay = half_phase;
3949
3950       if (i == EL_BLACK_ORB)
3951         element_info[i].ignition_delay = 1;
3952     }
3953   }
3954
3955   // correct non-moving belts to start moving left
3956   for (i = 0; i < NUM_BELTS; i++)
3957     if (game.belt_dir[i] == MV_NONE)
3958       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3959
3960 #if USE_NEW_PLAYER_ASSIGNMENTS
3961   // use preferred player also in local single-player mode
3962   if (!network.enabled && !game.team_mode)
3963   {
3964     int new_index_nr = setup.network_player_nr;
3965
3966     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3967     {
3968       for (i = 0; i < MAX_PLAYERS; i++)
3969         stored_player[i].connected_locally = FALSE;
3970
3971       stored_player[new_index_nr].connected_locally = TRUE;
3972     }
3973   }
3974
3975   for (i = 0; i < MAX_PLAYERS; i++)
3976   {
3977     stored_player[i].connected = FALSE;
3978
3979     // in network game mode, the local player might not be the first player
3980     if (stored_player[i].connected_locally)
3981       local_player = &stored_player[i];
3982   }
3983
3984   if (!network.enabled)
3985     local_player->connected = TRUE;
3986
3987   if (tape.playing)
3988   {
3989     for (i = 0; i < MAX_PLAYERS; i++)
3990       stored_player[i].connected = tape.player_participates[i];
3991   }
3992   else if (network.enabled)
3993   {
3994     // add team mode players connected over the network (needed for correct
3995     // assignment of player figures from level to locally playing players)
3996
3997     for (i = 0; i < MAX_PLAYERS; i++)
3998       if (stored_player[i].connected_network)
3999         stored_player[i].connected = TRUE;
4000   }
4001   else if (game.team_mode)
4002   {
4003     // try to guess locally connected team mode players (needed for correct
4004     // assignment of player figures from level to locally playing players)
4005
4006     for (i = 0; i < MAX_PLAYERS; i++)
4007       if (setup.input[i].use_joystick ||
4008           setup.input[i].key.left != KSYM_UNDEFINED)
4009         stored_player[i].connected = TRUE;
4010   }
4011
4012 #if DEBUG_INIT_PLAYER
4013   DebugPrintPlayerStatus("Player status after level initialization");
4014 #endif
4015
4016 #if DEBUG_INIT_PLAYER
4017   Debug("game:init:player", "Reassigning players ...");
4018 #endif
4019
4020   // check if any connected player was not found in playfield
4021   for (i = 0; i < MAX_PLAYERS; i++)
4022   {
4023     struct PlayerInfo *player = &stored_player[i];
4024
4025     if (player->connected && !player->present)
4026     {
4027       struct PlayerInfo *field_player = NULL;
4028
4029 #if DEBUG_INIT_PLAYER
4030       Debug("game:init:player",
4031             "- looking for field player for player %d ...", i + 1);
4032 #endif
4033
4034       // assign first free player found that is present in the playfield
4035
4036       // first try: look for unmapped playfield player that is not connected
4037       for (j = 0; j < MAX_PLAYERS; j++)
4038         if (field_player == NULL &&
4039             stored_player[j].present &&
4040             !stored_player[j].mapped &&
4041             !stored_player[j].connected)
4042           field_player = &stored_player[j];
4043
4044       // second try: look for *any* unmapped playfield player
4045       for (j = 0; j < MAX_PLAYERS; j++)
4046         if (field_player == NULL &&
4047             stored_player[j].present &&
4048             !stored_player[j].mapped)
4049           field_player = &stored_player[j];
4050
4051       if (field_player != NULL)
4052       {
4053         int jx = field_player->jx, jy = field_player->jy;
4054
4055 #if DEBUG_INIT_PLAYER
4056         Debug("game:init:player", "- found player %d",
4057               field_player->index_nr + 1);
4058 #endif
4059
4060         player->present = FALSE;
4061         player->active = FALSE;
4062
4063         field_player->present = TRUE;
4064         field_player->active = TRUE;
4065
4066         /*
4067         player->initial_element = field_player->initial_element;
4068         player->artwork_element = field_player->artwork_element;
4069
4070         player->block_last_field       = field_player->block_last_field;
4071         player->block_delay_adjustment = field_player->block_delay_adjustment;
4072         */
4073
4074         StorePlayer[jx][jy] = field_player->element_nr;
4075
4076         field_player->jx = field_player->last_jx = jx;
4077         field_player->jy = field_player->last_jy = jy;
4078
4079         if (local_player == player)
4080           local_player = field_player;
4081
4082         map_player_action[field_player->index_nr] = i;
4083
4084         field_player->mapped = TRUE;
4085
4086 #if DEBUG_INIT_PLAYER
4087         Debug("game:init:player", "- map_player_action[%d] == %d",
4088               field_player->index_nr + 1, i + 1);
4089 #endif
4090       }
4091     }
4092
4093     if (player->connected && player->present)
4094       player->mapped = TRUE;
4095   }
4096
4097 #if DEBUG_INIT_PLAYER
4098   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4099 #endif
4100
4101 #else
4102
4103   // check if any connected player was not found in playfield
4104   for (i = 0; i < MAX_PLAYERS; i++)
4105   {
4106     struct PlayerInfo *player = &stored_player[i];
4107
4108     if (player->connected && !player->present)
4109     {
4110       for (j = 0; j < MAX_PLAYERS; j++)
4111       {
4112         struct PlayerInfo *field_player = &stored_player[j];
4113         int jx = field_player->jx, jy = field_player->jy;
4114
4115         // assign first free player found that is present in the playfield
4116         if (field_player->present && !field_player->connected)
4117         {
4118           player->present = TRUE;
4119           player->active = TRUE;
4120
4121           field_player->present = FALSE;
4122           field_player->active = FALSE;
4123
4124           player->initial_element = field_player->initial_element;
4125           player->artwork_element = field_player->artwork_element;
4126
4127           player->block_last_field       = field_player->block_last_field;
4128           player->block_delay_adjustment = field_player->block_delay_adjustment;
4129
4130           StorePlayer[jx][jy] = player->element_nr;
4131
4132           player->jx = player->last_jx = jx;
4133           player->jy = player->last_jy = jy;
4134
4135           break;
4136         }
4137       }
4138     }
4139   }
4140 #endif
4141
4142 #if 0
4143   Debug("game:init:player", "local_player->present == %d",
4144         local_player->present);
4145 #endif
4146
4147   // set focus to local player for network games, else to all players
4148   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4149   game.centered_player_nr_next = game.centered_player_nr;
4150   game.set_centered_player = FALSE;
4151   game.set_centered_player_wrap = FALSE;
4152
4153   if (network_playing && tape.recording)
4154   {
4155     // store client dependent player focus when recording network games
4156     tape.centered_player_nr_next = game.centered_player_nr_next;
4157     tape.set_centered_player = TRUE;
4158   }
4159
4160   if (tape.playing)
4161   {
4162     // when playing a tape, eliminate all players who do not participate
4163
4164 #if USE_NEW_PLAYER_ASSIGNMENTS
4165
4166     if (!game.team_mode)
4167     {
4168       for (i = 0; i < MAX_PLAYERS; i++)
4169       {
4170         if (stored_player[i].active &&
4171             !tape.player_participates[map_player_action[i]])
4172         {
4173           struct PlayerInfo *player = &stored_player[i];
4174           int jx = player->jx, jy = player->jy;
4175
4176 #if DEBUG_INIT_PLAYER
4177           Debug("game:init:player", "Removing player %d at (%d, %d)",
4178                 i + 1, jx, jy);
4179 #endif
4180
4181           player->active = FALSE;
4182           StorePlayer[jx][jy] = 0;
4183           Tile[jx][jy] = EL_EMPTY;
4184         }
4185       }
4186     }
4187
4188 #else
4189
4190     for (i = 0; i < MAX_PLAYERS; i++)
4191     {
4192       if (stored_player[i].active &&
4193           !tape.player_participates[i])
4194       {
4195         struct PlayerInfo *player = &stored_player[i];
4196         int jx = player->jx, jy = player->jy;
4197
4198         player->active = FALSE;
4199         StorePlayer[jx][jy] = 0;
4200         Tile[jx][jy] = EL_EMPTY;
4201       }
4202     }
4203 #endif
4204   }
4205   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4206   {
4207     // when in single player mode, eliminate all but the local player
4208
4209     for (i = 0; i < MAX_PLAYERS; i++)
4210     {
4211       struct PlayerInfo *player = &stored_player[i];
4212
4213       if (player->active && player != local_player)
4214       {
4215         int jx = player->jx, jy = player->jy;
4216
4217         player->active = FALSE;
4218         player->present = FALSE;
4219
4220         StorePlayer[jx][jy] = 0;
4221         Tile[jx][jy] = EL_EMPTY;
4222       }
4223     }
4224   }
4225
4226   for (i = 0; i < MAX_PLAYERS; i++)
4227     if (stored_player[i].active)
4228       game.players_still_needed++;
4229
4230   if (level.solved_by_one_player)
4231     game.players_still_needed = 1;
4232
4233   // when recording the game, store which players take part in the game
4234   if (tape.recording)
4235   {
4236 #if USE_NEW_PLAYER_ASSIGNMENTS
4237     for (i = 0; i < MAX_PLAYERS; i++)
4238       if (stored_player[i].connected)
4239         tape.player_participates[i] = TRUE;
4240 #else
4241     for (i = 0; i < MAX_PLAYERS; i++)
4242       if (stored_player[i].active)
4243         tape.player_participates[i] = TRUE;
4244 #endif
4245   }
4246
4247 #if DEBUG_INIT_PLAYER
4248   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4249 #endif
4250
4251   if (BorderElement == EL_EMPTY)
4252   {
4253     SBX_Left = 0;
4254     SBX_Right = lev_fieldx - SCR_FIELDX;
4255     SBY_Upper = 0;
4256     SBY_Lower = lev_fieldy - SCR_FIELDY;
4257   }
4258   else
4259   {
4260     SBX_Left = -1;
4261     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4262     SBY_Upper = -1;
4263     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4264   }
4265
4266   if (full_lev_fieldx <= SCR_FIELDX)
4267     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4268   if (full_lev_fieldy <= SCR_FIELDY)
4269     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4270
4271   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4272     SBX_Left--;
4273   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4274     SBY_Upper--;
4275
4276   // if local player not found, look for custom element that might create
4277   // the player (make some assumptions about the right custom element)
4278   if (!local_player->present)
4279   {
4280     int start_x = 0, start_y = 0;
4281     int found_rating = 0;
4282     int found_element = EL_UNDEFINED;
4283     int player_nr = local_player->index_nr;
4284
4285     SCAN_PLAYFIELD(x, y)
4286     {
4287       int element = Tile[x][y];
4288       int content;
4289       int xx, yy;
4290       boolean is_player;
4291
4292       if (level.use_start_element[player_nr] &&
4293           level.start_element[player_nr] == element &&
4294           found_rating < 4)
4295       {
4296         start_x = x;
4297         start_y = y;
4298
4299         found_rating = 4;
4300         found_element = element;
4301       }
4302
4303       if (!IS_CUSTOM_ELEMENT(element))
4304         continue;
4305
4306       if (CAN_CHANGE(element))
4307       {
4308         for (i = 0; i < element_info[element].num_change_pages; i++)
4309         {
4310           // check for player created from custom element as single target
4311           content = element_info[element].change_page[i].target_element;
4312           is_player = IS_PLAYER_ELEMENT(content);
4313
4314           if (is_player && (found_rating < 3 ||
4315                             (found_rating == 3 && element < found_element)))
4316           {
4317             start_x = x;
4318             start_y = y;
4319
4320             found_rating = 3;
4321             found_element = element;
4322           }
4323         }
4324       }
4325
4326       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4327       {
4328         // check for player created from custom element as explosion content
4329         content = element_info[element].content.e[xx][yy];
4330         is_player = IS_PLAYER_ELEMENT(content);
4331
4332         if (is_player && (found_rating < 2 ||
4333                           (found_rating == 2 && element < found_element)))
4334         {
4335           start_x = x + xx - 1;
4336           start_y = y + yy - 1;
4337
4338           found_rating = 2;
4339           found_element = element;
4340         }
4341
4342         if (!CAN_CHANGE(element))
4343           continue;
4344
4345         for (i = 0; i < element_info[element].num_change_pages; i++)
4346         {
4347           // check for player created from custom element as extended target
4348           content =
4349             element_info[element].change_page[i].target_content.e[xx][yy];
4350
4351           is_player = IS_PLAYER_ELEMENT(content);
4352
4353           if (is_player && (found_rating < 1 ||
4354                             (found_rating == 1 && element < found_element)))
4355           {
4356             start_x = x + xx - 1;
4357             start_y = y + yy - 1;
4358
4359             found_rating = 1;
4360             found_element = element;
4361           }
4362         }
4363       }
4364     }
4365
4366     scroll_x = SCROLL_POSITION_X(start_x);
4367     scroll_y = SCROLL_POSITION_Y(start_y);
4368   }
4369   else
4370   {
4371     scroll_x = SCROLL_POSITION_X(local_player->jx);
4372     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4373   }
4374
4375   // !!! FIX THIS (START) !!!
4376   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4377   {
4378     InitGameEngine_EM();
4379   }
4380   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4381   {
4382     InitGameEngine_SP();
4383   }
4384   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4385   {
4386     InitGameEngine_MM();
4387   }
4388   else
4389   {
4390     DrawLevel(REDRAW_FIELD);
4391     DrawAllPlayers();
4392
4393     // after drawing the level, correct some elements
4394     if (game.timegate_time_left == 0)
4395       CloseAllOpenTimegates();
4396   }
4397
4398   // blit playfield from scroll buffer to normal back buffer for fading in
4399   BlitScreenToBitmap(backbuffer);
4400   // !!! FIX THIS (END) !!!
4401
4402   DrawMaskedBorder(fade_mask);
4403
4404   FadeIn(fade_mask);
4405
4406 #if 1
4407   // full screen redraw is required at this point in the following cases:
4408   // - special editor door undrawn when game was started from level editor
4409   // - drawing area (playfield) was changed and has to be removed completely
4410   redraw_mask = REDRAW_ALL;
4411   BackToFront();
4412 #endif
4413
4414   if (!game.restart_level)
4415   {
4416     // copy default game door content to main double buffer
4417
4418     // !!! CHECK AGAIN !!!
4419     SetPanelBackground();
4420     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4421     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4422   }
4423
4424   SetPanelBackground();
4425   SetDrawBackgroundMask(REDRAW_DOOR_1);
4426
4427   UpdateAndDisplayGameControlValues();
4428
4429   if (!game.restart_level)
4430   {
4431     UnmapGameButtons();
4432     UnmapTapeButtons();
4433
4434     FreeGameButtons();
4435     CreateGameButtons();
4436
4437     MapGameButtons();
4438     MapTapeButtons();
4439
4440     // copy actual game door content to door double buffer for OpenDoor()
4441     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4442
4443     OpenDoor(DOOR_OPEN_ALL);
4444
4445     KeyboardAutoRepeatOffUnlessAutoplay();
4446
4447 #if DEBUG_INIT_PLAYER
4448     DebugPrintPlayerStatus("Player status (final)");
4449 #endif
4450   }
4451
4452   UnmapAllGadgets();
4453
4454   MapGameButtons();
4455   MapTapeButtons();
4456
4457   if (!game.restart_level && !tape.playing)
4458   {
4459     LevelStats_incPlayed(level_nr);
4460
4461     SaveLevelSetup_SeriesInfo();
4462   }
4463
4464   game.restart_level = FALSE;
4465   game.restart_game_message = NULL;
4466
4467   game.request_active = FALSE;
4468   game.request_active_or_moving = FALSE;
4469
4470   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4471     InitGameActions_MM();
4472
4473   SaveEngineSnapshotToListInitial();
4474
4475   if (!game.restart_level)
4476   {
4477     PlaySound(SND_GAME_STARTING);
4478
4479     if (setup.sound_music)
4480       PlayLevelMusic();
4481   }
4482
4483   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4484 }
4485
4486 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4487                         int actual_player_x, int actual_player_y)
4488 {
4489   // this is used for non-R'n'D game engines to update certain engine values
4490
4491   // needed to determine if sounds are played within the visible screen area
4492   scroll_x = actual_scroll_x;
4493   scroll_y = actual_scroll_y;
4494
4495   // needed to get player position for "follow finger" playing input method
4496   local_player->jx = actual_player_x;
4497   local_player->jy = actual_player_y;
4498 }
4499
4500 void InitMovDir(int x, int y)
4501 {
4502   int i, element = Tile[x][y];
4503   static int xy[4][2] =
4504   {
4505     {  0, +1 },
4506     { +1,  0 },
4507     {  0, -1 },
4508     { -1,  0 }
4509   };
4510   static int direction[3][4] =
4511   {
4512     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4513     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4514     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4515   };
4516
4517   switch (element)
4518   {
4519     case EL_BUG_RIGHT:
4520     case EL_BUG_UP:
4521     case EL_BUG_LEFT:
4522     case EL_BUG_DOWN:
4523       Tile[x][y] = EL_BUG;
4524       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4525       break;
4526
4527     case EL_SPACESHIP_RIGHT:
4528     case EL_SPACESHIP_UP:
4529     case EL_SPACESHIP_LEFT:
4530     case EL_SPACESHIP_DOWN:
4531       Tile[x][y] = EL_SPACESHIP;
4532       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4533       break;
4534
4535     case EL_BD_BUTTERFLY_RIGHT:
4536     case EL_BD_BUTTERFLY_UP:
4537     case EL_BD_BUTTERFLY_LEFT:
4538     case EL_BD_BUTTERFLY_DOWN:
4539       Tile[x][y] = EL_BD_BUTTERFLY;
4540       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4541       break;
4542
4543     case EL_BD_FIREFLY_RIGHT:
4544     case EL_BD_FIREFLY_UP:
4545     case EL_BD_FIREFLY_LEFT:
4546     case EL_BD_FIREFLY_DOWN:
4547       Tile[x][y] = EL_BD_FIREFLY;
4548       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4549       break;
4550
4551     case EL_PACMAN_RIGHT:
4552     case EL_PACMAN_UP:
4553     case EL_PACMAN_LEFT:
4554     case EL_PACMAN_DOWN:
4555       Tile[x][y] = EL_PACMAN;
4556       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4557       break;
4558
4559     case EL_YAMYAM_LEFT:
4560     case EL_YAMYAM_RIGHT:
4561     case EL_YAMYAM_UP:
4562     case EL_YAMYAM_DOWN:
4563       Tile[x][y] = EL_YAMYAM;
4564       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4565       break;
4566
4567     case EL_SP_SNIKSNAK:
4568       MovDir[x][y] = MV_UP;
4569       break;
4570
4571     case EL_SP_ELECTRON:
4572       MovDir[x][y] = MV_LEFT;
4573       break;
4574
4575     case EL_MOLE_LEFT:
4576     case EL_MOLE_RIGHT:
4577     case EL_MOLE_UP:
4578     case EL_MOLE_DOWN:
4579       Tile[x][y] = EL_MOLE;
4580       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4581       break;
4582
4583     case EL_SPRING_LEFT:
4584     case EL_SPRING_RIGHT:
4585       Tile[x][y] = EL_SPRING;
4586       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4587       break;
4588
4589     default:
4590       if (IS_CUSTOM_ELEMENT(element))
4591       {
4592         struct ElementInfo *ei = &element_info[element];
4593         int move_direction_initial = ei->move_direction_initial;
4594         int move_pattern = ei->move_pattern;
4595
4596         if (move_direction_initial == MV_START_PREVIOUS)
4597         {
4598           if (MovDir[x][y] != MV_NONE)
4599             return;
4600
4601           move_direction_initial = MV_START_AUTOMATIC;
4602         }
4603
4604         if (move_direction_initial == MV_START_RANDOM)
4605           MovDir[x][y] = 1 << RND(4);
4606         else if (move_direction_initial & MV_ANY_DIRECTION)
4607           MovDir[x][y] = move_direction_initial;
4608         else if (move_pattern == MV_ALL_DIRECTIONS ||
4609                  move_pattern == MV_TURNING_LEFT ||
4610                  move_pattern == MV_TURNING_RIGHT ||
4611                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4612                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4613                  move_pattern == MV_TURNING_RANDOM)
4614           MovDir[x][y] = 1 << RND(4);
4615         else if (move_pattern == MV_HORIZONTAL)
4616           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4617         else if (move_pattern == MV_VERTICAL)
4618           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4619         else if (move_pattern & MV_ANY_DIRECTION)
4620           MovDir[x][y] = element_info[element].move_pattern;
4621         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4622                  move_pattern == MV_ALONG_RIGHT_SIDE)
4623         {
4624           // use random direction as default start direction
4625           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4626             MovDir[x][y] = 1 << RND(4);
4627
4628           for (i = 0; i < NUM_DIRECTIONS; i++)
4629           {
4630             int x1 = x + xy[i][0];
4631             int y1 = y + xy[i][1];
4632
4633             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4634             {
4635               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4636                 MovDir[x][y] = direction[0][i];
4637               else
4638                 MovDir[x][y] = direction[1][i];
4639
4640               break;
4641             }
4642           }
4643         }                
4644       }
4645       else
4646       {
4647         MovDir[x][y] = 1 << RND(4);
4648
4649         if (element != EL_BUG &&
4650             element != EL_SPACESHIP &&
4651             element != EL_BD_BUTTERFLY &&
4652             element != EL_BD_FIREFLY)
4653           break;
4654
4655         for (i = 0; i < NUM_DIRECTIONS; i++)
4656         {
4657           int x1 = x + xy[i][0];
4658           int y1 = y + xy[i][1];
4659
4660           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4661           {
4662             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4663             {
4664               MovDir[x][y] = direction[0][i];
4665               break;
4666             }
4667             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4668                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4669             {
4670               MovDir[x][y] = direction[1][i];
4671               break;
4672             }
4673           }
4674         }
4675       }
4676       break;
4677   }
4678
4679   GfxDir[x][y] = MovDir[x][y];
4680 }
4681
4682 void InitAmoebaNr(int x, int y)
4683 {
4684   int i;
4685   int group_nr = AmoebaNeighbourNr(x, y);
4686
4687   if (group_nr == 0)
4688   {
4689     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4690     {
4691       if (AmoebaCnt[i] == 0)
4692       {
4693         group_nr = i;
4694         break;
4695       }
4696     }
4697   }
4698
4699   AmoebaNr[x][y] = group_nr;
4700   AmoebaCnt[group_nr]++;
4701   AmoebaCnt2[group_nr]++;
4702 }
4703
4704 static void LevelSolved_SetFinalGameValues(void)
4705 {
4706   game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4707   game.score_time_final = (level.use_step_counter ? TimePlayed :
4708                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4709
4710   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4711                       game_em.lev->score :
4712                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4713                       game_mm.score :
4714                       game.score);
4715
4716   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4717                        MM_HEALTH(game_mm.laser_overload_value) :
4718                        game.health);
4719
4720   game.LevelSolved_CountingTime = game.time_final;
4721   game.LevelSolved_CountingScore = game.score_final;
4722   game.LevelSolved_CountingHealth = game.health_final;
4723 }
4724
4725 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4726 {
4727   game.LevelSolved_CountingTime = time;
4728   game.LevelSolved_CountingScore = score;
4729   game.LevelSolved_CountingHealth = health;
4730
4731   game_panel_controls[GAME_PANEL_TIME].value = time;
4732   game_panel_controls[GAME_PANEL_SCORE].value = score;
4733   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4734
4735   DisplayGameControlValues();
4736 }
4737
4738 static void LevelSolved(void)
4739 {
4740   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4741       game.players_still_needed > 0)
4742     return;
4743
4744   game.LevelSolved = TRUE;
4745   game.GameOver = TRUE;
4746
4747   // needed here to display correct panel values while player walks into exit
4748   LevelSolved_SetFinalGameValues();
4749 }
4750
4751 void GameWon(void)
4752 {
4753   static int time_count_steps;
4754   static int time, time_final;
4755   static float score, score_final; // needed for time score < 10 for 10 seconds
4756   static int health, health_final;
4757   static int game_over_delay_1 = 0;
4758   static int game_over_delay_2 = 0;
4759   static int game_over_delay_3 = 0;
4760   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4761   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4762
4763   if (!game.LevelSolved_GameWon)
4764   {
4765     int i;
4766
4767     // do not start end game actions before the player stops moving (to exit)
4768     if (local_player->active && local_player->MovPos)
4769       return;
4770
4771     // calculate final game values after player finished walking into exit
4772     LevelSolved_SetFinalGameValues();
4773
4774     game.LevelSolved_GameWon = TRUE;
4775     game.LevelSolved_SaveTape = tape.recording;
4776     game.LevelSolved_SaveScore = !tape.playing;
4777
4778     if (!tape.playing)
4779     {
4780       LevelStats_incSolved(level_nr);
4781
4782       SaveLevelSetup_SeriesInfo();
4783     }
4784
4785     if (tape.auto_play)         // tape might already be stopped here
4786       tape.auto_play_level_solved = TRUE;
4787
4788     TapeStop();
4789
4790     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4791     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4792     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4793
4794     time = time_final = game.time_final;
4795     score = score_final = game.score_final;
4796     health = health_final = game.health_final;
4797
4798     // update game panel values before (delayed) counting of score (if any)
4799     LevelSolved_DisplayFinalGameValues(time, score, health);
4800
4801     // if level has time score defined, calculate new final game values
4802     if (time_score > 0)
4803     {
4804       int time_final_max = 999;
4805       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4806       int time_frames = 0;
4807       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4808       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4809
4810       if (TimeLeft > 0)
4811       {
4812         time_final = 0;
4813         time_frames = time_frames_left;
4814       }
4815       else if (game.no_time_limit && TimePlayed < time_final_max)
4816       {
4817         time_final = time_final_max;
4818         time_frames = time_frames_final_max - time_frames_played;
4819       }
4820
4821       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4822
4823       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4824
4825       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4826       {
4827         health_final = 0;
4828         score_final += health * time_score;
4829       }
4830
4831       game.score_final = score_final;
4832       game.health_final = health_final;
4833     }
4834
4835     // if not counting score after game, immediately update game panel values
4836     if (level_editor_test_game || !setup.count_score_after_game)
4837     {
4838       time = time_final;
4839       score = score_final;
4840
4841       LevelSolved_DisplayFinalGameValues(time, score, health);
4842     }
4843
4844     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4845     {
4846       // check if last player has left the level
4847       if (game.exit_x >= 0 &&
4848           game.exit_y >= 0)
4849       {
4850         int x = game.exit_x;
4851         int y = game.exit_y;
4852         int element = Tile[x][y];
4853
4854         // close exit door after last player
4855         if ((game.all_players_gone &&
4856              (element == EL_EXIT_OPEN ||
4857               element == EL_SP_EXIT_OPEN ||
4858               element == EL_STEEL_EXIT_OPEN)) ||
4859             element == EL_EM_EXIT_OPEN ||
4860             element == EL_EM_STEEL_EXIT_OPEN)
4861         {
4862
4863           Tile[x][y] =
4864             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4865              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4866              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4867              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4868              EL_EM_STEEL_EXIT_CLOSING);
4869
4870           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4871         }
4872
4873         // player disappears
4874         DrawLevelField(x, y);
4875       }
4876
4877       for (i = 0; i < MAX_PLAYERS; i++)
4878       {
4879         struct PlayerInfo *player = &stored_player[i];
4880
4881         if (player->present)
4882         {
4883           RemovePlayer(player);
4884
4885           // player disappears
4886           DrawLevelField(player->jx, player->jy);
4887         }
4888       }
4889     }
4890
4891     PlaySound(SND_GAME_WINNING);
4892   }
4893
4894   if (setup.count_score_after_game)
4895   {
4896     if (time != time_final)
4897     {
4898       if (game_over_delay_1 > 0)
4899       {
4900         game_over_delay_1--;
4901
4902         return;
4903       }
4904
4905       int time_to_go = ABS(time_final - time);
4906       int time_count_dir = (time < time_final ? +1 : -1);
4907
4908       if (time_to_go < time_count_steps)
4909         time_count_steps = 1;
4910
4911       time  += time_count_steps * time_count_dir;
4912       score += time_count_steps * time_score;
4913
4914       // set final score to correct rounding differences after counting score
4915       if (time == time_final)
4916         score = score_final;
4917
4918       LevelSolved_DisplayFinalGameValues(time, score, health);
4919
4920       if (time == time_final)
4921         StopSound(SND_GAME_LEVELTIME_BONUS);
4922       else if (setup.sound_loops)
4923         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4924       else
4925         PlaySound(SND_GAME_LEVELTIME_BONUS);
4926
4927       return;
4928     }
4929
4930     if (health != health_final)
4931     {
4932       if (game_over_delay_2 > 0)
4933       {
4934         game_over_delay_2--;
4935
4936         return;
4937       }
4938
4939       int health_count_dir = (health < health_final ? +1 : -1);
4940
4941       health += health_count_dir;
4942       score  += time_score;
4943
4944       LevelSolved_DisplayFinalGameValues(time, score, health);
4945
4946       if (health == health_final)
4947         StopSound(SND_GAME_LEVELTIME_BONUS);
4948       else if (setup.sound_loops)
4949         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4950       else
4951         PlaySound(SND_GAME_LEVELTIME_BONUS);
4952
4953       return;
4954     }
4955   }
4956
4957   game.panel.active = FALSE;
4958
4959   if (game_over_delay_3 > 0)
4960   {
4961     game_over_delay_3--;
4962
4963     return;
4964   }
4965
4966   GameEnd();
4967 }
4968
4969 void GameEnd(void)
4970 {
4971   // used instead of "level_nr" (needed for network games)
4972   int last_level_nr = levelset.level_nr;
4973   boolean tape_saved = FALSE;
4974
4975   game.LevelSolved_GameEnd = TRUE;
4976
4977   if (game.LevelSolved_SaveTape)
4978   {
4979     // make sure that request dialog to save tape does not open door again
4980     if (!global.use_envelope_request)
4981       CloseDoor(DOOR_CLOSE_1);
4982
4983     // ask to save tape
4984     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
4985
4986     // set unique basename for score tape (also saved in high score table)
4987     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
4988   }
4989
4990   // if no tape is to be saved, close both doors simultaneously
4991   CloseDoor(DOOR_CLOSE_ALL);
4992
4993   if (level_editor_test_game)
4994   {
4995     SetGameStatus(GAME_MODE_MAIN);
4996
4997     DrawMainMenu();
4998
4999     return;
5000   }
5001
5002   if (!game.LevelSolved_SaveScore)
5003   {
5004     SetGameStatus(GAME_MODE_MAIN);
5005
5006     DrawMainMenu();
5007
5008     return;
5009   }
5010
5011   if (level_nr == leveldir_current->handicap_level)
5012   {
5013     leveldir_current->handicap_level++;
5014
5015     SaveLevelSetup_SeriesInfo();
5016   }
5017
5018   // save score and score tape before potentially erasing tape below
5019   NewHighScore(last_level_nr, tape_saved);
5020
5021   if (setup.increment_levels &&
5022       level_nr < leveldir_current->last_level &&
5023       !network_playing)
5024   {
5025     level_nr++;         // advance to next level
5026     TapeErase();        // start with empty tape
5027
5028     if (setup.auto_play_next_level)
5029     {
5030       LoadLevel(level_nr);
5031
5032       SaveLevelSetup_SeriesInfo();
5033     }
5034   }
5035
5036   if (scores.last_added >= 0 && setup.show_scores_after_game)
5037   {
5038     SetGameStatus(GAME_MODE_SCORES);
5039
5040     DrawHallOfFame(last_level_nr);
5041   }
5042   else if (setup.auto_play_next_level && setup.increment_levels &&
5043            last_level_nr < leveldir_current->last_level &&
5044            !network_playing)
5045   {
5046     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5047   }
5048   else
5049   {
5050     SetGameStatus(GAME_MODE_MAIN);
5051
5052     DrawMainMenu();
5053   }
5054 }
5055
5056 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5057                          boolean one_score_entry_per_name)
5058 {
5059   int i;
5060
5061   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5062     return -1;
5063
5064   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5065   {
5066     struct ScoreEntry *entry = &list->entry[i];
5067     boolean score_is_better = (new_entry->score >  entry->score);
5068     boolean score_is_equal  = (new_entry->score == entry->score);
5069     boolean time_is_better  = (new_entry->time  <  entry->time);
5070     boolean time_is_equal   = (new_entry->time  == entry->time);
5071     boolean better_by_score = (score_is_better ||
5072                                (score_is_equal && time_is_better));
5073     boolean better_by_time  = (time_is_better ||
5074                                (time_is_equal && score_is_better));
5075     boolean is_better = (level.rate_time_over_score ? better_by_time :
5076                          better_by_score);
5077     boolean entry_is_empty = (entry->score == 0 &&
5078                               entry->time == 0);
5079
5080     // prevent adding server score entries if also existing in local score file
5081     // (special case: historic score entries have an empty tape basename entry)
5082     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5083         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5084       return -1;
5085
5086     if (is_better || entry_is_empty)
5087     {
5088       // player has made it to the hall of fame
5089
5090       if (i < MAX_SCORE_ENTRIES - 1)
5091       {
5092         int m = MAX_SCORE_ENTRIES - 1;
5093         int l;
5094
5095         if (one_score_entry_per_name)
5096         {
5097           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5098             if (strEqual(list->entry[l].name, new_entry->name))
5099               m = l;
5100
5101           if (m == i)   // player's new highscore overwrites his old one
5102             goto put_into_list;
5103         }
5104
5105         for (l = m; l > i; l--)
5106           list->entry[l] = list->entry[l - 1];
5107       }
5108
5109       put_into_list:
5110
5111       *entry = *new_entry;
5112
5113       return i;
5114     }
5115     else if (one_score_entry_per_name &&
5116              strEqual(entry->name, new_entry->name))
5117     {
5118       // player already in high score list with better score or time
5119
5120       return -1;
5121     }
5122   }
5123
5124   return -1;
5125 }
5126
5127 void NewHighScore(int level_nr, boolean tape_saved)
5128 {
5129   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5130   boolean one_per_name = FALSE;
5131
5132   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5133   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5134
5135   new_entry.score = game.score_final;
5136   new_entry.time = game.score_time_final;
5137
5138   LoadScore(level_nr);
5139
5140   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5141
5142   if (scores.last_added < 0)
5143     return;
5144
5145   SaveScore(level_nr);
5146
5147   // store last added local score entry (before merging server scores)
5148   scores.last_added_local = scores.last_added;
5149
5150   if (!game.LevelSolved_SaveTape)
5151     return;
5152
5153   SaveScoreTape(level_nr);
5154
5155   if (setup.ask_for_using_api_server)
5156   {
5157     setup.use_api_server =
5158       Request("Upload your score and tape to the high score server?", REQ_ASK);
5159
5160     if (!setup.use_api_server)
5161       Request("Not using high score server! Use setup menu to enable again!",
5162               REQ_CONFIRM);
5163
5164     runtime.use_api_server = setup.use_api_server;
5165
5166     // after asking for using API server once, do not ask again
5167     setup.ask_for_using_api_server = FALSE;
5168
5169     SaveSetup_ServerSetup();
5170   }
5171
5172   SaveServerScore(level_nr, tape_saved);
5173 }
5174
5175 void MergeServerScore(void)
5176 {
5177   struct ScoreEntry last_added_entry;
5178   boolean one_per_name = FALSE;
5179   int i;
5180
5181   if (scores.last_added >= 0)
5182     last_added_entry = scores.entry[scores.last_added];
5183
5184   for (i = 0; i < server_scores.num_entries; i++)
5185   {
5186     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5187
5188     if (pos >= 0 && pos <= scores.last_added)
5189       scores.last_added++;
5190   }
5191
5192   if (scores.last_added >= MAX_SCORE_ENTRIES)
5193   {
5194     scores.last_added = MAX_SCORE_ENTRIES - 1;
5195     scores.force_last_added = TRUE;
5196
5197     scores.entry[scores.last_added] = last_added_entry;
5198   }
5199 }
5200
5201 static int getElementMoveStepsizeExt(int x, int y, int direction)
5202 {
5203   int element = Tile[x][y];
5204   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5205   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5206   int horiz_move = (dx != 0);
5207   int sign = (horiz_move ? dx : dy);
5208   int step = sign * element_info[element].move_stepsize;
5209
5210   // special values for move stepsize for spring and things on conveyor belt
5211   if (horiz_move)
5212   {
5213     if (CAN_FALL(element) &&
5214         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5215       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5216     else if (element == EL_SPRING)
5217       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5218   }
5219
5220   return step;
5221 }
5222
5223 static int getElementMoveStepsize(int x, int y)
5224 {
5225   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5226 }
5227
5228 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5229 {
5230   if (player->GfxAction != action || player->GfxDir != dir)
5231   {
5232     player->GfxAction = action;
5233     player->GfxDir = dir;
5234     player->Frame = 0;
5235     player->StepFrame = 0;
5236   }
5237 }
5238
5239 static void ResetGfxFrame(int x, int y)
5240 {
5241   // profiling showed that "autotest" spends 10~20% of its time in this function
5242   if (DrawingDeactivatedField())
5243     return;
5244
5245   int element = Tile[x][y];
5246   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5247
5248   if (graphic_info[graphic].anim_global_sync)
5249     GfxFrame[x][y] = FrameCounter;
5250   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5251     GfxFrame[x][y] = CustomValue[x][y];
5252   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5253     GfxFrame[x][y] = element_info[element].collect_score;
5254   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5255     GfxFrame[x][y] = ChangeDelay[x][y];
5256 }
5257
5258 static void ResetGfxAnimation(int x, int y)
5259 {
5260   GfxAction[x][y] = ACTION_DEFAULT;
5261   GfxDir[x][y] = MovDir[x][y];
5262   GfxFrame[x][y] = 0;
5263
5264   ResetGfxFrame(x, y);
5265 }
5266
5267 static void ResetRandomAnimationValue(int x, int y)
5268 {
5269   GfxRandom[x][y] = INIT_GFX_RANDOM();
5270 }
5271
5272 static void InitMovingField(int x, int y, int direction)
5273 {
5274   int element = Tile[x][y];
5275   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5276   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5277   int newx = x + dx;
5278   int newy = y + dy;
5279   boolean is_moving_before, is_moving_after;
5280
5281   // check if element was/is moving or being moved before/after mode change
5282   is_moving_before = (WasJustMoving[x][y] != 0);
5283   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5284
5285   // reset animation only for moving elements which change direction of moving
5286   // or which just started or stopped moving
5287   // (else CEs with property "can move" / "not moving" are reset each frame)
5288   if (is_moving_before != is_moving_after ||
5289       direction != MovDir[x][y])
5290     ResetGfxAnimation(x, y);
5291
5292   MovDir[x][y] = direction;
5293   GfxDir[x][y] = direction;
5294
5295   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5296                      direction == MV_DOWN && CAN_FALL(element) ?
5297                      ACTION_FALLING : ACTION_MOVING);
5298
5299   // this is needed for CEs with property "can move" / "not moving"
5300
5301   if (is_moving_after)
5302   {
5303     if (Tile[newx][newy] == EL_EMPTY)
5304       Tile[newx][newy] = EL_BLOCKED;
5305
5306     MovDir[newx][newy] = MovDir[x][y];
5307
5308     CustomValue[newx][newy] = CustomValue[x][y];
5309
5310     GfxFrame[newx][newy] = GfxFrame[x][y];
5311     GfxRandom[newx][newy] = GfxRandom[x][y];
5312     GfxAction[newx][newy] = GfxAction[x][y];
5313     GfxDir[newx][newy] = GfxDir[x][y];
5314   }
5315 }
5316
5317 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5318 {
5319   int direction = MovDir[x][y];
5320   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5321   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5322
5323   *goes_to_x = newx;
5324   *goes_to_y = newy;
5325 }
5326
5327 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5328 {
5329   int oldx = x, oldy = y;
5330   int direction = MovDir[x][y];
5331
5332   if (direction == MV_LEFT)
5333     oldx++;
5334   else if (direction == MV_RIGHT)
5335     oldx--;
5336   else if (direction == MV_UP)
5337     oldy++;
5338   else if (direction == MV_DOWN)
5339     oldy--;
5340
5341   *comes_from_x = oldx;
5342   *comes_from_y = oldy;
5343 }
5344
5345 static int MovingOrBlocked2Element(int x, int y)
5346 {
5347   int element = Tile[x][y];
5348
5349   if (element == EL_BLOCKED)
5350   {
5351     int oldx, oldy;
5352
5353     Blocked2Moving(x, y, &oldx, &oldy);
5354     return Tile[oldx][oldy];
5355   }
5356   else
5357     return element;
5358 }
5359
5360 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5361 {
5362   // like MovingOrBlocked2Element(), but if element is moving
5363   // and (x,y) is the field the moving element is just leaving,
5364   // return EL_BLOCKED instead of the element value
5365   int element = Tile[x][y];
5366
5367   if (IS_MOVING(x, y))
5368   {
5369     if (element == EL_BLOCKED)
5370     {
5371       int oldx, oldy;
5372
5373       Blocked2Moving(x, y, &oldx, &oldy);
5374       return Tile[oldx][oldy];
5375     }
5376     else
5377       return EL_BLOCKED;
5378   }
5379   else
5380     return element;
5381 }
5382
5383 static void RemoveField(int x, int y)
5384 {
5385   Tile[x][y] = EL_EMPTY;
5386
5387   MovPos[x][y] = 0;
5388   MovDir[x][y] = 0;
5389   MovDelay[x][y] = 0;
5390
5391   CustomValue[x][y] = 0;
5392
5393   AmoebaNr[x][y] = 0;
5394   ChangeDelay[x][y] = 0;
5395   ChangePage[x][y] = -1;
5396   Pushed[x][y] = FALSE;
5397
5398   GfxElement[x][y] = EL_UNDEFINED;
5399   GfxAction[x][y] = ACTION_DEFAULT;
5400   GfxDir[x][y] = MV_NONE;
5401 }
5402
5403 static void RemoveMovingField(int x, int y)
5404 {
5405   int oldx = x, oldy = y, newx = x, newy = y;
5406   int element = Tile[x][y];
5407   int next_element = EL_UNDEFINED;
5408
5409   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5410     return;
5411
5412   if (IS_MOVING(x, y))
5413   {
5414     Moving2Blocked(x, y, &newx, &newy);
5415
5416     if (Tile[newx][newy] != EL_BLOCKED)
5417     {
5418       // element is moving, but target field is not free (blocked), but
5419       // already occupied by something different (example: acid pool);
5420       // in this case, only remove the moving field, but not the target
5421
5422       RemoveField(oldx, oldy);
5423
5424       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5425
5426       TEST_DrawLevelField(oldx, oldy);
5427
5428       return;
5429     }
5430   }
5431   else if (element == EL_BLOCKED)
5432   {
5433     Blocked2Moving(x, y, &oldx, &oldy);
5434     if (!IS_MOVING(oldx, oldy))
5435       return;
5436   }
5437
5438   if (element == EL_BLOCKED &&
5439       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5440        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5441        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5442        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5443        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5444        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5445     next_element = get_next_element(Tile[oldx][oldy]);
5446
5447   RemoveField(oldx, oldy);
5448   RemoveField(newx, newy);
5449
5450   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5451
5452   if (next_element != EL_UNDEFINED)
5453     Tile[oldx][oldy] = next_element;
5454
5455   TEST_DrawLevelField(oldx, oldy);
5456   TEST_DrawLevelField(newx, newy);
5457 }
5458
5459 void DrawDynamite(int x, int y)
5460 {
5461   int sx = SCREENX(x), sy = SCREENY(y);
5462   int graphic = el2img(Tile[x][y]);
5463   int frame;
5464
5465   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5466     return;
5467
5468   if (IS_WALKABLE_INSIDE(Back[x][y]))
5469     return;
5470
5471   if (Back[x][y])
5472     DrawLevelElement(x, y, Back[x][y]);
5473   else if (Store[x][y])
5474     DrawLevelElement(x, y, Store[x][y]);
5475   else if (game.use_masked_elements)
5476     DrawLevelElement(x, y, EL_EMPTY);
5477
5478   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5479
5480   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5481     DrawGraphicThruMask(sx, sy, graphic, frame);
5482   else
5483     DrawGraphic(sx, sy, graphic, frame);
5484 }
5485
5486 static void CheckDynamite(int x, int y)
5487 {
5488   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5489   {
5490     MovDelay[x][y]--;
5491
5492     if (MovDelay[x][y] != 0)
5493     {
5494       DrawDynamite(x, y);
5495       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5496
5497       return;
5498     }
5499   }
5500
5501   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5502
5503   Bang(x, y);
5504 }
5505
5506 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5507 {
5508   boolean num_checked_players = 0;
5509   int i;
5510
5511   for (i = 0; i < MAX_PLAYERS; i++)
5512   {
5513     if (stored_player[i].active)
5514     {
5515       int sx = stored_player[i].jx;
5516       int sy = stored_player[i].jy;
5517
5518       if (num_checked_players == 0)
5519       {
5520         *sx1 = *sx2 = sx;
5521         *sy1 = *sy2 = sy;
5522       }
5523       else
5524       {
5525         *sx1 = MIN(*sx1, sx);
5526         *sy1 = MIN(*sy1, sy);
5527         *sx2 = MAX(*sx2, sx);
5528         *sy2 = MAX(*sy2, sy);
5529       }
5530
5531       num_checked_players++;
5532     }
5533   }
5534 }
5535
5536 static boolean checkIfAllPlayersFitToScreen_RND(void)
5537 {
5538   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5539
5540   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5541
5542   return (sx2 - sx1 < SCR_FIELDX &&
5543           sy2 - sy1 < SCR_FIELDY);
5544 }
5545
5546 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5547 {
5548   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5549
5550   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5551
5552   *sx = (sx1 + sx2) / 2;
5553   *sy = (sy1 + sy2) / 2;
5554 }
5555
5556 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5557                                boolean center_screen, boolean quick_relocation)
5558 {
5559   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5560   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5561   boolean no_delay = (tape.warp_forward);
5562   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5563   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5564   int new_scroll_x, new_scroll_y;
5565
5566   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5567   {
5568     // case 1: quick relocation inside visible screen (without scrolling)
5569
5570     RedrawPlayfield();
5571
5572     return;
5573   }
5574
5575   if (!level.shifted_relocation || center_screen)
5576   {
5577     // relocation _with_ centering of screen
5578
5579     new_scroll_x = SCROLL_POSITION_X(x);
5580     new_scroll_y = SCROLL_POSITION_Y(y);
5581   }
5582   else
5583   {
5584     // relocation _without_ centering of screen
5585
5586     int center_scroll_x = SCROLL_POSITION_X(old_x);
5587     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5588     int offset_x = x + (scroll_x - center_scroll_x);
5589     int offset_y = y + (scroll_y - center_scroll_y);
5590
5591     // for new screen position, apply previous offset to center position
5592     new_scroll_x = SCROLL_POSITION_X(offset_x);
5593     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5594   }
5595
5596   if (quick_relocation)
5597   {
5598     // case 2: quick relocation (redraw without visible scrolling)
5599
5600     scroll_x = new_scroll_x;
5601     scroll_y = new_scroll_y;
5602
5603     RedrawPlayfield();
5604
5605     return;
5606   }
5607
5608   // case 3: visible relocation (with scrolling to new position)
5609
5610   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5611
5612   SetVideoFrameDelay(wait_delay_value);
5613
5614   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5615   {
5616     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5617     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5618
5619     if (dx == 0 && dy == 0)             // no scrolling needed at all
5620       break;
5621
5622     scroll_x -= dx;
5623     scroll_y -= dy;
5624
5625     // set values for horizontal/vertical screen scrolling (half tile size)
5626     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5627     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5628     int pos_x = dx * TILEX / 2;
5629     int pos_y = dy * TILEY / 2;
5630     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5631     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5632
5633     ScrollLevel(dx, dy);
5634     DrawAllPlayers();
5635
5636     // scroll in two steps of half tile size to make things smoother
5637     BlitScreenToBitmapExt_RND(window, fx, fy);
5638
5639     // scroll second step to align at full tile size
5640     BlitScreenToBitmap(window);
5641   }
5642
5643   DrawAllPlayers();
5644   BackToFront();
5645
5646   SetVideoFrameDelay(frame_delay_value_old);
5647 }
5648
5649 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5650 {
5651   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5652   int player_nr = GET_PLAYER_NR(el_player);
5653   struct PlayerInfo *player = &stored_player[player_nr];
5654   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5655   boolean no_delay = (tape.warp_forward);
5656   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5657   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5658   int old_jx = player->jx;
5659   int old_jy = player->jy;
5660   int old_element = Tile[old_jx][old_jy];
5661   int element = Tile[jx][jy];
5662   boolean player_relocated = (old_jx != jx || old_jy != jy);
5663
5664   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5665   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5666   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5667   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5668   int leave_side_horiz = move_dir_horiz;
5669   int leave_side_vert  = move_dir_vert;
5670   int enter_side = enter_side_horiz | enter_side_vert;
5671   int leave_side = leave_side_horiz | leave_side_vert;
5672
5673   if (player->buried)           // do not reanimate dead player
5674     return;
5675
5676   if (!player_relocated)        // no need to relocate the player
5677     return;
5678
5679   if (IS_PLAYER(jx, jy))        // player already placed at new position
5680   {
5681     RemoveField(jx, jy);        // temporarily remove newly placed player
5682     DrawLevelField(jx, jy);
5683   }
5684
5685   if (player->present)
5686   {
5687     while (player->MovPos)
5688     {
5689       ScrollPlayer(player, SCROLL_GO_ON);
5690       ScrollScreen(NULL, SCROLL_GO_ON);
5691
5692       AdvanceFrameAndPlayerCounters(player->index_nr);
5693
5694       DrawPlayer(player);
5695
5696       BackToFront_WithFrameDelay(wait_delay_value);
5697     }
5698
5699     DrawPlayer(player);         // needed here only to cleanup last field
5700     DrawLevelField(player->jx, player->jy);     // remove player graphic
5701
5702     player->is_moving = FALSE;
5703   }
5704
5705   if (IS_CUSTOM_ELEMENT(old_element))
5706     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5707                                CE_LEFT_BY_PLAYER,
5708                                player->index_bit, leave_side);
5709
5710   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5711                                       CE_PLAYER_LEAVES_X,
5712                                       player->index_bit, leave_side);
5713
5714   Tile[jx][jy] = el_player;
5715   InitPlayerField(jx, jy, el_player, TRUE);
5716
5717   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5718      possible that the relocation target field did not contain a player element,
5719      but a walkable element, to which the new player was relocated -- in this
5720      case, restore that (already initialized!) element on the player field */
5721   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5722   {
5723     Tile[jx][jy] = element;     // restore previously existing element
5724   }
5725
5726   // only visually relocate centered player
5727   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5728                      FALSE, level.instant_relocation);
5729
5730   TestIfPlayerTouchesBadThing(jx, jy);
5731   TestIfPlayerTouchesCustomElement(jx, jy);
5732
5733   if (IS_CUSTOM_ELEMENT(element))
5734     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5735                                player->index_bit, enter_side);
5736
5737   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5738                                       player->index_bit, enter_side);
5739
5740   if (player->is_switching)
5741   {
5742     /* ensure that relocation while still switching an element does not cause
5743        a new element to be treated as also switched directly after relocation
5744        (this is important for teleporter switches that teleport the player to
5745        a place where another teleporter switch is in the same direction, which
5746        would then incorrectly be treated as immediately switched before the
5747        direction key that caused the switch was released) */
5748
5749     player->switch_x += jx - old_jx;
5750     player->switch_y += jy - old_jy;
5751   }
5752 }
5753
5754 static void Explode(int ex, int ey, int phase, int mode)
5755 {
5756   int x, y;
5757   int last_phase;
5758   int border_element;
5759
5760   // !!! eliminate this variable !!!
5761   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5762
5763   if (game.explosions_delayed)
5764   {
5765     ExplodeField[ex][ey] = mode;
5766     return;
5767   }
5768
5769   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5770   {
5771     int center_element = Tile[ex][ey];
5772     int artwork_element, explosion_element;     // set these values later
5773
5774     // remove things displayed in background while burning dynamite
5775     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5776       Back[ex][ey] = 0;
5777
5778     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5779     {
5780       // put moving element to center field (and let it explode there)
5781       center_element = MovingOrBlocked2Element(ex, ey);
5782       RemoveMovingField(ex, ey);
5783       Tile[ex][ey] = center_element;
5784     }
5785
5786     // now "center_element" is finally determined -- set related values now
5787     artwork_element = center_element;           // for custom player artwork
5788     explosion_element = center_element;         // for custom player artwork
5789
5790     if (IS_PLAYER(ex, ey))
5791     {
5792       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5793
5794       artwork_element = stored_player[player_nr].artwork_element;
5795
5796       if (level.use_explosion_element[player_nr])
5797       {
5798         explosion_element = level.explosion_element[player_nr];
5799         artwork_element = explosion_element;
5800       }
5801     }
5802
5803     if (mode == EX_TYPE_NORMAL ||
5804         mode == EX_TYPE_CENTER ||
5805         mode == EX_TYPE_CROSS)
5806       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5807
5808     last_phase = element_info[explosion_element].explosion_delay + 1;
5809
5810     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5811     {
5812       int xx = x - ex + 1;
5813       int yy = y - ey + 1;
5814       int element;
5815
5816       if (!IN_LEV_FIELD(x, y) ||
5817           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5818           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5819         continue;
5820
5821       element = Tile[x][y];
5822
5823       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5824       {
5825         element = MovingOrBlocked2Element(x, y);
5826
5827         if (!IS_EXPLOSION_PROOF(element))
5828           RemoveMovingField(x, y);
5829       }
5830
5831       // indestructible elements can only explode in center (but not flames)
5832       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5833                                            mode == EX_TYPE_BORDER)) ||
5834           element == EL_FLAMES)
5835         continue;
5836
5837       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5838          behaviour, for example when touching a yamyam that explodes to rocks
5839          with active deadly shield, a rock is created under the player !!! */
5840       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5841 #if 0
5842       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5843           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5844            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5845 #else
5846       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5847 #endif
5848       {
5849         if (IS_ACTIVE_BOMB(element))
5850         {
5851           // re-activate things under the bomb like gate or penguin
5852           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5853           Back[x][y] = 0;
5854         }
5855
5856         continue;
5857       }
5858
5859       // save walkable background elements while explosion on same tile
5860       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5861           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5862         Back[x][y] = element;
5863
5864       // ignite explodable elements reached by other explosion
5865       if (element == EL_EXPLOSION)
5866         element = Store2[x][y];
5867
5868       if (AmoebaNr[x][y] &&
5869           (element == EL_AMOEBA_FULL ||
5870            element == EL_BD_AMOEBA ||
5871            element == EL_AMOEBA_GROWING))
5872       {
5873         AmoebaCnt[AmoebaNr[x][y]]--;
5874         AmoebaCnt2[AmoebaNr[x][y]]--;
5875       }
5876
5877       RemoveField(x, y);
5878
5879       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5880       {
5881         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5882
5883         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5884
5885         if (PLAYERINFO(ex, ey)->use_murphy)
5886           Store[x][y] = EL_EMPTY;
5887       }
5888
5889       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5890       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5891       else if (IS_PLAYER_ELEMENT(center_element))
5892         Store[x][y] = EL_EMPTY;
5893       else if (center_element == EL_YAMYAM)
5894         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5895       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5896         Store[x][y] = element_info[center_element].content.e[xx][yy];
5897 #if 1
5898       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5899       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5900       // otherwise) -- FIX THIS !!!
5901       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5902         Store[x][y] = element_info[element].content.e[1][1];
5903 #else
5904       else if (!CAN_EXPLODE(element))
5905         Store[x][y] = element_info[element].content.e[1][1];
5906 #endif
5907       else
5908         Store[x][y] = EL_EMPTY;
5909
5910       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5911           center_element == EL_AMOEBA_TO_DIAMOND)
5912         Store2[x][y] = element;
5913
5914       Tile[x][y] = EL_EXPLOSION;
5915       GfxElement[x][y] = artwork_element;
5916
5917       ExplodePhase[x][y] = 1;
5918       ExplodeDelay[x][y] = last_phase;
5919
5920       Stop[x][y] = TRUE;
5921     }
5922
5923     if (center_element == EL_YAMYAM)
5924       game.yamyam_content_nr =
5925         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5926
5927     return;
5928   }
5929
5930   if (Stop[ex][ey])
5931     return;
5932
5933   x = ex;
5934   y = ey;
5935
5936   if (phase == 1)
5937     GfxFrame[x][y] = 0;         // restart explosion animation
5938
5939   last_phase = ExplodeDelay[x][y];
5940
5941   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5942
5943   // this can happen if the player leaves an explosion just in time
5944   if (GfxElement[x][y] == EL_UNDEFINED)
5945     GfxElement[x][y] = EL_EMPTY;
5946
5947   border_element = Store2[x][y];
5948   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5949     border_element = StorePlayer[x][y];
5950
5951   if (phase == element_info[border_element].ignition_delay ||
5952       phase == last_phase)
5953   {
5954     boolean border_explosion = FALSE;
5955
5956     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5957         !PLAYER_EXPLOSION_PROTECTED(x, y))
5958     {
5959       KillPlayerUnlessExplosionProtected(x, y);
5960       border_explosion = TRUE;
5961     }
5962     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5963     {
5964       Tile[x][y] = Store2[x][y];
5965       Store2[x][y] = 0;
5966       Bang(x, y);
5967       border_explosion = TRUE;
5968     }
5969     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5970     {
5971       AmoebaToDiamond(x, y);
5972       Store2[x][y] = 0;
5973       border_explosion = TRUE;
5974     }
5975
5976     // if an element just explodes due to another explosion (chain-reaction),
5977     // do not immediately end the new explosion when it was the last frame of
5978     // the explosion (as it would be done in the following "if"-statement!)
5979     if (border_explosion && phase == last_phase)
5980       return;
5981   }
5982
5983   if (phase == last_phase)
5984   {
5985     int element;
5986
5987     element = Tile[x][y] = Store[x][y];
5988     Store[x][y] = Store2[x][y] = 0;
5989     GfxElement[x][y] = EL_UNDEFINED;
5990
5991     // player can escape from explosions and might therefore be still alive
5992     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5993         element <= EL_PLAYER_IS_EXPLODING_4)
5994     {
5995       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5996       int explosion_element = EL_PLAYER_1 + player_nr;
5997       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5998       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5999
6000       if (level.use_explosion_element[player_nr])
6001         explosion_element = level.explosion_element[player_nr];
6002
6003       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6004                     element_info[explosion_element].content.e[xx][yy]);
6005     }
6006
6007     // restore probably existing indestructible background element
6008     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6009       element = Tile[x][y] = Back[x][y];
6010     Back[x][y] = 0;
6011
6012     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6013     GfxDir[x][y] = MV_NONE;
6014     ChangeDelay[x][y] = 0;
6015     ChangePage[x][y] = -1;
6016
6017     CustomValue[x][y] = 0;
6018
6019     InitField_WithBug2(x, y, FALSE);
6020
6021     TEST_DrawLevelField(x, y);
6022
6023     TestIfElementTouchesCustomElement(x, y);
6024
6025     if (GFX_CRUMBLED(element))
6026       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6027
6028     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6029       StorePlayer[x][y] = 0;
6030
6031     if (IS_PLAYER_ELEMENT(element))
6032       RelocatePlayer(x, y, element);
6033   }
6034   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6035   {
6036     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6037     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6038
6039     if (phase == delay)
6040       TEST_DrawLevelFieldCrumbled(x, y);
6041
6042     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6043     {
6044       DrawLevelElement(x, y, Back[x][y]);
6045       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6046     }
6047     else if (IS_WALKABLE_UNDER(Back[x][y]))
6048     {
6049       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6050       DrawLevelElementThruMask(x, y, Back[x][y]);
6051     }
6052     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6053       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6054   }
6055 }
6056
6057 static void DynaExplode(int ex, int ey)
6058 {
6059   int i, j;
6060   int dynabomb_element = Tile[ex][ey];
6061   int dynabomb_size = 1;
6062   boolean dynabomb_xl = FALSE;
6063   struct PlayerInfo *player;
6064   static int xy[4][2] =
6065   {
6066     { 0, -1 },
6067     { -1, 0 },
6068     { +1, 0 },
6069     { 0, +1 }
6070   };
6071
6072   if (IS_ACTIVE_BOMB(dynabomb_element))
6073   {
6074     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6075     dynabomb_size = player->dynabomb_size;
6076     dynabomb_xl = player->dynabomb_xl;
6077     player->dynabombs_left++;
6078   }
6079
6080   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6081
6082   for (i = 0; i < NUM_DIRECTIONS; i++)
6083   {
6084     for (j = 1; j <= dynabomb_size; j++)
6085     {
6086       int x = ex + j * xy[i][0];
6087       int y = ey + j * xy[i][1];
6088       int element;
6089
6090       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6091         break;
6092
6093       element = Tile[x][y];
6094
6095       // do not restart explosions of fields with active bombs
6096       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6097         continue;
6098
6099       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6100
6101       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6102           !IS_DIGGABLE(element) && !dynabomb_xl)
6103         break;
6104     }
6105   }
6106 }
6107
6108 void Bang(int x, int y)
6109 {
6110   int element = MovingOrBlocked2Element(x, y);
6111   int explosion_type = EX_TYPE_NORMAL;
6112
6113   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6114   {
6115     struct PlayerInfo *player = PLAYERINFO(x, y);
6116
6117     element = Tile[x][y] = player->initial_element;
6118
6119     if (level.use_explosion_element[player->index_nr])
6120     {
6121       int explosion_element = level.explosion_element[player->index_nr];
6122
6123       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6124         explosion_type = EX_TYPE_CROSS;
6125       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6126         explosion_type = EX_TYPE_CENTER;
6127     }
6128   }
6129
6130   switch (element)
6131   {
6132     case EL_BUG:
6133     case EL_SPACESHIP:
6134     case EL_BD_BUTTERFLY:
6135     case EL_BD_FIREFLY:
6136     case EL_YAMYAM:
6137     case EL_DARK_YAMYAM:
6138     case EL_ROBOT:
6139     case EL_PACMAN:
6140     case EL_MOLE:
6141       RaiseScoreElement(element);
6142       break;
6143
6144     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6145     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6146     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6147     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6148     case EL_DYNABOMB_INCREASE_NUMBER:
6149     case EL_DYNABOMB_INCREASE_SIZE:
6150     case EL_DYNABOMB_INCREASE_POWER:
6151       explosion_type = EX_TYPE_DYNA;
6152       break;
6153
6154     case EL_DC_LANDMINE:
6155       explosion_type = EX_TYPE_CENTER;
6156       break;
6157
6158     case EL_PENGUIN:
6159     case EL_LAMP:
6160     case EL_LAMP_ACTIVE:
6161     case EL_AMOEBA_TO_DIAMOND:
6162       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6163         explosion_type = EX_TYPE_CENTER;
6164       break;
6165
6166     default:
6167       if (element_info[element].explosion_type == EXPLODES_CROSS)
6168         explosion_type = EX_TYPE_CROSS;
6169       else if (element_info[element].explosion_type == EXPLODES_1X1)
6170         explosion_type = EX_TYPE_CENTER;
6171       break;
6172   }
6173
6174   if (explosion_type == EX_TYPE_DYNA)
6175     DynaExplode(x, y);
6176   else
6177     Explode(x, y, EX_PHASE_START, explosion_type);
6178
6179   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6180 }
6181
6182 static void SplashAcid(int x, int y)
6183 {
6184   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6185       (!IN_LEV_FIELD(x - 1, y - 2) ||
6186        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6187     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6188
6189   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6190       (!IN_LEV_FIELD(x + 1, y - 2) ||
6191        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6192     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6193
6194   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6195 }
6196
6197 static void InitBeltMovement(void)
6198 {
6199   static int belt_base_element[4] =
6200   {
6201     EL_CONVEYOR_BELT_1_LEFT,
6202     EL_CONVEYOR_BELT_2_LEFT,
6203     EL_CONVEYOR_BELT_3_LEFT,
6204     EL_CONVEYOR_BELT_4_LEFT
6205   };
6206   static int belt_base_active_element[4] =
6207   {
6208     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6209     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6210     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6211     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6212   };
6213
6214   int x, y, i, j;
6215
6216   // set frame order for belt animation graphic according to belt direction
6217   for (i = 0; i < NUM_BELTS; i++)
6218   {
6219     int belt_nr = i;
6220
6221     for (j = 0; j < NUM_BELT_PARTS; j++)
6222     {
6223       int element = belt_base_active_element[belt_nr] + j;
6224       int graphic_1 = el2img(element);
6225       int graphic_2 = el2panelimg(element);
6226
6227       if (game.belt_dir[i] == MV_LEFT)
6228       {
6229         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6230         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6231       }
6232       else
6233       {
6234         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6235         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6236       }
6237     }
6238   }
6239
6240   SCAN_PLAYFIELD(x, y)
6241   {
6242     int element = Tile[x][y];
6243
6244     for (i = 0; i < NUM_BELTS; i++)
6245     {
6246       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6247       {
6248         int e_belt_nr = getBeltNrFromBeltElement(element);
6249         int belt_nr = i;
6250
6251         if (e_belt_nr == belt_nr)
6252         {
6253           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6254
6255           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6256         }
6257       }
6258     }
6259   }
6260 }
6261
6262 static void ToggleBeltSwitch(int x, int y)
6263 {
6264   static int belt_base_element[4] =
6265   {
6266     EL_CONVEYOR_BELT_1_LEFT,
6267     EL_CONVEYOR_BELT_2_LEFT,
6268     EL_CONVEYOR_BELT_3_LEFT,
6269     EL_CONVEYOR_BELT_4_LEFT
6270   };
6271   static int belt_base_active_element[4] =
6272   {
6273     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6274     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6275     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6276     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6277   };
6278   static int belt_base_switch_element[4] =
6279   {
6280     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6281     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6282     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6283     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6284   };
6285   static int belt_move_dir[4] =
6286   {
6287     MV_LEFT,
6288     MV_NONE,
6289     MV_RIGHT,
6290     MV_NONE,
6291   };
6292
6293   int element = Tile[x][y];
6294   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6295   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6296   int belt_dir = belt_move_dir[belt_dir_nr];
6297   int xx, yy, i;
6298
6299   if (!IS_BELT_SWITCH(element))
6300     return;
6301
6302   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6303   game.belt_dir[belt_nr] = belt_dir;
6304
6305   if (belt_dir_nr == 3)
6306     belt_dir_nr = 1;
6307
6308   // set frame order for belt animation graphic according to belt direction
6309   for (i = 0; i < NUM_BELT_PARTS; i++)
6310   {
6311     int element = belt_base_active_element[belt_nr] + i;
6312     int graphic_1 = el2img(element);
6313     int graphic_2 = el2panelimg(element);
6314
6315     if (belt_dir == MV_LEFT)
6316     {
6317       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6318       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6319     }
6320     else
6321     {
6322       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6323       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6324     }
6325   }
6326
6327   SCAN_PLAYFIELD(xx, yy)
6328   {
6329     int element = Tile[xx][yy];
6330
6331     if (IS_BELT_SWITCH(element))
6332     {
6333       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6334
6335       if (e_belt_nr == belt_nr)
6336       {
6337         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6338         TEST_DrawLevelField(xx, yy);
6339       }
6340     }
6341     else if (IS_BELT(element) && belt_dir != MV_NONE)
6342     {
6343       int e_belt_nr = getBeltNrFromBeltElement(element);
6344
6345       if (e_belt_nr == belt_nr)
6346       {
6347         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6348
6349         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6350         TEST_DrawLevelField(xx, yy);
6351       }
6352     }
6353     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6354     {
6355       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6356
6357       if (e_belt_nr == belt_nr)
6358       {
6359         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6360
6361         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6362         TEST_DrawLevelField(xx, yy);
6363       }
6364     }
6365   }
6366 }
6367
6368 static void ToggleSwitchgateSwitch(int x, int y)
6369 {
6370   int xx, yy;
6371
6372   game.switchgate_pos = !game.switchgate_pos;
6373
6374   SCAN_PLAYFIELD(xx, yy)
6375   {
6376     int element = Tile[xx][yy];
6377
6378     if (element == EL_SWITCHGATE_SWITCH_UP)
6379     {
6380       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6381       TEST_DrawLevelField(xx, yy);
6382     }
6383     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6384     {
6385       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6386       TEST_DrawLevelField(xx, yy);
6387     }
6388     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6389     {
6390       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6391       TEST_DrawLevelField(xx, yy);
6392     }
6393     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6394     {
6395       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6396       TEST_DrawLevelField(xx, yy);
6397     }
6398     else if (element == EL_SWITCHGATE_OPEN ||
6399              element == EL_SWITCHGATE_OPENING)
6400     {
6401       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6402
6403       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6404     }
6405     else if (element == EL_SWITCHGATE_CLOSED ||
6406              element == EL_SWITCHGATE_CLOSING)
6407     {
6408       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6409
6410       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6411     }
6412   }
6413 }
6414
6415 static int getInvisibleActiveFromInvisibleElement(int element)
6416 {
6417   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6418           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6419           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6420           element);
6421 }
6422
6423 static int getInvisibleFromInvisibleActiveElement(int element)
6424 {
6425   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6426           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6427           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6428           element);
6429 }
6430
6431 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6432 {
6433   int x, y;
6434
6435   SCAN_PLAYFIELD(x, y)
6436   {
6437     int element = Tile[x][y];
6438
6439     if (element == EL_LIGHT_SWITCH &&
6440         game.light_time_left > 0)
6441     {
6442       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6443       TEST_DrawLevelField(x, y);
6444     }
6445     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6446              game.light_time_left == 0)
6447     {
6448       Tile[x][y] = EL_LIGHT_SWITCH;
6449       TEST_DrawLevelField(x, y);
6450     }
6451     else if (element == EL_EMC_DRIPPER &&
6452              game.light_time_left > 0)
6453     {
6454       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6455       TEST_DrawLevelField(x, y);
6456     }
6457     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6458              game.light_time_left == 0)
6459     {
6460       Tile[x][y] = EL_EMC_DRIPPER;
6461       TEST_DrawLevelField(x, y);
6462     }
6463     else if (element == EL_INVISIBLE_STEELWALL ||
6464              element == EL_INVISIBLE_WALL ||
6465              element == EL_INVISIBLE_SAND)
6466     {
6467       if (game.light_time_left > 0)
6468         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6469
6470       TEST_DrawLevelField(x, y);
6471
6472       // uncrumble neighbour fields, if needed
6473       if (element == EL_INVISIBLE_SAND)
6474         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6475     }
6476     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6477              element == EL_INVISIBLE_WALL_ACTIVE ||
6478              element == EL_INVISIBLE_SAND_ACTIVE)
6479     {
6480       if (game.light_time_left == 0)
6481         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6482
6483       TEST_DrawLevelField(x, y);
6484
6485       // re-crumble neighbour fields, if needed
6486       if (element == EL_INVISIBLE_SAND)
6487         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6488     }
6489   }
6490 }
6491
6492 static void RedrawAllInvisibleElementsForLenses(void)
6493 {
6494   int x, y;
6495
6496   SCAN_PLAYFIELD(x, y)
6497   {
6498     int element = Tile[x][y];
6499
6500     if (element == EL_EMC_DRIPPER &&
6501         game.lenses_time_left > 0)
6502     {
6503       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6504       TEST_DrawLevelField(x, y);
6505     }
6506     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6507              game.lenses_time_left == 0)
6508     {
6509       Tile[x][y] = EL_EMC_DRIPPER;
6510       TEST_DrawLevelField(x, y);
6511     }
6512     else if (element == EL_INVISIBLE_STEELWALL ||
6513              element == EL_INVISIBLE_WALL ||
6514              element == EL_INVISIBLE_SAND)
6515     {
6516       if (game.lenses_time_left > 0)
6517         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6518
6519       TEST_DrawLevelField(x, y);
6520
6521       // uncrumble neighbour fields, if needed
6522       if (element == EL_INVISIBLE_SAND)
6523         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6524     }
6525     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6526              element == EL_INVISIBLE_WALL_ACTIVE ||
6527              element == EL_INVISIBLE_SAND_ACTIVE)
6528     {
6529       if (game.lenses_time_left == 0)
6530         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6531
6532       TEST_DrawLevelField(x, y);
6533
6534       // re-crumble neighbour fields, if needed
6535       if (element == EL_INVISIBLE_SAND)
6536         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6537     }
6538   }
6539 }
6540
6541 static void RedrawAllInvisibleElementsForMagnifier(void)
6542 {
6543   int x, y;
6544
6545   SCAN_PLAYFIELD(x, y)
6546   {
6547     int element = Tile[x][y];
6548
6549     if (element == EL_EMC_FAKE_GRASS &&
6550         game.magnify_time_left > 0)
6551     {
6552       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6553       TEST_DrawLevelField(x, y);
6554     }
6555     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6556              game.magnify_time_left == 0)
6557     {
6558       Tile[x][y] = EL_EMC_FAKE_GRASS;
6559       TEST_DrawLevelField(x, y);
6560     }
6561     else if (IS_GATE_GRAY(element) &&
6562              game.magnify_time_left > 0)
6563     {
6564       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6565                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6566                     IS_EM_GATE_GRAY(element) ?
6567                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6568                     IS_EMC_GATE_GRAY(element) ?
6569                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6570                     IS_DC_GATE_GRAY(element) ?
6571                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6572                     element);
6573       TEST_DrawLevelField(x, y);
6574     }
6575     else if (IS_GATE_GRAY_ACTIVE(element) &&
6576              game.magnify_time_left == 0)
6577     {
6578       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6579                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6580                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6581                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6582                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6583                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6584                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6585                     EL_DC_GATE_WHITE_GRAY :
6586                     element);
6587       TEST_DrawLevelField(x, y);
6588     }
6589   }
6590 }
6591
6592 static void ToggleLightSwitch(int x, int y)
6593 {
6594   int element = Tile[x][y];
6595
6596   game.light_time_left =
6597     (element == EL_LIGHT_SWITCH ?
6598      level.time_light * FRAMES_PER_SECOND : 0);
6599
6600   RedrawAllLightSwitchesAndInvisibleElements();
6601 }
6602
6603 static void ActivateTimegateSwitch(int x, int y)
6604 {
6605   int xx, yy;
6606
6607   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6608
6609   SCAN_PLAYFIELD(xx, yy)
6610   {
6611     int element = Tile[xx][yy];
6612
6613     if (element == EL_TIMEGATE_CLOSED ||
6614         element == EL_TIMEGATE_CLOSING)
6615     {
6616       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6617       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6618     }
6619
6620     /*
6621     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6622     {
6623       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6624       TEST_DrawLevelField(xx, yy);
6625     }
6626     */
6627
6628   }
6629
6630   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6631                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6632 }
6633
6634 static void Impact(int x, int y)
6635 {
6636   boolean last_line = (y == lev_fieldy - 1);
6637   boolean object_hit = FALSE;
6638   boolean impact = (last_line || object_hit);
6639   int element = Tile[x][y];
6640   int smashed = EL_STEELWALL;
6641
6642   if (!last_line)       // check if element below was hit
6643   {
6644     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6645       return;
6646
6647     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6648                                          MovDir[x][y + 1] != MV_DOWN ||
6649                                          MovPos[x][y + 1] <= TILEY / 2));
6650
6651     // do not smash moving elements that left the smashed field in time
6652     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6653         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6654       object_hit = FALSE;
6655
6656 #if USE_QUICKSAND_IMPACT_BUGFIX
6657     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6658     {
6659       RemoveMovingField(x, y + 1);
6660       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6661       Tile[x][y + 2] = EL_ROCK;
6662       TEST_DrawLevelField(x, y + 2);
6663
6664       object_hit = TRUE;
6665     }
6666
6667     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6668     {
6669       RemoveMovingField(x, y + 1);
6670       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6671       Tile[x][y + 2] = EL_ROCK;
6672       TEST_DrawLevelField(x, y + 2);
6673
6674       object_hit = TRUE;
6675     }
6676 #endif
6677
6678     if (object_hit)
6679       smashed = MovingOrBlocked2Element(x, y + 1);
6680
6681     impact = (last_line || object_hit);
6682   }
6683
6684   if (!last_line && smashed == EL_ACID) // element falls into acid
6685   {
6686     SplashAcid(x, y + 1);
6687     return;
6688   }
6689
6690   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6691   // only reset graphic animation if graphic really changes after impact
6692   if (impact &&
6693       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6694   {
6695     ResetGfxAnimation(x, y);
6696     TEST_DrawLevelField(x, y);
6697   }
6698
6699   if (impact && CAN_EXPLODE_IMPACT(element))
6700   {
6701     Bang(x, y);
6702     return;
6703   }
6704   else if (impact && element == EL_PEARL &&
6705            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6706   {
6707     ResetGfxAnimation(x, y);
6708
6709     Tile[x][y] = EL_PEARL_BREAKING;
6710     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6711     return;
6712   }
6713   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6714   {
6715     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6716
6717     return;
6718   }
6719
6720   if (impact && element == EL_AMOEBA_DROP)
6721   {
6722     if (object_hit && IS_PLAYER(x, y + 1))
6723       KillPlayerUnlessEnemyProtected(x, y + 1);
6724     else if (object_hit && smashed == EL_PENGUIN)
6725       Bang(x, y + 1);
6726     else
6727     {
6728       Tile[x][y] = EL_AMOEBA_GROWING;
6729       Store[x][y] = EL_AMOEBA_WET;
6730
6731       ResetRandomAnimationValue(x, y);
6732     }
6733     return;
6734   }
6735
6736   if (object_hit)               // check which object was hit
6737   {
6738     if ((CAN_PASS_MAGIC_WALL(element) && 
6739          (smashed == EL_MAGIC_WALL ||
6740           smashed == EL_BD_MAGIC_WALL)) ||
6741         (CAN_PASS_DC_MAGIC_WALL(element) &&
6742          smashed == EL_DC_MAGIC_WALL))
6743     {
6744       int xx, yy;
6745       int activated_magic_wall =
6746         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6747          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6748          EL_DC_MAGIC_WALL_ACTIVE);
6749
6750       // activate magic wall / mill
6751       SCAN_PLAYFIELD(xx, yy)
6752       {
6753         if (Tile[xx][yy] == smashed)
6754           Tile[xx][yy] = activated_magic_wall;
6755       }
6756
6757       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6758       game.magic_wall_active = TRUE;
6759
6760       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6761                             SND_MAGIC_WALL_ACTIVATING :
6762                             smashed == EL_BD_MAGIC_WALL ?
6763                             SND_BD_MAGIC_WALL_ACTIVATING :
6764                             SND_DC_MAGIC_WALL_ACTIVATING));
6765     }
6766
6767     if (IS_PLAYER(x, y + 1))
6768     {
6769       if (CAN_SMASH_PLAYER(element))
6770       {
6771         KillPlayerUnlessEnemyProtected(x, y + 1);
6772         return;
6773       }
6774     }
6775     else if (smashed == EL_PENGUIN)
6776     {
6777       if (CAN_SMASH_PLAYER(element))
6778       {
6779         Bang(x, y + 1);
6780         return;
6781       }
6782     }
6783     else if (element == EL_BD_DIAMOND)
6784     {
6785       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6786       {
6787         Bang(x, y + 1);
6788         return;
6789       }
6790     }
6791     else if (((element == EL_SP_INFOTRON ||
6792                element == EL_SP_ZONK) &&
6793               (smashed == EL_SP_SNIKSNAK ||
6794                smashed == EL_SP_ELECTRON ||
6795                smashed == EL_SP_DISK_ORANGE)) ||
6796              (element == EL_SP_INFOTRON &&
6797               smashed == EL_SP_DISK_YELLOW))
6798     {
6799       Bang(x, y + 1);
6800       return;
6801     }
6802     else if (CAN_SMASH_EVERYTHING(element))
6803     {
6804       if (IS_CLASSIC_ENEMY(smashed) ||
6805           CAN_EXPLODE_SMASHED(smashed))
6806       {
6807         Bang(x, y + 1);
6808         return;
6809       }
6810       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6811       {
6812         if (smashed == EL_LAMP ||
6813             smashed == EL_LAMP_ACTIVE)
6814         {
6815           Bang(x, y + 1);
6816           return;
6817         }
6818         else if (smashed == EL_NUT)
6819         {
6820           Tile[x][y + 1] = EL_NUT_BREAKING;
6821           PlayLevelSound(x, y, SND_NUT_BREAKING);
6822           RaiseScoreElement(EL_NUT);
6823           return;
6824         }
6825         else if (smashed == EL_PEARL)
6826         {
6827           ResetGfxAnimation(x, y);
6828
6829           Tile[x][y + 1] = EL_PEARL_BREAKING;
6830           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6831           return;
6832         }
6833         else if (smashed == EL_DIAMOND)
6834         {
6835           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6836           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6837           return;
6838         }
6839         else if (IS_BELT_SWITCH(smashed))
6840         {
6841           ToggleBeltSwitch(x, y + 1);
6842         }
6843         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6844                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6845                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6846                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6847         {
6848           ToggleSwitchgateSwitch(x, y + 1);
6849         }
6850         else if (smashed == EL_LIGHT_SWITCH ||
6851                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6852         {
6853           ToggleLightSwitch(x, y + 1);
6854         }
6855         else
6856         {
6857           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6858
6859           CheckElementChangeBySide(x, y + 1, smashed, element,
6860                                    CE_SWITCHED, CH_SIDE_TOP);
6861           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6862                                             CH_SIDE_TOP);
6863         }
6864       }
6865       else
6866       {
6867         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6868       }
6869     }
6870   }
6871
6872   // play sound of magic wall / mill
6873   if (!last_line &&
6874       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6875        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6876        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6877   {
6878     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6879       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6880     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6881       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6882     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6883       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6884
6885     return;
6886   }
6887
6888   // play sound of object that hits the ground
6889   if (last_line || object_hit)
6890     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6891 }
6892
6893 static void TurnRoundExt(int x, int y)
6894 {
6895   static struct
6896   {
6897     int dx, dy;
6898   } move_xy[] =
6899   {
6900     {  0,  0 },
6901     { -1,  0 },
6902     { +1,  0 },
6903     {  0,  0 },
6904     {  0, -1 },
6905     {  0,  0 }, { 0, 0 }, { 0, 0 },
6906     {  0, +1 }
6907   };
6908   static struct
6909   {
6910     int left, right, back;
6911   } turn[] =
6912   {
6913     { 0,        0,              0        },
6914     { MV_DOWN,  MV_UP,          MV_RIGHT },
6915     { MV_UP,    MV_DOWN,        MV_LEFT  },
6916     { 0,        0,              0        },
6917     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6918     { 0,        0,              0        },
6919     { 0,        0,              0        },
6920     { 0,        0,              0        },
6921     { MV_RIGHT, MV_LEFT,        MV_UP    }
6922   };
6923
6924   int element = Tile[x][y];
6925   int move_pattern = element_info[element].move_pattern;
6926
6927   int old_move_dir = MovDir[x][y];
6928   int left_dir  = turn[old_move_dir].left;
6929   int right_dir = turn[old_move_dir].right;
6930   int back_dir  = turn[old_move_dir].back;
6931
6932   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6933   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6934   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6935   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6936
6937   int left_x  = x + left_dx,  left_y  = y + left_dy;
6938   int right_x = x + right_dx, right_y = y + right_dy;
6939   int move_x  = x + move_dx,  move_y  = y + move_dy;
6940
6941   int xx, yy;
6942
6943   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6944   {
6945     TestIfBadThingTouchesOtherBadThing(x, y);
6946
6947     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6948       MovDir[x][y] = right_dir;
6949     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6950       MovDir[x][y] = left_dir;
6951
6952     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6953       MovDelay[x][y] = 9;
6954     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6955       MovDelay[x][y] = 1;
6956   }
6957   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6958   {
6959     TestIfBadThingTouchesOtherBadThing(x, y);
6960
6961     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6962       MovDir[x][y] = left_dir;
6963     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6964       MovDir[x][y] = right_dir;
6965
6966     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6967       MovDelay[x][y] = 9;
6968     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6969       MovDelay[x][y] = 1;
6970   }
6971   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6972   {
6973     TestIfBadThingTouchesOtherBadThing(x, y);
6974
6975     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6976       MovDir[x][y] = left_dir;
6977     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6978       MovDir[x][y] = right_dir;
6979
6980     if (MovDir[x][y] != old_move_dir)
6981       MovDelay[x][y] = 9;
6982   }
6983   else if (element == EL_YAMYAM)
6984   {
6985     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6986     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6987
6988     if (can_turn_left && can_turn_right)
6989       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6990     else if (can_turn_left)
6991       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6992     else if (can_turn_right)
6993       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6994     else
6995       MovDir[x][y] = back_dir;
6996
6997     MovDelay[x][y] = 16 + 16 * RND(3);
6998   }
6999   else if (element == EL_DARK_YAMYAM)
7000   {
7001     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7002                                                          left_x, left_y);
7003     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7004                                                          right_x, right_y);
7005
7006     if (can_turn_left && can_turn_right)
7007       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7008     else if (can_turn_left)
7009       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7010     else if (can_turn_right)
7011       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7012     else
7013       MovDir[x][y] = back_dir;
7014
7015     MovDelay[x][y] = 16 + 16 * RND(3);
7016   }
7017   else if (element == EL_PACMAN)
7018   {
7019     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7020     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7021
7022     if (can_turn_left && can_turn_right)
7023       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7024     else if (can_turn_left)
7025       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7026     else if (can_turn_right)
7027       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7028     else
7029       MovDir[x][y] = back_dir;
7030
7031     MovDelay[x][y] = 6 + RND(40);
7032   }
7033   else if (element == EL_PIG)
7034   {
7035     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7036     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7037     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7038     boolean should_turn_left, should_turn_right, should_move_on;
7039     int rnd_value = 24;
7040     int rnd = RND(rnd_value);
7041
7042     should_turn_left = (can_turn_left &&
7043                         (!can_move_on ||
7044                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7045                                                    y + back_dy + left_dy)));
7046     should_turn_right = (can_turn_right &&
7047                          (!can_move_on ||
7048                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7049                                                     y + back_dy + right_dy)));
7050     should_move_on = (can_move_on &&
7051                       (!can_turn_left ||
7052                        !can_turn_right ||
7053                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7054                                                  y + move_dy + left_dy) ||
7055                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7056                                                  y + move_dy + right_dy)));
7057
7058     if (should_turn_left || should_turn_right || should_move_on)
7059     {
7060       if (should_turn_left && should_turn_right && should_move_on)
7061         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7062                         rnd < 2 * rnd_value / 3 ? right_dir :
7063                         old_move_dir);
7064       else if (should_turn_left && should_turn_right)
7065         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7066       else if (should_turn_left && should_move_on)
7067         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7068       else if (should_turn_right && should_move_on)
7069         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7070       else if (should_turn_left)
7071         MovDir[x][y] = left_dir;
7072       else if (should_turn_right)
7073         MovDir[x][y] = right_dir;
7074       else if (should_move_on)
7075         MovDir[x][y] = old_move_dir;
7076     }
7077     else if (can_move_on && rnd > rnd_value / 8)
7078       MovDir[x][y] = old_move_dir;
7079     else if (can_turn_left && can_turn_right)
7080       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7081     else if (can_turn_left && rnd > rnd_value / 8)
7082       MovDir[x][y] = left_dir;
7083     else if (can_turn_right && rnd > rnd_value/8)
7084       MovDir[x][y] = right_dir;
7085     else
7086       MovDir[x][y] = back_dir;
7087
7088     xx = x + move_xy[MovDir[x][y]].dx;
7089     yy = y + move_xy[MovDir[x][y]].dy;
7090
7091     if (!IN_LEV_FIELD(xx, yy) ||
7092         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7093       MovDir[x][y] = old_move_dir;
7094
7095     MovDelay[x][y] = 0;
7096   }
7097   else if (element == EL_DRAGON)
7098   {
7099     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7100     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7101     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7102     int rnd_value = 24;
7103     int rnd = RND(rnd_value);
7104
7105     if (can_move_on && rnd > rnd_value / 8)
7106       MovDir[x][y] = old_move_dir;
7107     else if (can_turn_left && can_turn_right)
7108       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7109     else if (can_turn_left && rnd > rnd_value / 8)
7110       MovDir[x][y] = left_dir;
7111     else if (can_turn_right && rnd > rnd_value / 8)
7112       MovDir[x][y] = right_dir;
7113     else
7114       MovDir[x][y] = back_dir;
7115
7116     xx = x + move_xy[MovDir[x][y]].dx;
7117     yy = y + move_xy[MovDir[x][y]].dy;
7118
7119     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7120       MovDir[x][y] = old_move_dir;
7121
7122     MovDelay[x][y] = 0;
7123   }
7124   else if (element == EL_MOLE)
7125   {
7126     boolean can_move_on =
7127       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7128                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7129                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7130     if (!can_move_on)
7131     {
7132       boolean can_turn_left =
7133         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7134                               IS_AMOEBOID(Tile[left_x][left_y])));
7135
7136       boolean can_turn_right =
7137         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7138                               IS_AMOEBOID(Tile[right_x][right_y])));
7139
7140       if (can_turn_left && can_turn_right)
7141         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7142       else if (can_turn_left)
7143         MovDir[x][y] = left_dir;
7144       else
7145         MovDir[x][y] = right_dir;
7146     }
7147
7148     if (MovDir[x][y] != old_move_dir)
7149       MovDelay[x][y] = 9;
7150   }
7151   else if (element == EL_BALLOON)
7152   {
7153     MovDir[x][y] = game.wind_direction;
7154     MovDelay[x][y] = 0;
7155   }
7156   else if (element == EL_SPRING)
7157   {
7158     if (MovDir[x][y] & MV_HORIZONTAL)
7159     {
7160       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7161           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7162       {
7163         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7164         ResetGfxAnimation(move_x, move_y);
7165         TEST_DrawLevelField(move_x, move_y);
7166
7167         MovDir[x][y] = back_dir;
7168       }
7169       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7170                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7171         MovDir[x][y] = MV_NONE;
7172     }
7173
7174     MovDelay[x][y] = 0;
7175   }
7176   else if (element == EL_ROBOT ||
7177            element == EL_SATELLITE ||
7178            element == EL_PENGUIN ||
7179            element == EL_EMC_ANDROID)
7180   {
7181     int attr_x = -1, attr_y = -1;
7182
7183     if (game.all_players_gone)
7184     {
7185       attr_x = game.exit_x;
7186       attr_y = game.exit_y;
7187     }
7188     else
7189     {
7190       int i;
7191
7192       for (i = 0; i < MAX_PLAYERS; i++)
7193       {
7194         struct PlayerInfo *player = &stored_player[i];
7195         int jx = player->jx, jy = player->jy;
7196
7197         if (!player->active)
7198           continue;
7199
7200         if (attr_x == -1 ||
7201             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7202         {
7203           attr_x = jx;
7204           attr_y = jy;
7205         }
7206       }
7207     }
7208
7209     if (element == EL_ROBOT &&
7210         game.robot_wheel_x >= 0 &&
7211         game.robot_wheel_y >= 0 &&
7212         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7213          game.engine_version < VERSION_IDENT(3,1,0,0)))
7214     {
7215       attr_x = game.robot_wheel_x;
7216       attr_y = game.robot_wheel_y;
7217     }
7218
7219     if (element == EL_PENGUIN)
7220     {
7221       int i;
7222       static int xy[4][2] =
7223       {
7224         { 0, -1 },
7225         { -1, 0 },
7226         { +1, 0 },
7227         { 0, +1 }
7228       };
7229
7230       for (i = 0; i < NUM_DIRECTIONS; i++)
7231       {
7232         int ex = x + xy[i][0];
7233         int ey = y + xy[i][1];
7234
7235         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7236                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7237                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7238                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7239         {
7240           attr_x = ex;
7241           attr_y = ey;
7242           break;
7243         }
7244       }
7245     }
7246
7247     MovDir[x][y] = MV_NONE;
7248     if (attr_x < x)
7249       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7250     else if (attr_x > x)
7251       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7252     if (attr_y < y)
7253       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7254     else if (attr_y > y)
7255       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7256
7257     if (element == EL_ROBOT)
7258     {
7259       int newx, newy;
7260
7261       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7262         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7263       Moving2Blocked(x, y, &newx, &newy);
7264
7265       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7266         MovDelay[x][y] = 8 + 8 * !RND(3);
7267       else
7268         MovDelay[x][y] = 16;
7269     }
7270     else if (element == EL_PENGUIN)
7271     {
7272       int newx, newy;
7273
7274       MovDelay[x][y] = 1;
7275
7276       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7277       {
7278         boolean first_horiz = RND(2);
7279         int new_move_dir = MovDir[x][y];
7280
7281         MovDir[x][y] =
7282           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7283         Moving2Blocked(x, y, &newx, &newy);
7284
7285         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7286           return;
7287
7288         MovDir[x][y] =
7289           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7290         Moving2Blocked(x, y, &newx, &newy);
7291
7292         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7293           return;
7294
7295         MovDir[x][y] = old_move_dir;
7296         return;
7297       }
7298     }
7299     else if (element == EL_SATELLITE)
7300     {
7301       int newx, newy;
7302
7303       MovDelay[x][y] = 1;
7304
7305       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7306       {
7307         boolean first_horiz = RND(2);
7308         int new_move_dir = MovDir[x][y];
7309
7310         MovDir[x][y] =
7311           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7312         Moving2Blocked(x, y, &newx, &newy);
7313
7314         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7315           return;
7316
7317         MovDir[x][y] =
7318           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7319         Moving2Blocked(x, y, &newx, &newy);
7320
7321         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7322           return;
7323
7324         MovDir[x][y] = old_move_dir;
7325         return;
7326       }
7327     }
7328     else if (element == EL_EMC_ANDROID)
7329     {
7330       static int check_pos[16] =
7331       {
7332         -1,             //  0 => (invalid)
7333         7,              //  1 => MV_LEFT
7334         3,              //  2 => MV_RIGHT
7335         -1,             //  3 => (invalid)
7336         1,              //  4 =>            MV_UP
7337         0,              //  5 => MV_LEFT  | MV_UP
7338         2,              //  6 => MV_RIGHT | MV_UP
7339         -1,             //  7 => (invalid)
7340         5,              //  8 =>            MV_DOWN
7341         6,              //  9 => MV_LEFT  | MV_DOWN
7342         4,              // 10 => MV_RIGHT | MV_DOWN
7343         -1,             // 11 => (invalid)
7344         -1,             // 12 => (invalid)
7345         -1,             // 13 => (invalid)
7346         -1,             // 14 => (invalid)
7347         -1,             // 15 => (invalid)
7348       };
7349       static struct
7350       {
7351         int dx, dy;
7352         int dir;
7353       } check_xy[8] =
7354       {
7355         { -1, -1,       MV_LEFT  | MV_UP   },
7356         {  0, -1,                  MV_UP   },
7357         { +1, -1,       MV_RIGHT | MV_UP   },
7358         { +1,  0,       MV_RIGHT           },
7359         { +1, +1,       MV_RIGHT | MV_DOWN },
7360         {  0, +1,                  MV_DOWN },
7361         { -1, +1,       MV_LEFT  | MV_DOWN },
7362         { -1,  0,       MV_LEFT            },
7363       };
7364       int start_pos, check_order;
7365       boolean can_clone = FALSE;
7366       int i;
7367
7368       // check if there is any free field around current position
7369       for (i = 0; i < 8; i++)
7370       {
7371         int newx = x + check_xy[i].dx;
7372         int newy = y + check_xy[i].dy;
7373
7374         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7375         {
7376           can_clone = TRUE;
7377
7378           break;
7379         }
7380       }
7381
7382       if (can_clone)            // randomly find an element to clone
7383       {
7384         can_clone = FALSE;
7385
7386         start_pos = check_pos[RND(8)];
7387         check_order = (RND(2) ? -1 : +1);
7388
7389         for (i = 0; i < 8; i++)
7390         {
7391           int pos_raw = start_pos + i * check_order;
7392           int pos = (pos_raw + 8) % 8;
7393           int newx = x + check_xy[pos].dx;
7394           int newy = y + check_xy[pos].dy;
7395
7396           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7397           {
7398             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7399             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7400
7401             Store[x][y] = Tile[newx][newy];
7402
7403             can_clone = TRUE;
7404
7405             break;
7406           }
7407         }
7408       }
7409
7410       if (can_clone)            // randomly find a direction to move
7411       {
7412         can_clone = FALSE;
7413
7414         start_pos = check_pos[RND(8)];
7415         check_order = (RND(2) ? -1 : +1);
7416
7417         for (i = 0; i < 8; i++)
7418         {
7419           int pos_raw = start_pos + i * check_order;
7420           int pos = (pos_raw + 8) % 8;
7421           int newx = x + check_xy[pos].dx;
7422           int newy = y + check_xy[pos].dy;
7423           int new_move_dir = check_xy[pos].dir;
7424
7425           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7426           {
7427             MovDir[x][y] = new_move_dir;
7428             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7429
7430             can_clone = TRUE;
7431
7432             break;
7433           }
7434         }
7435       }
7436
7437       if (can_clone)            // cloning and moving successful
7438         return;
7439
7440       // cannot clone -- try to move towards player
7441
7442       start_pos = check_pos[MovDir[x][y] & 0x0f];
7443       check_order = (RND(2) ? -1 : +1);
7444
7445       for (i = 0; i < 3; i++)
7446       {
7447         // first check start_pos, then previous/next or (next/previous) pos
7448         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7449         int pos = (pos_raw + 8) % 8;
7450         int newx = x + check_xy[pos].dx;
7451         int newy = y + check_xy[pos].dy;
7452         int new_move_dir = check_xy[pos].dir;
7453
7454         if (IS_PLAYER(newx, newy))
7455           break;
7456
7457         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7458         {
7459           MovDir[x][y] = new_move_dir;
7460           MovDelay[x][y] = level.android_move_time * 8 + 1;
7461
7462           break;
7463         }
7464       }
7465     }
7466   }
7467   else if (move_pattern == MV_TURNING_LEFT ||
7468            move_pattern == MV_TURNING_RIGHT ||
7469            move_pattern == MV_TURNING_LEFT_RIGHT ||
7470            move_pattern == MV_TURNING_RIGHT_LEFT ||
7471            move_pattern == MV_TURNING_RANDOM ||
7472            move_pattern == MV_ALL_DIRECTIONS)
7473   {
7474     boolean can_turn_left =
7475       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7476     boolean can_turn_right =
7477       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7478
7479     if (element_info[element].move_stepsize == 0)       // "not moving"
7480       return;
7481
7482     if (move_pattern == MV_TURNING_LEFT)
7483       MovDir[x][y] = left_dir;
7484     else if (move_pattern == MV_TURNING_RIGHT)
7485       MovDir[x][y] = right_dir;
7486     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7487       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7488     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7489       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7490     else if (move_pattern == MV_TURNING_RANDOM)
7491       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7492                       can_turn_right && !can_turn_left ? right_dir :
7493                       RND(2) ? left_dir : right_dir);
7494     else if (can_turn_left && can_turn_right)
7495       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7496     else if (can_turn_left)
7497       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7498     else if (can_turn_right)
7499       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7500     else
7501       MovDir[x][y] = back_dir;
7502
7503     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7504   }
7505   else if (move_pattern == MV_HORIZONTAL ||
7506            move_pattern == MV_VERTICAL)
7507   {
7508     if (move_pattern & old_move_dir)
7509       MovDir[x][y] = back_dir;
7510     else if (move_pattern == MV_HORIZONTAL)
7511       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7512     else if (move_pattern == MV_VERTICAL)
7513       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7514
7515     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7516   }
7517   else if (move_pattern & MV_ANY_DIRECTION)
7518   {
7519     MovDir[x][y] = move_pattern;
7520     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7521   }
7522   else if (move_pattern & MV_WIND_DIRECTION)
7523   {
7524     MovDir[x][y] = game.wind_direction;
7525     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7526   }
7527   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7528   {
7529     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7530       MovDir[x][y] = left_dir;
7531     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7532       MovDir[x][y] = right_dir;
7533
7534     if (MovDir[x][y] != old_move_dir)
7535       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7536   }
7537   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7538   {
7539     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7540       MovDir[x][y] = right_dir;
7541     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7542       MovDir[x][y] = left_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_TOWARDS_PLAYER ||
7548            move_pattern == MV_AWAY_FROM_PLAYER)
7549   {
7550     int attr_x = -1, attr_y = -1;
7551     int newx, newy;
7552     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7553
7554     if (game.all_players_gone)
7555     {
7556       attr_x = game.exit_x;
7557       attr_y = game.exit_y;
7558     }
7559     else
7560     {
7561       int i;
7562
7563       for (i = 0; i < MAX_PLAYERS; i++)
7564       {
7565         struct PlayerInfo *player = &stored_player[i];
7566         int jx = player->jx, jy = player->jy;
7567
7568         if (!player->active)
7569           continue;
7570
7571         if (attr_x == -1 ||
7572             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7573         {
7574           attr_x = jx;
7575           attr_y = jy;
7576         }
7577       }
7578     }
7579
7580     MovDir[x][y] = MV_NONE;
7581     if (attr_x < x)
7582       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7583     else if (attr_x > x)
7584       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7585     if (attr_y < y)
7586       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7587     else if (attr_y > y)
7588       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7589
7590     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7591
7592     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7593     {
7594       boolean first_horiz = RND(2);
7595       int new_move_dir = MovDir[x][y];
7596
7597       if (element_info[element].move_stepsize == 0)     // "not moving"
7598       {
7599         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7600         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7601
7602         return;
7603       }
7604
7605       MovDir[x][y] =
7606         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7607       Moving2Blocked(x, y, &newx, &newy);
7608
7609       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7610         return;
7611
7612       MovDir[x][y] =
7613         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7614       Moving2Blocked(x, y, &newx, &newy);
7615
7616       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7617         return;
7618
7619       MovDir[x][y] = old_move_dir;
7620     }
7621   }
7622   else if (move_pattern == MV_WHEN_PUSHED ||
7623            move_pattern == MV_WHEN_DROPPED)
7624   {
7625     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7626       MovDir[x][y] = MV_NONE;
7627
7628     MovDelay[x][y] = 0;
7629   }
7630   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7631   {
7632     static int test_xy[7][2] =
7633     {
7634       { 0, -1 },
7635       { -1, 0 },
7636       { +1, 0 },
7637       { 0, +1 },
7638       { 0, -1 },
7639       { -1, 0 },
7640       { +1, 0 },
7641     };
7642     static int test_dir[7] =
7643     {
7644       MV_UP,
7645       MV_LEFT,
7646       MV_RIGHT,
7647       MV_DOWN,
7648       MV_UP,
7649       MV_LEFT,
7650       MV_RIGHT,
7651     };
7652     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7653     int move_preference = -1000000;     // start with very low preference
7654     int new_move_dir = MV_NONE;
7655     int start_test = RND(4);
7656     int i;
7657
7658     for (i = 0; i < NUM_DIRECTIONS; i++)
7659     {
7660       int move_dir = test_dir[start_test + i];
7661       int move_dir_preference;
7662
7663       xx = x + test_xy[start_test + i][0];
7664       yy = y + test_xy[start_test + i][1];
7665
7666       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7667           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7668       {
7669         new_move_dir = move_dir;
7670
7671         break;
7672       }
7673
7674       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7675         continue;
7676
7677       move_dir_preference = -1 * RunnerVisit[xx][yy];
7678       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7679         move_dir_preference = PlayerVisit[xx][yy];
7680
7681       if (move_dir_preference > move_preference)
7682       {
7683         // prefer field that has not been visited for the longest time
7684         move_preference = move_dir_preference;
7685         new_move_dir = move_dir;
7686       }
7687       else if (move_dir_preference == move_preference &&
7688                move_dir == old_move_dir)
7689       {
7690         // prefer last direction when all directions are preferred equally
7691         move_preference = move_dir_preference;
7692         new_move_dir = move_dir;
7693       }
7694     }
7695
7696     MovDir[x][y] = new_move_dir;
7697     if (old_move_dir != new_move_dir)
7698       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7699   }
7700 }
7701
7702 static void TurnRound(int x, int y)
7703 {
7704   int direction = MovDir[x][y];
7705
7706   TurnRoundExt(x, y);
7707
7708   GfxDir[x][y] = MovDir[x][y];
7709
7710   if (direction != MovDir[x][y])
7711     GfxFrame[x][y] = 0;
7712
7713   if (MovDelay[x][y])
7714     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7715
7716   ResetGfxFrame(x, y);
7717 }
7718
7719 static boolean JustBeingPushed(int x, int y)
7720 {
7721   int i;
7722
7723   for (i = 0; i < MAX_PLAYERS; i++)
7724   {
7725     struct PlayerInfo *player = &stored_player[i];
7726
7727     if (player->active && player->is_pushing && player->MovPos)
7728     {
7729       int next_jx = player->jx + (player->jx - player->last_jx);
7730       int next_jy = player->jy + (player->jy - player->last_jy);
7731
7732       if (x == next_jx && y == next_jy)
7733         return TRUE;
7734     }
7735   }
7736
7737   return FALSE;
7738 }
7739
7740 static void StartMoving(int x, int y)
7741 {
7742   boolean started_moving = FALSE;       // some elements can fall _and_ move
7743   int element = Tile[x][y];
7744
7745   if (Stop[x][y])
7746     return;
7747
7748   if (MovDelay[x][y] == 0)
7749     GfxAction[x][y] = ACTION_DEFAULT;
7750
7751   if (CAN_FALL(element) && y < lev_fieldy - 1)
7752   {
7753     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7754         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7755       if (JustBeingPushed(x, y))
7756         return;
7757
7758     if (element == EL_QUICKSAND_FULL)
7759     {
7760       if (IS_FREE(x, y + 1))
7761       {
7762         InitMovingField(x, y, MV_DOWN);
7763         started_moving = TRUE;
7764
7765         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7766 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7767         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7768           Store[x][y] = EL_ROCK;
7769 #else
7770         Store[x][y] = EL_ROCK;
7771 #endif
7772
7773         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7774       }
7775       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7776       {
7777         if (!MovDelay[x][y])
7778         {
7779           MovDelay[x][y] = TILEY + 1;
7780
7781           ResetGfxAnimation(x, y);
7782           ResetGfxAnimation(x, y + 1);
7783         }
7784
7785         if (MovDelay[x][y])
7786         {
7787           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7788           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7789
7790           MovDelay[x][y]--;
7791           if (MovDelay[x][y])
7792             return;
7793         }
7794
7795         Tile[x][y] = EL_QUICKSAND_EMPTY;
7796         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7797         Store[x][y + 1] = Store[x][y];
7798         Store[x][y] = 0;
7799
7800         PlayLevelSoundAction(x, y, ACTION_FILLING);
7801       }
7802       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7803       {
7804         if (!MovDelay[x][y])
7805         {
7806           MovDelay[x][y] = TILEY + 1;
7807
7808           ResetGfxAnimation(x, y);
7809           ResetGfxAnimation(x, y + 1);
7810         }
7811
7812         if (MovDelay[x][y])
7813         {
7814           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7815           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7816
7817           MovDelay[x][y]--;
7818           if (MovDelay[x][y])
7819             return;
7820         }
7821
7822         Tile[x][y] = EL_QUICKSAND_EMPTY;
7823         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7824         Store[x][y + 1] = Store[x][y];
7825         Store[x][y] = 0;
7826
7827         PlayLevelSoundAction(x, y, ACTION_FILLING);
7828       }
7829     }
7830     else if (element == EL_QUICKSAND_FAST_FULL)
7831     {
7832       if (IS_FREE(x, y + 1))
7833       {
7834         InitMovingField(x, y, MV_DOWN);
7835         started_moving = TRUE;
7836
7837         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7838 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7839         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7840           Store[x][y] = EL_ROCK;
7841 #else
7842         Store[x][y] = EL_ROCK;
7843 #endif
7844
7845         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7846       }
7847       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7848       {
7849         if (!MovDelay[x][y])
7850         {
7851           MovDelay[x][y] = TILEY + 1;
7852
7853           ResetGfxAnimation(x, y);
7854           ResetGfxAnimation(x, y + 1);
7855         }
7856
7857         if (MovDelay[x][y])
7858         {
7859           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7860           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7861
7862           MovDelay[x][y]--;
7863           if (MovDelay[x][y])
7864             return;
7865         }
7866
7867         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7868         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7869         Store[x][y + 1] = Store[x][y];
7870         Store[x][y] = 0;
7871
7872         PlayLevelSoundAction(x, y, ACTION_FILLING);
7873       }
7874       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7875       {
7876         if (!MovDelay[x][y])
7877         {
7878           MovDelay[x][y] = TILEY + 1;
7879
7880           ResetGfxAnimation(x, y);
7881           ResetGfxAnimation(x, y + 1);
7882         }
7883
7884         if (MovDelay[x][y])
7885         {
7886           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7887           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7888
7889           MovDelay[x][y]--;
7890           if (MovDelay[x][y])
7891             return;
7892         }
7893
7894         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7895         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7896         Store[x][y + 1] = Store[x][y];
7897         Store[x][y] = 0;
7898
7899         PlayLevelSoundAction(x, y, ACTION_FILLING);
7900       }
7901     }
7902     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7903              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7904     {
7905       InitMovingField(x, y, MV_DOWN);
7906       started_moving = TRUE;
7907
7908       Tile[x][y] = EL_QUICKSAND_FILLING;
7909       Store[x][y] = element;
7910
7911       PlayLevelSoundAction(x, y, ACTION_FILLING);
7912     }
7913     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7914              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7915     {
7916       InitMovingField(x, y, MV_DOWN);
7917       started_moving = TRUE;
7918
7919       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7920       Store[x][y] = element;
7921
7922       PlayLevelSoundAction(x, y, ACTION_FILLING);
7923     }
7924     else if (element == EL_MAGIC_WALL_FULL)
7925     {
7926       if (IS_FREE(x, y + 1))
7927       {
7928         InitMovingField(x, y, MV_DOWN);
7929         started_moving = TRUE;
7930
7931         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7932         Store[x][y] = EL_CHANGED(Store[x][y]);
7933       }
7934       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7935       {
7936         if (!MovDelay[x][y])
7937           MovDelay[x][y] = TILEY / 4 + 1;
7938
7939         if (MovDelay[x][y])
7940         {
7941           MovDelay[x][y]--;
7942           if (MovDelay[x][y])
7943             return;
7944         }
7945
7946         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7947         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7948         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7949         Store[x][y] = 0;
7950       }
7951     }
7952     else if (element == EL_BD_MAGIC_WALL_FULL)
7953     {
7954       if (IS_FREE(x, y + 1))
7955       {
7956         InitMovingField(x, y, MV_DOWN);
7957         started_moving = TRUE;
7958
7959         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7960         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7961       }
7962       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7963       {
7964         if (!MovDelay[x][y])
7965           MovDelay[x][y] = TILEY / 4 + 1;
7966
7967         if (MovDelay[x][y])
7968         {
7969           MovDelay[x][y]--;
7970           if (MovDelay[x][y])
7971             return;
7972         }
7973
7974         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7975         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7976         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7977         Store[x][y] = 0;
7978       }
7979     }
7980     else if (element == EL_DC_MAGIC_WALL_FULL)
7981     {
7982       if (IS_FREE(x, y + 1))
7983       {
7984         InitMovingField(x, y, MV_DOWN);
7985         started_moving = TRUE;
7986
7987         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7988         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7989       }
7990       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7991       {
7992         if (!MovDelay[x][y])
7993           MovDelay[x][y] = TILEY / 4 + 1;
7994
7995         if (MovDelay[x][y])
7996         {
7997           MovDelay[x][y]--;
7998           if (MovDelay[x][y])
7999             return;
8000         }
8001
8002         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8003         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8004         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8005         Store[x][y] = 0;
8006       }
8007     }
8008     else if ((CAN_PASS_MAGIC_WALL(element) &&
8009               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8010                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8011              (CAN_PASS_DC_MAGIC_WALL(element) &&
8012               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8013
8014     {
8015       InitMovingField(x, y, MV_DOWN);
8016       started_moving = TRUE;
8017
8018       Tile[x][y] =
8019         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8020          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8021          EL_DC_MAGIC_WALL_FILLING);
8022       Store[x][y] = element;
8023     }
8024     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8025     {
8026       SplashAcid(x, y + 1);
8027
8028       InitMovingField(x, y, MV_DOWN);
8029       started_moving = TRUE;
8030
8031       Store[x][y] = EL_ACID;
8032     }
8033     else if (
8034              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8035               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8036              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8037               CAN_FALL(element) && WasJustFalling[x][y] &&
8038               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8039
8040              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8041               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8042               (Tile[x][y + 1] == EL_BLOCKED)))
8043     {
8044       /* this is needed for a special case not covered by calling "Impact()"
8045          from "ContinueMoving()": if an element moves to a tile directly below
8046          another element which was just falling on that tile (which was empty
8047          in the previous frame), the falling element above would just stop
8048          instead of smashing the element below (in previous version, the above
8049          element was just checked for "moving" instead of "falling", resulting
8050          in incorrect smashes caused by horizontal movement of the above
8051          element; also, the case of the player being the element to smash was
8052          simply not covered here... :-/ ) */
8053
8054       CheckCollision[x][y] = 0;
8055       CheckImpact[x][y] = 0;
8056
8057       Impact(x, y);
8058     }
8059     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8060     {
8061       if (MovDir[x][y] == MV_NONE)
8062       {
8063         InitMovingField(x, y, MV_DOWN);
8064         started_moving = TRUE;
8065       }
8066     }
8067     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8068     {
8069       if (WasJustFalling[x][y]) // prevent animation from being restarted
8070         MovDir[x][y] = MV_DOWN;
8071
8072       InitMovingField(x, y, MV_DOWN);
8073       started_moving = TRUE;
8074     }
8075     else if (element == EL_AMOEBA_DROP)
8076     {
8077       Tile[x][y] = EL_AMOEBA_GROWING;
8078       Store[x][y] = EL_AMOEBA_WET;
8079     }
8080     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8081               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8082              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8083              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8084     {
8085       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8086                                 (IS_FREE(x - 1, y + 1) ||
8087                                  Tile[x - 1][y + 1] == EL_ACID));
8088       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8089                                 (IS_FREE(x + 1, y + 1) ||
8090                                  Tile[x + 1][y + 1] == EL_ACID));
8091       boolean can_fall_any  = (can_fall_left || can_fall_right);
8092       boolean can_fall_both = (can_fall_left && can_fall_right);
8093       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8094
8095       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8096       {
8097         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8098           can_fall_right = FALSE;
8099         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8100           can_fall_left = FALSE;
8101         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8102           can_fall_right = FALSE;
8103         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8104           can_fall_left = FALSE;
8105
8106         can_fall_any  = (can_fall_left || can_fall_right);
8107         can_fall_both = FALSE;
8108       }
8109
8110       if (can_fall_both)
8111       {
8112         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8113           can_fall_right = FALSE;       // slip down on left side
8114         else
8115           can_fall_left = !(can_fall_right = RND(2));
8116
8117         can_fall_both = FALSE;
8118       }
8119
8120       if (can_fall_any)
8121       {
8122         // if not determined otherwise, prefer left side for slipping down
8123         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8124         started_moving = TRUE;
8125       }
8126     }
8127     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8128     {
8129       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8130       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8131       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8132       int belt_dir = game.belt_dir[belt_nr];
8133
8134       if ((belt_dir == MV_LEFT  && left_is_free) ||
8135           (belt_dir == MV_RIGHT && right_is_free))
8136       {
8137         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8138
8139         InitMovingField(x, y, belt_dir);
8140         started_moving = TRUE;
8141
8142         Pushed[x][y] = TRUE;
8143         Pushed[nextx][y] = TRUE;
8144
8145         GfxAction[x][y] = ACTION_DEFAULT;
8146       }
8147       else
8148       {
8149         MovDir[x][y] = 0;       // if element was moving, stop it
8150       }
8151     }
8152   }
8153
8154   // not "else if" because of elements that can fall and move (EL_SPRING)
8155   if (CAN_MOVE(element) && !started_moving)
8156   {
8157     int move_pattern = element_info[element].move_pattern;
8158     int newx, newy;
8159
8160     Moving2Blocked(x, y, &newx, &newy);
8161
8162     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8163       return;
8164
8165     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8166         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8167     {
8168       WasJustMoving[x][y] = 0;
8169       CheckCollision[x][y] = 0;
8170
8171       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8172
8173       if (Tile[x][y] != element)        // element has changed
8174         return;
8175     }
8176
8177     if (!MovDelay[x][y])        // start new movement phase
8178     {
8179       // all objects that can change their move direction after each step
8180       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8181
8182       if (element != EL_YAMYAM &&
8183           element != EL_DARK_YAMYAM &&
8184           element != EL_PACMAN &&
8185           !(move_pattern & MV_ANY_DIRECTION) &&
8186           move_pattern != MV_TURNING_LEFT &&
8187           move_pattern != MV_TURNING_RIGHT &&
8188           move_pattern != MV_TURNING_LEFT_RIGHT &&
8189           move_pattern != MV_TURNING_RIGHT_LEFT &&
8190           move_pattern != MV_TURNING_RANDOM)
8191       {
8192         TurnRound(x, y);
8193
8194         if (MovDelay[x][y] && (element == EL_BUG ||
8195                                element == EL_SPACESHIP ||
8196                                element == EL_SP_SNIKSNAK ||
8197                                element == EL_SP_ELECTRON ||
8198                                element == EL_MOLE))
8199           TEST_DrawLevelField(x, y);
8200       }
8201     }
8202
8203     if (MovDelay[x][y])         // wait some time before next movement
8204     {
8205       MovDelay[x][y]--;
8206
8207       if (element == EL_ROBOT ||
8208           element == EL_YAMYAM ||
8209           element == EL_DARK_YAMYAM)
8210       {
8211         DrawLevelElementAnimationIfNeeded(x, y, element);
8212         PlayLevelSoundAction(x, y, ACTION_WAITING);
8213       }
8214       else if (element == EL_SP_ELECTRON)
8215         DrawLevelElementAnimationIfNeeded(x, y, element);
8216       else if (element == EL_DRAGON)
8217       {
8218         int i;
8219         int dir = MovDir[x][y];
8220         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8221         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8222         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8223                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8224                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8225                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8226         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8227
8228         GfxAction[x][y] = ACTION_ATTACKING;
8229
8230         if (IS_PLAYER(x, y))
8231           DrawPlayerField(x, y);
8232         else
8233           TEST_DrawLevelField(x, y);
8234
8235         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8236
8237         for (i = 1; i <= 3; i++)
8238         {
8239           int xx = x + i * dx;
8240           int yy = y + i * dy;
8241           int sx = SCREENX(xx);
8242           int sy = SCREENY(yy);
8243           int flame_graphic = graphic + (i - 1);
8244
8245           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8246             break;
8247
8248           if (MovDelay[x][y])
8249           {
8250             int flamed = MovingOrBlocked2Element(xx, yy);
8251
8252             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8253               Bang(xx, yy);
8254             else
8255               RemoveMovingField(xx, yy);
8256
8257             ChangeDelay[xx][yy] = 0;
8258
8259             Tile[xx][yy] = EL_FLAMES;
8260
8261             if (IN_SCR_FIELD(sx, sy))
8262             {
8263               TEST_DrawLevelFieldCrumbled(xx, yy);
8264               DrawGraphic(sx, sy, flame_graphic, frame);
8265             }
8266           }
8267           else
8268           {
8269             if (Tile[xx][yy] == EL_FLAMES)
8270               Tile[xx][yy] = EL_EMPTY;
8271             TEST_DrawLevelField(xx, yy);
8272           }
8273         }
8274       }
8275
8276       if (MovDelay[x][y])       // element still has to wait some time
8277       {
8278         PlayLevelSoundAction(x, y, ACTION_WAITING);
8279
8280         return;
8281       }
8282     }
8283
8284     // now make next step
8285
8286     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8287
8288     if (DONT_COLLIDE_WITH(element) &&
8289         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8290         !PLAYER_ENEMY_PROTECTED(newx, newy))
8291     {
8292       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8293
8294       return;
8295     }
8296
8297     else if (CAN_MOVE_INTO_ACID(element) &&
8298              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8299              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8300              (MovDir[x][y] == MV_DOWN ||
8301               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8302     {
8303       SplashAcid(newx, newy);
8304       Store[x][y] = EL_ACID;
8305     }
8306     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8307     {
8308       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8309           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8310           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8311           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8312       {
8313         RemoveField(x, y);
8314         TEST_DrawLevelField(x, y);
8315
8316         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8317         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8318           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8319
8320         game.friends_still_needed--;
8321         if (!game.friends_still_needed &&
8322             !game.GameOver &&
8323             game.all_players_gone)
8324           LevelSolved();
8325
8326         return;
8327       }
8328       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8329       {
8330         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8331           TEST_DrawLevelField(newx, newy);
8332         else
8333           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8334       }
8335       else if (!IS_FREE(newx, newy))
8336       {
8337         GfxAction[x][y] = ACTION_WAITING;
8338
8339         if (IS_PLAYER(x, y))
8340           DrawPlayerField(x, y);
8341         else
8342           TEST_DrawLevelField(x, y);
8343
8344         return;
8345       }
8346     }
8347     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8348     {
8349       if (IS_FOOD_PIG(Tile[newx][newy]))
8350       {
8351         if (IS_MOVING(newx, newy))
8352           RemoveMovingField(newx, newy);
8353         else
8354         {
8355           Tile[newx][newy] = EL_EMPTY;
8356           TEST_DrawLevelField(newx, newy);
8357         }
8358
8359         PlayLevelSound(x, y, SND_PIG_DIGGING);
8360       }
8361       else if (!IS_FREE(newx, newy))
8362       {
8363         if (IS_PLAYER(x, y))
8364           DrawPlayerField(x, y);
8365         else
8366           TEST_DrawLevelField(x, y);
8367
8368         return;
8369       }
8370     }
8371     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8372     {
8373       if (Store[x][y] != EL_EMPTY)
8374       {
8375         boolean can_clone = FALSE;
8376         int xx, yy;
8377
8378         // check if element to clone is still there
8379         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8380         {
8381           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8382           {
8383             can_clone = TRUE;
8384
8385             break;
8386           }
8387         }
8388
8389         // cannot clone or target field not free anymore -- do not clone
8390         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8391           Store[x][y] = EL_EMPTY;
8392       }
8393
8394       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8395       {
8396         if (IS_MV_DIAGONAL(MovDir[x][y]))
8397         {
8398           int diagonal_move_dir = MovDir[x][y];
8399           int stored = Store[x][y];
8400           int change_delay = 8;
8401           int graphic;
8402
8403           // android is moving diagonally
8404
8405           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8406
8407           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8408           GfxElement[x][y] = EL_EMC_ANDROID;
8409           GfxAction[x][y] = ACTION_SHRINKING;
8410           GfxDir[x][y] = diagonal_move_dir;
8411           ChangeDelay[x][y] = change_delay;
8412
8413           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8414                                    GfxDir[x][y]);
8415
8416           DrawLevelGraphicAnimation(x, y, graphic);
8417           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8418
8419           if (Tile[newx][newy] == EL_ACID)
8420           {
8421             SplashAcid(newx, newy);
8422
8423             return;
8424           }
8425
8426           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8427
8428           Store[newx][newy] = EL_EMC_ANDROID;
8429           GfxElement[newx][newy] = EL_EMC_ANDROID;
8430           GfxAction[newx][newy] = ACTION_GROWING;
8431           GfxDir[newx][newy] = diagonal_move_dir;
8432           ChangeDelay[newx][newy] = change_delay;
8433
8434           graphic = el_act_dir2img(GfxElement[newx][newy],
8435                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8436
8437           DrawLevelGraphicAnimation(newx, newy, graphic);
8438           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8439
8440           return;
8441         }
8442         else
8443         {
8444           Tile[newx][newy] = EL_EMPTY;
8445           TEST_DrawLevelField(newx, newy);
8446
8447           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8448         }
8449       }
8450       else if (!IS_FREE(newx, newy))
8451       {
8452         return;
8453       }
8454     }
8455     else if (IS_CUSTOM_ELEMENT(element) &&
8456              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8457     {
8458       if (!DigFieldByCE(newx, newy, element))
8459         return;
8460
8461       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8462       {
8463         RunnerVisit[x][y] = FrameCounter;
8464         PlayerVisit[x][y] /= 8;         // expire player visit path
8465       }
8466     }
8467     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8468     {
8469       if (!IS_FREE(newx, newy))
8470       {
8471         if (IS_PLAYER(x, y))
8472           DrawPlayerField(x, y);
8473         else
8474           TEST_DrawLevelField(x, y);
8475
8476         return;
8477       }
8478       else
8479       {
8480         boolean wanna_flame = !RND(10);
8481         int dx = newx - x, dy = newy - y;
8482         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8483         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8484         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8485                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8486         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8487                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8488
8489         if ((wanna_flame ||
8490              IS_CLASSIC_ENEMY(element1) ||
8491              IS_CLASSIC_ENEMY(element2)) &&
8492             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8493             element1 != EL_FLAMES && element2 != EL_FLAMES)
8494         {
8495           ResetGfxAnimation(x, y);
8496           GfxAction[x][y] = ACTION_ATTACKING;
8497
8498           if (IS_PLAYER(x, y))
8499             DrawPlayerField(x, y);
8500           else
8501             TEST_DrawLevelField(x, y);
8502
8503           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8504
8505           MovDelay[x][y] = 50;
8506
8507           Tile[newx][newy] = EL_FLAMES;
8508           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8509             Tile[newx1][newy1] = EL_FLAMES;
8510           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8511             Tile[newx2][newy2] = EL_FLAMES;
8512
8513           return;
8514         }
8515       }
8516     }
8517     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8518              Tile[newx][newy] == EL_DIAMOND)
8519     {
8520       if (IS_MOVING(newx, newy))
8521         RemoveMovingField(newx, newy);
8522       else
8523       {
8524         Tile[newx][newy] = EL_EMPTY;
8525         TEST_DrawLevelField(newx, newy);
8526       }
8527
8528       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8529     }
8530     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8531              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8532     {
8533       if (AmoebaNr[newx][newy])
8534       {
8535         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8536         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8537             Tile[newx][newy] == EL_BD_AMOEBA)
8538           AmoebaCnt[AmoebaNr[newx][newy]]--;
8539       }
8540
8541       if (IS_MOVING(newx, newy))
8542       {
8543         RemoveMovingField(newx, newy);
8544       }
8545       else
8546       {
8547         Tile[newx][newy] = EL_EMPTY;
8548         TEST_DrawLevelField(newx, newy);
8549       }
8550
8551       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8552     }
8553     else if ((element == EL_PACMAN || element == EL_MOLE)
8554              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8555     {
8556       if (AmoebaNr[newx][newy])
8557       {
8558         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8559         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8560             Tile[newx][newy] == EL_BD_AMOEBA)
8561           AmoebaCnt[AmoebaNr[newx][newy]]--;
8562       }
8563
8564       if (element == EL_MOLE)
8565       {
8566         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8567         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8568
8569         ResetGfxAnimation(x, y);
8570         GfxAction[x][y] = ACTION_DIGGING;
8571         TEST_DrawLevelField(x, y);
8572
8573         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8574
8575         return;                         // wait for shrinking amoeba
8576       }
8577       else      // element == EL_PACMAN
8578       {
8579         Tile[newx][newy] = EL_EMPTY;
8580         TEST_DrawLevelField(newx, newy);
8581         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8582       }
8583     }
8584     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8585              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8586               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8587     {
8588       // wait for shrinking amoeba to completely disappear
8589       return;
8590     }
8591     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8592     {
8593       // object was running against a wall
8594
8595       TurnRound(x, y);
8596
8597       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8598         DrawLevelElementAnimation(x, y, element);
8599
8600       if (DONT_TOUCH(element))
8601         TestIfBadThingTouchesPlayer(x, y);
8602
8603       return;
8604     }
8605
8606     InitMovingField(x, y, MovDir[x][y]);
8607
8608     PlayLevelSoundAction(x, y, ACTION_MOVING);
8609   }
8610
8611   if (MovDir[x][y])
8612     ContinueMoving(x, y);
8613 }
8614
8615 void ContinueMoving(int x, int y)
8616 {
8617   int element = Tile[x][y];
8618   struct ElementInfo *ei = &element_info[element];
8619   int direction = MovDir[x][y];
8620   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8621   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8622   int newx = x + dx, newy = y + dy;
8623   int stored = Store[x][y];
8624   int stored_new = Store[newx][newy];
8625   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8626   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8627   boolean last_line = (newy == lev_fieldy - 1);
8628   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8629
8630   if (pushed_by_player)         // special case: moving object pushed by player
8631   {
8632     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8633   }
8634   else if (use_step_delay)      // special case: moving object has step delay
8635   {
8636     if (!MovDelay[x][y])
8637       MovPos[x][y] += getElementMoveStepsize(x, y);
8638
8639     if (MovDelay[x][y])
8640       MovDelay[x][y]--;
8641     else
8642       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8643
8644     if (MovDelay[x][y])
8645     {
8646       TEST_DrawLevelField(x, y);
8647
8648       return;   // element is still waiting
8649     }
8650   }
8651   else                          // normal case: generically moving object
8652   {
8653     MovPos[x][y] += getElementMoveStepsize(x, y);
8654   }
8655
8656   if (ABS(MovPos[x][y]) < TILEX)
8657   {
8658     TEST_DrawLevelField(x, y);
8659
8660     return;     // element is still moving
8661   }
8662
8663   // element reached destination field
8664
8665   Tile[x][y] = EL_EMPTY;
8666   Tile[newx][newy] = element;
8667   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8668
8669   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8670   {
8671     element = Tile[newx][newy] = EL_ACID;
8672   }
8673   else if (element == EL_MOLE)
8674   {
8675     Tile[x][y] = EL_SAND;
8676
8677     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8678   }
8679   else if (element == EL_QUICKSAND_FILLING)
8680   {
8681     element = Tile[newx][newy] = get_next_element(element);
8682     Store[newx][newy] = Store[x][y];
8683   }
8684   else if (element == EL_QUICKSAND_EMPTYING)
8685   {
8686     Tile[x][y] = get_next_element(element);
8687     element = Tile[newx][newy] = Store[x][y];
8688   }
8689   else if (element == EL_QUICKSAND_FAST_FILLING)
8690   {
8691     element = Tile[newx][newy] = get_next_element(element);
8692     Store[newx][newy] = Store[x][y];
8693   }
8694   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8695   {
8696     Tile[x][y] = get_next_element(element);
8697     element = Tile[newx][newy] = Store[x][y];
8698   }
8699   else if (element == EL_MAGIC_WALL_FILLING)
8700   {
8701     element = Tile[newx][newy] = get_next_element(element);
8702     if (!game.magic_wall_active)
8703       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8704     Store[newx][newy] = Store[x][y];
8705   }
8706   else if (element == EL_MAGIC_WALL_EMPTYING)
8707   {
8708     Tile[x][y] = get_next_element(element);
8709     if (!game.magic_wall_active)
8710       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8711     element = Tile[newx][newy] = Store[x][y];
8712
8713     InitField(newx, newy, FALSE);
8714   }
8715   else if (element == EL_BD_MAGIC_WALL_FILLING)
8716   {
8717     element = Tile[newx][newy] = get_next_element(element);
8718     if (!game.magic_wall_active)
8719       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8720     Store[newx][newy] = Store[x][y];
8721   }
8722   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8723   {
8724     Tile[x][y] = get_next_element(element);
8725     if (!game.magic_wall_active)
8726       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8727     element = Tile[newx][newy] = Store[x][y];
8728
8729     InitField(newx, newy, FALSE);
8730   }
8731   else if (element == EL_DC_MAGIC_WALL_FILLING)
8732   {
8733     element = Tile[newx][newy] = get_next_element(element);
8734     if (!game.magic_wall_active)
8735       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8736     Store[newx][newy] = Store[x][y];
8737   }
8738   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8739   {
8740     Tile[x][y] = get_next_element(element);
8741     if (!game.magic_wall_active)
8742       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8743     element = Tile[newx][newy] = Store[x][y];
8744
8745     InitField(newx, newy, FALSE);
8746   }
8747   else if (element == EL_AMOEBA_DROPPING)
8748   {
8749     Tile[x][y] = get_next_element(element);
8750     element = Tile[newx][newy] = Store[x][y];
8751   }
8752   else if (element == EL_SOKOBAN_OBJECT)
8753   {
8754     if (Back[x][y])
8755       Tile[x][y] = Back[x][y];
8756
8757     if (Back[newx][newy])
8758       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8759
8760     Back[x][y] = Back[newx][newy] = 0;
8761   }
8762
8763   Store[x][y] = EL_EMPTY;
8764   MovPos[x][y] = 0;
8765   MovDir[x][y] = 0;
8766   MovDelay[x][y] = 0;
8767
8768   MovDelay[newx][newy] = 0;
8769
8770   if (CAN_CHANGE_OR_HAS_ACTION(element))
8771   {
8772     // copy element change control values to new field
8773     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8774     ChangePage[newx][newy]  = ChangePage[x][y];
8775     ChangeCount[newx][newy] = ChangeCount[x][y];
8776     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8777   }
8778
8779   CustomValue[newx][newy] = CustomValue[x][y];
8780
8781   ChangeDelay[x][y] = 0;
8782   ChangePage[x][y] = -1;
8783   ChangeCount[x][y] = 0;
8784   ChangeEvent[x][y] = -1;
8785
8786   CustomValue[x][y] = 0;
8787
8788   // copy animation control values to new field
8789   GfxFrame[newx][newy]  = GfxFrame[x][y];
8790   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8791   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8792   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8793
8794   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8795
8796   // some elements can leave other elements behind after moving
8797   if (ei->move_leave_element != EL_EMPTY &&
8798       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8799       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8800   {
8801     int move_leave_element = ei->move_leave_element;
8802
8803     // this makes it possible to leave the removed element again
8804     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8805       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8806
8807     Tile[x][y] = move_leave_element;
8808
8809     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8810       MovDir[x][y] = direction;
8811
8812     InitField(x, y, FALSE);
8813
8814     if (GFX_CRUMBLED(Tile[x][y]))
8815       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8816
8817     if (IS_PLAYER_ELEMENT(move_leave_element))
8818       RelocatePlayer(x, y, move_leave_element);
8819   }
8820
8821   // do this after checking for left-behind element
8822   ResetGfxAnimation(x, y);      // reset animation values for old field
8823
8824   if (!CAN_MOVE(element) ||
8825       (CAN_FALL(element) && direction == MV_DOWN &&
8826        (element == EL_SPRING ||
8827         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8828         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8829     GfxDir[x][y] = MovDir[newx][newy] = 0;
8830
8831   TEST_DrawLevelField(x, y);
8832   TEST_DrawLevelField(newx, newy);
8833
8834   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8835
8836   // prevent pushed element from moving on in pushed direction
8837   if (pushed_by_player && CAN_MOVE(element) &&
8838       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8839       !(element_info[element].move_pattern & direction))
8840     TurnRound(newx, newy);
8841
8842   // prevent elements on conveyor belt from moving on in last direction
8843   if (pushed_by_conveyor && CAN_FALL(element) &&
8844       direction & MV_HORIZONTAL)
8845     MovDir[newx][newy] = 0;
8846
8847   if (!pushed_by_player)
8848   {
8849     int nextx = newx + dx, nexty = newy + dy;
8850     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8851
8852     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8853
8854     if (CAN_FALL(element) && direction == MV_DOWN)
8855       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8856
8857     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8858       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8859
8860     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8861       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8862   }
8863
8864   if (DONT_TOUCH(element))      // object may be nasty to player or others
8865   {
8866     TestIfBadThingTouchesPlayer(newx, newy);
8867     TestIfBadThingTouchesFriend(newx, newy);
8868
8869     if (!IS_CUSTOM_ELEMENT(element))
8870       TestIfBadThingTouchesOtherBadThing(newx, newy);
8871   }
8872   else if (element == EL_PENGUIN)
8873     TestIfFriendTouchesBadThing(newx, newy);
8874
8875   if (DONT_GET_HIT_BY(element))
8876   {
8877     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8878   }
8879
8880   // give the player one last chance (one more frame) to move away
8881   if (CAN_FALL(element) && direction == MV_DOWN &&
8882       (last_line || (!IS_FREE(x, newy + 1) &&
8883                      (!IS_PLAYER(x, newy + 1) ||
8884                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8885     Impact(x, newy);
8886
8887   if (pushed_by_player && !game.use_change_when_pushing_bug)
8888   {
8889     int push_side = MV_DIR_OPPOSITE(direction);
8890     struct PlayerInfo *player = PLAYERINFO(x, y);
8891
8892     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8893                                player->index_bit, push_side);
8894     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8895                                         player->index_bit, push_side);
8896   }
8897
8898   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8899     MovDelay[newx][newy] = 1;
8900
8901   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8902
8903   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8904   TestIfElementHitsCustomElement(newx, newy, direction);
8905   TestIfPlayerTouchesCustomElement(newx, newy);
8906   TestIfElementTouchesCustomElement(newx, newy);
8907
8908   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8909       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8910     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8911                              MV_DIR_OPPOSITE(direction));
8912 }
8913
8914 int AmoebaNeighbourNr(int ax, int ay)
8915 {
8916   int i;
8917   int element = Tile[ax][ay];
8918   int group_nr = 0;
8919   static int xy[4][2] =
8920   {
8921     { 0, -1 },
8922     { -1, 0 },
8923     { +1, 0 },
8924     { 0, +1 }
8925   };
8926
8927   for (i = 0; i < NUM_DIRECTIONS; i++)
8928   {
8929     int x = ax + xy[i][0];
8930     int y = ay + xy[i][1];
8931
8932     if (!IN_LEV_FIELD(x, y))
8933       continue;
8934
8935     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8936       group_nr = AmoebaNr[x][y];
8937   }
8938
8939   return group_nr;
8940 }
8941
8942 static void AmoebaMerge(int ax, int ay)
8943 {
8944   int i, x, y, xx, yy;
8945   int new_group_nr = AmoebaNr[ax][ay];
8946   static int xy[4][2] =
8947   {
8948     { 0, -1 },
8949     { -1, 0 },
8950     { +1, 0 },
8951     { 0, +1 }
8952   };
8953
8954   if (new_group_nr == 0)
8955     return;
8956
8957   for (i = 0; i < NUM_DIRECTIONS; i++)
8958   {
8959     x = ax + xy[i][0];
8960     y = ay + xy[i][1];
8961
8962     if (!IN_LEV_FIELD(x, y))
8963       continue;
8964
8965     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8966          Tile[x][y] == EL_BD_AMOEBA ||
8967          Tile[x][y] == EL_AMOEBA_DEAD) &&
8968         AmoebaNr[x][y] != new_group_nr)
8969     {
8970       int old_group_nr = AmoebaNr[x][y];
8971
8972       if (old_group_nr == 0)
8973         return;
8974
8975       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8976       AmoebaCnt[old_group_nr] = 0;
8977       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8978       AmoebaCnt2[old_group_nr] = 0;
8979
8980       SCAN_PLAYFIELD(xx, yy)
8981       {
8982         if (AmoebaNr[xx][yy] == old_group_nr)
8983           AmoebaNr[xx][yy] = new_group_nr;
8984       }
8985     }
8986   }
8987 }
8988
8989 void AmoebaToDiamond(int ax, int ay)
8990 {
8991   int i, x, y;
8992
8993   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8994   {
8995     int group_nr = AmoebaNr[ax][ay];
8996
8997 #ifdef DEBUG
8998     if (group_nr == 0)
8999     {
9000       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9001       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9002
9003       return;
9004     }
9005 #endif
9006
9007     SCAN_PLAYFIELD(x, y)
9008     {
9009       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9010       {
9011         AmoebaNr[x][y] = 0;
9012         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9013       }
9014     }
9015
9016     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9017                             SND_AMOEBA_TURNING_TO_GEM :
9018                             SND_AMOEBA_TURNING_TO_ROCK));
9019     Bang(ax, ay);
9020   }
9021   else
9022   {
9023     static int xy[4][2] =
9024     {
9025       { 0, -1 },
9026       { -1, 0 },
9027       { +1, 0 },
9028       { 0, +1 }
9029     };
9030
9031     for (i = 0; i < NUM_DIRECTIONS; i++)
9032     {
9033       x = ax + xy[i][0];
9034       y = ay + xy[i][1];
9035
9036       if (!IN_LEV_FIELD(x, y))
9037         continue;
9038
9039       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9040       {
9041         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9042                               SND_AMOEBA_TURNING_TO_GEM :
9043                               SND_AMOEBA_TURNING_TO_ROCK));
9044         Bang(x, y);
9045       }
9046     }
9047   }
9048 }
9049
9050 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9051 {
9052   int x, y;
9053   int group_nr = AmoebaNr[ax][ay];
9054   boolean done = FALSE;
9055
9056 #ifdef DEBUG
9057   if (group_nr == 0)
9058   {
9059     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9060     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9061
9062     return;
9063   }
9064 #endif
9065
9066   SCAN_PLAYFIELD(x, y)
9067   {
9068     if (AmoebaNr[x][y] == group_nr &&
9069         (Tile[x][y] == EL_AMOEBA_DEAD ||
9070          Tile[x][y] == EL_BD_AMOEBA ||
9071          Tile[x][y] == EL_AMOEBA_GROWING))
9072     {
9073       AmoebaNr[x][y] = 0;
9074       Tile[x][y] = new_element;
9075       InitField(x, y, FALSE);
9076       TEST_DrawLevelField(x, y);
9077       done = TRUE;
9078     }
9079   }
9080
9081   if (done)
9082     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9083                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9084                             SND_BD_AMOEBA_TURNING_TO_GEM));
9085 }
9086
9087 static void AmoebaGrowing(int x, int y)
9088 {
9089   static unsigned int sound_delay = 0;
9090   static unsigned int sound_delay_value = 0;
9091
9092   if (!MovDelay[x][y])          // start new growing cycle
9093   {
9094     MovDelay[x][y] = 7;
9095
9096     if (DelayReached(&sound_delay, sound_delay_value))
9097     {
9098       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9099       sound_delay_value = 30;
9100     }
9101   }
9102
9103   if (MovDelay[x][y])           // wait some time before growing bigger
9104   {
9105     MovDelay[x][y]--;
9106     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9107     {
9108       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9109                                            6 - MovDelay[x][y]);
9110
9111       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9112     }
9113
9114     if (!MovDelay[x][y])
9115     {
9116       Tile[x][y] = Store[x][y];
9117       Store[x][y] = 0;
9118       TEST_DrawLevelField(x, y);
9119     }
9120   }
9121 }
9122
9123 static void AmoebaShrinking(int x, int y)
9124 {
9125   static unsigned int sound_delay = 0;
9126   static unsigned int sound_delay_value = 0;
9127
9128   if (!MovDelay[x][y])          // start new shrinking cycle
9129   {
9130     MovDelay[x][y] = 7;
9131
9132     if (DelayReached(&sound_delay, sound_delay_value))
9133       sound_delay_value = 30;
9134   }
9135
9136   if (MovDelay[x][y])           // wait some time before shrinking
9137   {
9138     MovDelay[x][y]--;
9139     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9140     {
9141       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9142                                            6 - MovDelay[x][y]);
9143
9144       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9145     }
9146
9147     if (!MovDelay[x][y])
9148     {
9149       Tile[x][y] = EL_EMPTY;
9150       TEST_DrawLevelField(x, y);
9151
9152       // don't let mole enter this field in this cycle;
9153       // (give priority to objects falling to this field from above)
9154       Stop[x][y] = TRUE;
9155     }
9156   }
9157 }
9158
9159 static void AmoebaReproduce(int ax, int ay)
9160 {
9161   int i;
9162   int element = Tile[ax][ay];
9163   int graphic = el2img(element);
9164   int newax = ax, neway = ay;
9165   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9166   static int xy[4][2] =
9167   {
9168     { 0, -1 },
9169     { -1, 0 },
9170     { +1, 0 },
9171     { 0, +1 }
9172   };
9173
9174   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9175   {
9176     Tile[ax][ay] = EL_AMOEBA_DEAD;
9177     TEST_DrawLevelField(ax, ay);
9178     return;
9179   }
9180
9181   if (IS_ANIMATED(graphic))
9182     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9183
9184   if (!MovDelay[ax][ay])        // start making new amoeba field
9185     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9186
9187   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9188   {
9189     MovDelay[ax][ay]--;
9190     if (MovDelay[ax][ay])
9191       return;
9192   }
9193
9194   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9195   {
9196     int start = RND(4);
9197     int x = ax + xy[start][0];
9198     int y = ay + xy[start][1];
9199
9200     if (!IN_LEV_FIELD(x, y))
9201       return;
9202
9203     if (IS_FREE(x, y) ||
9204         CAN_GROW_INTO(Tile[x][y]) ||
9205         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9206         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9207     {
9208       newax = x;
9209       neway = y;
9210     }
9211
9212     if (newax == ax && neway == ay)
9213       return;
9214   }
9215   else                          // normal or "filled" (BD style) amoeba
9216   {
9217     int start = RND(4);
9218     boolean waiting_for_player = FALSE;
9219
9220     for (i = 0; i < NUM_DIRECTIONS; i++)
9221     {
9222       int j = (start + i) % 4;
9223       int x = ax + xy[j][0];
9224       int y = ay + xy[j][1];
9225
9226       if (!IN_LEV_FIELD(x, y))
9227         continue;
9228
9229       if (IS_FREE(x, y) ||
9230           CAN_GROW_INTO(Tile[x][y]) ||
9231           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9232           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9233       {
9234         newax = x;
9235         neway = y;
9236         break;
9237       }
9238       else if (IS_PLAYER(x, y))
9239         waiting_for_player = TRUE;
9240     }
9241
9242     if (newax == ax && neway == ay)             // amoeba cannot grow
9243     {
9244       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9245       {
9246         Tile[ax][ay] = EL_AMOEBA_DEAD;
9247         TEST_DrawLevelField(ax, ay);
9248         AmoebaCnt[AmoebaNr[ax][ay]]--;
9249
9250         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9251         {
9252           if (element == EL_AMOEBA_FULL)
9253             AmoebaToDiamond(ax, ay);
9254           else if (element == EL_BD_AMOEBA)
9255             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9256         }
9257       }
9258       return;
9259     }
9260     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9261     {
9262       // amoeba gets larger by growing in some direction
9263
9264       int new_group_nr = AmoebaNr[ax][ay];
9265
9266 #ifdef DEBUG
9267   if (new_group_nr == 0)
9268   {
9269     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9270           newax, neway);
9271     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9272
9273     return;
9274   }
9275 #endif
9276
9277       AmoebaNr[newax][neway] = new_group_nr;
9278       AmoebaCnt[new_group_nr]++;
9279       AmoebaCnt2[new_group_nr]++;
9280
9281       // if amoeba touches other amoeba(s) after growing, unify them
9282       AmoebaMerge(newax, neway);
9283
9284       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9285       {
9286         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9287         return;
9288       }
9289     }
9290   }
9291
9292   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9293       (neway == lev_fieldy - 1 && newax != ax))
9294   {
9295     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9296     Store[newax][neway] = element;
9297   }
9298   else if (neway == ay || element == EL_EMC_DRIPPER)
9299   {
9300     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9301
9302     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9303   }
9304   else
9305   {
9306     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9307     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9308     Store[ax][ay] = EL_AMOEBA_DROP;
9309     ContinueMoving(ax, ay);
9310     return;
9311   }
9312
9313   TEST_DrawLevelField(newax, neway);
9314 }
9315
9316 static void Life(int ax, int ay)
9317 {
9318   int x1, y1, x2, y2;
9319   int life_time = 40;
9320   int element = Tile[ax][ay];
9321   int graphic = el2img(element);
9322   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9323                          level.biomaze);
9324   boolean changed = FALSE;
9325
9326   if (IS_ANIMATED(graphic))
9327     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9328
9329   if (Stop[ax][ay])
9330     return;
9331
9332   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9333     MovDelay[ax][ay] = life_time;
9334
9335   if (MovDelay[ax][ay])         // wait some time before next cycle
9336   {
9337     MovDelay[ax][ay]--;
9338     if (MovDelay[ax][ay])
9339       return;
9340   }
9341
9342   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9343   {
9344     int xx = ax+x1, yy = ay+y1;
9345     int old_element = Tile[xx][yy];
9346     int num_neighbours = 0;
9347
9348     if (!IN_LEV_FIELD(xx, yy))
9349       continue;
9350
9351     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9352     {
9353       int x = xx+x2, y = yy+y2;
9354
9355       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9356         continue;
9357
9358       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9359       boolean is_neighbour = FALSE;
9360
9361       if (level.use_life_bugs)
9362         is_neighbour =
9363           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9364            (IS_FREE(x, y)                             &&  Stop[x][y]));
9365       else
9366         is_neighbour =
9367           (Last[x][y] == element || is_player_cell);
9368
9369       if (is_neighbour)
9370         num_neighbours++;
9371     }
9372
9373     boolean is_free = FALSE;
9374
9375     if (level.use_life_bugs)
9376       is_free = (IS_FREE(xx, yy));
9377     else
9378       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9379
9380     if (xx == ax && yy == ay)           // field in the middle
9381     {
9382       if (num_neighbours < life_parameter[0] ||
9383           num_neighbours > life_parameter[1])
9384       {
9385         Tile[xx][yy] = EL_EMPTY;
9386         if (Tile[xx][yy] != old_element)
9387           TEST_DrawLevelField(xx, yy);
9388         Stop[xx][yy] = TRUE;
9389         changed = TRUE;
9390       }
9391     }
9392     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9393     {                                   // free border field
9394       if (num_neighbours >= life_parameter[2] &&
9395           num_neighbours <= life_parameter[3])
9396       {
9397         Tile[xx][yy] = element;
9398         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9399         if (Tile[xx][yy] != old_element)
9400           TEST_DrawLevelField(xx, yy);
9401         Stop[xx][yy] = TRUE;
9402         changed = TRUE;
9403       }
9404     }
9405   }
9406
9407   if (changed)
9408     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9409                    SND_GAME_OF_LIFE_GROWING);
9410 }
9411
9412 static void InitRobotWheel(int x, int y)
9413 {
9414   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9415 }
9416
9417 static void RunRobotWheel(int x, int y)
9418 {
9419   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9420 }
9421
9422 static void StopRobotWheel(int x, int y)
9423 {
9424   if (game.robot_wheel_x == x &&
9425       game.robot_wheel_y == y)
9426   {
9427     game.robot_wheel_x = -1;
9428     game.robot_wheel_y = -1;
9429     game.robot_wheel_active = FALSE;
9430   }
9431 }
9432
9433 static void InitTimegateWheel(int x, int y)
9434 {
9435   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9436 }
9437
9438 static void RunTimegateWheel(int x, int y)
9439 {
9440   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9441 }
9442
9443 static void InitMagicBallDelay(int x, int y)
9444 {
9445   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9446 }
9447
9448 static void ActivateMagicBall(int bx, int by)
9449 {
9450   int x, y;
9451
9452   if (level.ball_random)
9453   {
9454     int pos_border = RND(8);    // select one of the eight border elements
9455     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9456     int xx = pos_content % 3;
9457     int yy = pos_content / 3;
9458
9459     x = bx - 1 + xx;
9460     y = by - 1 + yy;
9461
9462     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9463       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9464   }
9465   else
9466   {
9467     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9468     {
9469       int xx = x - bx + 1;
9470       int yy = y - by + 1;
9471
9472       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9473         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9474     }
9475   }
9476
9477   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9478 }
9479
9480 static void CheckExit(int x, int y)
9481 {
9482   if (game.gems_still_needed > 0 ||
9483       game.sokoban_fields_still_needed > 0 ||
9484       game.sokoban_objects_still_needed > 0 ||
9485       game.lights_still_needed > 0)
9486   {
9487     int element = Tile[x][y];
9488     int graphic = el2img(element);
9489
9490     if (IS_ANIMATED(graphic))
9491       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9492
9493     return;
9494   }
9495
9496   // do not re-open exit door closed after last player
9497   if (game.all_players_gone)
9498     return;
9499
9500   Tile[x][y] = EL_EXIT_OPENING;
9501
9502   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9503 }
9504
9505 static void CheckExitEM(int x, int y)
9506 {
9507   if (game.gems_still_needed > 0 ||
9508       game.sokoban_fields_still_needed > 0 ||
9509       game.sokoban_objects_still_needed > 0 ||
9510       game.lights_still_needed > 0)
9511   {
9512     int element = Tile[x][y];
9513     int graphic = el2img(element);
9514
9515     if (IS_ANIMATED(graphic))
9516       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9517
9518     return;
9519   }
9520
9521   // do not re-open exit door closed after last player
9522   if (game.all_players_gone)
9523     return;
9524
9525   Tile[x][y] = EL_EM_EXIT_OPENING;
9526
9527   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9528 }
9529
9530 static void CheckExitSteel(int x, int y)
9531 {
9532   if (game.gems_still_needed > 0 ||
9533       game.sokoban_fields_still_needed > 0 ||
9534       game.sokoban_objects_still_needed > 0 ||
9535       game.lights_still_needed > 0)
9536   {
9537     int element = Tile[x][y];
9538     int graphic = el2img(element);
9539
9540     if (IS_ANIMATED(graphic))
9541       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9542
9543     return;
9544   }
9545
9546   // do not re-open exit door closed after last player
9547   if (game.all_players_gone)
9548     return;
9549
9550   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9551
9552   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9553 }
9554
9555 static void CheckExitSteelEM(int x, int y)
9556 {
9557   if (game.gems_still_needed > 0 ||
9558       game.sokoban_fields_still_needed > 0 ||
9559       game.sokoban_objects_still_needed > 0 ||
9560       game.lights_still_needed > 0)
9561   {
9562     int element = Tile[x][y];
9563     int graphic = el2img(element);
9564
9565     if (IS_ANIMATED(graphic))
9566       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9567
9568     return;
9569   }
9570
9571   // do not re-open exit door closed after last player
9572   if (game.all_players_gone)
9573     return;
9574
9575   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9576
9577   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9578 }
9579
9580 static void CheckExitSP(int x, int y)
9581 {
9582   if (game.gems_still_needed > 0)
9583   {
9584     int element = Tile[x][y];
9585     int graphic = el2img(element);
9586
9587     if (IS_ANIMATED(graphic))
9588       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9589
9590     return;
9591   }
9592
9593   // do not re-open exit door closed after last player
9594   if (game.all_players_gone)
9595     return;
9596
9597   Tile[x][y] = EL_SP_EXIT_OPENING;
9598
9599   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9600 }
9601
9602 static void CloseAllOpenTimegates(void)
9603 {
9604   int x, y;
9605
9606   SCAN_PLAYFIELD(x, y)
9607   {
9608     int element = Tile[x][y];
9609
9610     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9611     {
9612       Tile[x][y] = EL_TIMEGATE_CLOSING;
9613
9614       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9615     }
9616   }
9617 }
9618
9619 static void DrawTwinkleOnField(int x, int y)
9620 {
9621   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9622     return;
9623
9624   if (Tile[x][y] == EL_BD_DIAMOND)
9625     return;
9626
9627   if (MovDelay[x][y] == 0)      // next animation frame
9628     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9629
9630   if (MovDelay[x][y] != 0)      // wait some time before next frame
9631   {
9632     MovDelay[x][y]--;
9633
9634     DrawLevelElementAnimation(x, y, Tile[x][y]);
9635
9636     if (MovDelay[x][y] != 0)
9637     {
9638       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9639                                            10 - MovDelay[x][y]);
9640
9641       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9642     }
9643   }
9644 }
9645
9646 static void MauerWaechst(int x, int y)
9647 {
9648   int delay = 6;
9649
9650   if (!MovDelay[x][y])          // next animation frame
9651     MovDelay[x][y] = 3 * delay;
9652
9653   if (MovDelay[x][y])           // wait some time before next frame
9654   {
9655     MovDelay[x][y]--;
9656
9657     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9658     {
9659       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9660       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9661
9662       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9663     }
9664
9665     if (!MovDelay[x][y])
9666     {
9667       if (MovDir[x][y] == MV_LEFT)
9668       {
9669         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9670           TEST_DrawLevelField(x - 1, y);
9671       }
9672       else if (MovDir[x][y] == MV_RIGHT)
9673       {
9674         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9675           TEST_DrawLevelField(x + 1, y);
9676       }
9677       else if (MovDir[x][y] == MV_UP)
9678       {
9679         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9680           TEST_DrawLevelField(x, y - 1);
9681       }
9682       else
9683       {
9684         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9685           TEST_DrawLevelField(x, y + 1);
9686       }
9687
9688       Tile[x][y] = Store[x][y];
9689       Store[x][y] = 0;
9690       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9691       TEST_DrawLevelField(x, y);
9692     }
9693   }
9694 }
9695
9696 static void MauerAbleger(int ax, int ay)
9697 {
9698   int element = Tile[ax][ay];
9699   int graphic = el2img(element);
9700   boolean oben_frei = FALSE, unten_frei = FALSE;
9701   boolean links_frei = FALSE, rechts_frei = FALSE;
9702   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9703   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9704   boolean new_wall = FALSE;
9705
9706   if (IS_ANIMATED(graphic))
9707     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9708
9709   if (!MovDelay[ax][ay])        // start building new wall
9710     MovDelay[ax][ay] = 6;
9711
9712   if (MovDelay[ax][ay])         // wait some time before building new wall
9713   {
9714     MovDelay[ax][ay]--;
9715     if (MovDelay[ax][ay])
9716       return;
9717   }
9718
9719   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9720     oben_frei = TRUE;
9721   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9722     unten_frei = TRUE;
9723   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9724     links_frei = TRUE;
9725   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9726     rechts_frei = TRUE;
9727
9728   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9729       element == EL_EXPANDABLE_WALL_ANY)
9730   {
9731     if (oben_frei)
9732     {
9733       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9734       Store[ax][ay-1] = element;
9735       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9736       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9737         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9738                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9739       new_wall = TRUE;
9740     }
9741     if (unten_frei)
9742     {
9743       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9744       Store[ax][ay+1] = element;
9745       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9746       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9747         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9748                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9749       new_wall = TRUE;
9750     }
9751   }
9752
9753   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9754       element == EL_EXPANDABLE_WALL_ANY ||
9755       element == EL_EXPANDABLE_WALL ||
9756       element == EL_BD_EXPANDABLE_WALL)
9757   {
9758     if (links_frei)
9759     {
9760       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9761       Store[ax-1][ay] = element;
9762       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9763       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9764         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9765                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9766       new_wall = TRUE;
9767     }
9768
9769     if (rechts_frei)
9770     {
9771       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9772       Store[ax+1][ay] = element;
9773       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9774       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9775         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9776                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9777       new_wall = TRUE;
9778     }
9779   }
9780
9781   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9782     TEST_DrawLevelField(ax, ay);
9783
9784   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9785     oben_massiv = TRUE;
9786   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9787     unten_massiv = TRUE;
9788   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9789     links_massiv = TRUE;
9790   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9791     rechts_massiv = TRUE;
9792
9793   if (((oben_massiv && unten_massiv) ||
9794        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9795        element == EL_EXPANDABLE_WALL) &&
9796       ((links_massiv && rechts_massiv) ||
9797        element == EL_EXPANDABLE_WALL_VERTICAL))
9798     Tile[ax][ay] = EL_WALL;
9799
9800   if (new_wall)
9801     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9802 }
9803
9804 static void MauerAblegerStahl(int ax, int ay)
9805 {
9806   int element = Tile[ax][ay];
9807   int graphic = el2img(element);
9808   boolean oben_frei = FALSE, unten_frei = FALSE;
9809   boolean links_frei = FALSE, rechts_frei = FALSE;
9810   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9811   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9812   boolean new_wall = FALSE;
9813
9814   if (IS_ANIMATED(graphic))
9815     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9816
9817   if (!MovDelay[ax][ay])        // start building new wall
9818     MovDelay[ax][ay] = 6;
9819
9820   if (MovDelay[ax][ay])         // wait some time before building new wall
9821   {
9822     MovDelay[ax][ay]--;
9823     if (MovDelay[ax][ay])
9824       return;
9825   }
9826
9827   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9828     oben_frei = TRUE;
9829   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9830     unten_frei = TRUE;
9831   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9832     links_frei = TRUE;
9833   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9834     rechts_frei = TRUE;
9835
9836   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9837       element == EL_EXPANDABLE_STEELWALL_ANY)
9838   {
9839     if (oben_frei)
9840     {
9841       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9842       Store[ax][ay-1] = element;
9843       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9844       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9845         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9846                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9847       new_wall = TRUE;
9848     }
9849     if (unten_frei)
9850     {
9851       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9852       Store[ax][ay+1] = element;
9853       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9854       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9855         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9856                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9857       new_wall = TRUE;
9858     }
9859   }
9860
9861   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9862       element == EL_EXPANDABLE_STEELWALL_ANY)
9863   {
9864     if (links_frei)
9865     {
9866       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9867       Store[ax-1][ay] = element;
9868       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9869       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9870         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9871                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9872       new_wall = TRUE;
9873     }
9874
9875     if (rechts_frei)
9876     {
9877       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9878       Store[ax+1][ay] = element;
9879       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9880       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9881         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9882                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9883       new_wall = TRUE;
9884     }
9885   }
9886
9887   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9888     oben_massiv = TRUE;
9889   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9890     unten_massiv = TRUE;
9891   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9892     links_massiv = TRUE;
9893   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9894     rechts_massiv = TRUE;
9895
9896   if (((oben_massiv && unten_massiv) ||
9897        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9898       ((links_massiv && rechts_massiv) ||
9899        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9900     Tile[ax][ay] = EL_STEELWALL;
9901
9902   if (new_wall)
9903     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9904 }
9905
9906 static void CheckForDragon(int x, int y)
9907 {
9908   int i, j;
9909   boolean dragon_found = FALSE;
9910   static int xy[4][2] =
9911   {
9912     { 0, -1 },
9913     { -1, 0 },
9914     { +1, 0 },
9915     { 0, +1 }
9916   };
9917
9918   for (i = 0; i < NUM_DIRECTIONS; i++)
9919   {
9920     for (j = 0; j < 4; j++)
9921     {
9922       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9923
9924       if (IN_LEV_FIELD(xx, yy) &&
9925           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9926       {
9927         if (Tile[xx][yy] == EL_DRAGON)
9928           dragon_found = TRUE;
9929       }
9930       else
9931         break;
9932     }
9933   }
9934
9935   if (!dragon_found)
9936   {
9937     for (i = 0; i < NUM_DIRECTIONS; i++)
9938     {
9939       for (j = 0; j < 3; j++)
9940       {
9941         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9942   
9943         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9944         {
9945           Tile[xx][yy] = EL_EMPTY;
9946           TEST_DrawLevelField(xx, yy);
9947         }
9948         else
9949           break;
9950       }
9951     }
9952   }
9953 }
9954
9955 static void InitBuggyBase(int x, int y)
9956 {
9957   int element = Tile[x][y];
9958   int activating_delay = FRAMES_PER_SECOND / 4;
9959
9960   ChangeDelay[x][y] =
9961     (element == EL_SP_BUGGY_BASE ?
9962      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9963      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9964      activating_delay :
9965      element == EL_SP_BUGGY_BASE_ACTIVE ?
9966      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9967 }
9968
9969 static void WarnBuggyBase(int x, int y)
9970 {
9971   int i;
9972   static int xy[4][2] =
9973   {
9974     { 0, -1 },
9975     { -1, 0 },
9976     { +1, 0 },
9977     { 0, +1 }
9978   };
9979
9980   for (i = 0; i < NUM_DIRECTIONS; i++)
9981   {
9982     int xx = x + xy[i][0];
9983     int yy = y + xy[i][1];
9984
9985     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9986     {
9987       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9988
9989       break;
9990     }
9991   }
9992 }
9993
9994 static void InitTrap(int x, int y)
9995 {
9996   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9997 }
9998
9999 static void ActivateTrap(int x, int y)
10000 {
10001   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10002 }
10003
10004 static void ChangeActiveTrap(int x, int y)
10005 {
10006   int graphic = IMG_TRAP_ACTIVE;
10007
10008   // if new animation frame was drawn, correct crumbled sand border
10009   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10010     TEST_DrawLevelFieldCrumbled(x, y);
10011 }
10012
10013 static int getSpecialActionElement(int element, int number, int base_element)
10014 {
10015   return (element != EL_EMPTY ? element :
10016           number != -1 ? base_element + number - 1 :
10017           EL_EMPTY);
10018 }
10019
10020 static int getModifiedActionNumber(int value_old, int operator, int operand,
10021                                    int value_min, int value_max)
10022 {
10023   int value_new = (operator == CA_MODE_SET      ? operand :
10024                    operator == CA_MODE_ADD      ? value_old + operand :
10025                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10026                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10027                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10028                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10029                    value_old);
10030
10031   return (value_new < value_min ? value_min :
10032           value_new > value_max ? value_max :
10033           value_new);
10034 }
10035
10036 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10037 {
10038   struct ElementInfo *ei = &element_info[element];
10039   struct ElementChangeInfo *change = &ei->change_page[page];
10040   int target_element = change->target_element;
10041   int action_type = change->action_type;
10042   int action_mode = change->action_mode;
10043   int action_arg = change->action_arg;
10044   int action_element = change->action_element;
10045   int i;
10046
10047   if (!change->has_action)
10048     return;
10049
10050   // ---------- determine action paramater values -----------------------------
10051
10052   int level_time_value =
10053     (level.time > 0 ? TimeLeft :
10054      TimePlayed);
10055
10056   int action_arg_element_raw =
10057     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10058      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10059      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10060      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10061      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10062      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10063      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10064      EL_EMPTY);
10065   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10066
10067   int action_arg_direction =
10068     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10069      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10070      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10071      change->actual_trigger_side :
10072      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10073      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10074      MV_NONE);
10075
10076   int action_arg_number_min =
10077     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10078      CA_ARG_MIN);
10079
10080   int action_arg_number_max =
10081     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10082      action_type == CA_SET_LEVEL_GEMS ? 999 :
10083      action_type == CA_SET_LEVEL_TIME ? 9999 :
10084      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10085      action_type == CA_SET_CE_VALUE ? 9999 :
10086      action_type == CA_SET_CE_SCORE ? 9999 :
10087      CA_ARG_MAX);
10088
10089   int action_arg_number_reset =
10090     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10091      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10092      action_type == CA_SET_LEVEL_TIME ? level.time :
10093      action_type == CA_SET_LEVEL_SCORE ? 0 :
10094      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10095      action_type == CA_SET_CE_SCORE ? 0 :
10096      0);
10097
10098   int action_arg_number =
10099     (action_arg <= CA_ARG_MAX ? action_arg :
10100      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10101      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10102      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10103      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10104      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10105      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10106      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10107      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10108      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10109      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10110      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10111      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10112      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10113      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10114      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10115      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10116      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10117      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10118      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10119      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10120      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10121      -1);
10122
10123   int action_arg_number_old =
10124     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10125      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10126      action_type == CA_SET_LEVEL_SCORE ? game.score :
10127      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10128      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10129      0);
10130
10131   int action_arg_number_new =
10132     getModifiedActionNumber(action_arg_number_old,
10133                             action_mode, action_arg_number,
10134                             action_arg_number_min, action_arg_number_max);
10135
10136   int trigger_player_bits =
10137     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10138      change->actual_trigger_player_bits : change->trigger_player);
10139
10140   int action_arg_player_bits =
10141     (action_arg >= CA_ARG_PLAYER_1 &&
10142      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10143      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10144      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10145      PLAYER_BITS_ANY);
10146
10147   // ---------- execute action  -----------------------------------------------
10148
10149   switch (action_type)
10150   {
10151     case CA_NO_ACTION:
10152     {
10153       return;
10154     }
10155
10156     // ---------- level actions  ----------------------------------------------
10157
10158     case CA_RESTART_LEVEL:
10159     {
10160       game.restart_level = TRUE;
10161
10162       break;
10163     }
10164
10165     case CA_SHOW_ENVELOPE:
10166     {
10167       int element = getSpecialActionElement(action_arg_element,
10168                                             action_arg_number, EL_ENVELOPE_1);
10169
10170       if (IS_ENVELOPE(element))
10171         local_player->show_envelope = element;
10172
10173       break;
10174     }
10175
10176     case CA_SET_LEVEL_TIME:
10177     {
10178       if (level.time > 0)       // only modify limited time value
10179       {
10180         TimeLeft = action_arg_number_new;
10181
10182         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10183
10184         DisplayGameControlValues();
10185
10186         if (!TimeLeft && setup.time_limit)
10187           for (i = 0; i < MAX_PLAYERS; i++)
10188             KillPlayer(&stored_player[i]);
10189       }
10190
10191       break;
10192     }
10193
10194     case CA_SET_LEVEL_SCORE:
10195     {
10196       game.score = action_arg_number_new;
10197
10198       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10199
10200       DisplayGameControlValues();
10201
10202       break;
10203     }
10204
10205     case CA_SET_LEVEL_GEMS:
10206     {
10207       game.gems_still_needed = action_arg_number_new;
10208
10209       game.snapshot.collected_item = TRUE;
10210
10211       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10212
10213       DisplayGameControlValues();
10214
10215       break;
10216     }
10217
10218     case CA_SET_LEVEL_WIND:
10219     {
10220       game.wind_direction = action_arg_direction;
10221
10222       break;
10223     }
10224
10225     case CA_SET_LEVEL_RANDOM_SEED:
10226     {
10227       // ensure that setting a new random seed while playing is predictable
10228       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10229
10230       break;
10231     }
10232
10233     // ---------- player actions  ---------------------------------------------
10234
10235     case CA_MOVE_PLAYER:
10236     case CA_MOVE_PLAYER_NEW:
10237     {
10238       // automatically move to the next field in specified direction
10239       for (i = 0; i < MAX_PLAYERS; i++)
10240         if (trigger_player_bits & (1 << i))
10241           if (action_type == CA_MOVE_PLAYER ||
10242               stored_player[i].MovPos == 0)
10243             stored_player[i].programmed_action = action_arg_direction;
10244
10245       break;
10246     }
10247
10248     case CA_EXIT_PLAYER:
10249     {
10250       for (i = 0; i < MAX_PLAYERS; i++)
10251         if (action_arg_player_bits & (1 << i))
10252           ExitPlayer(&stored_player[i]);
10253
10254       if (game.players_still_needed == 0)
10255         LevelSolved();
10256
10257       break;
10258     }
10259
10260     case CA_KILL_PLAYER:
10261     {
10262       for (i = 0; i < MAX_PLAYERS; i++)
10263         if (action_arg_player_bits & (1 << i))
10264           KillPlayer(&stored_player[i]);
10265
10266       break;
10267     }
10268
10269     case CA_SET_PLAYER_KEYS:
10270     {
10271       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10272       int element = getSpecialActionElement(action_arg_element,
10273                                             action_arg_number, EL_KEY_1);
10274
10275       if (IS_KEY(element))
10276       {
10277         for (i = 0; i < MAX_PLAYERS; i++)
10278         {
10279           if (trigger_player_bits & (1 << i))
10280           {
10281             stored_player[i].key[KEY_NR(element)] = key_state;
10282
10283             DrawGameDoorValues();
10284           }
10285         }
10286       }
10287
10288       break;
10289     }
10290
10291     case CA_SET_PLAYER_SPEED:
10292     {
10293       for (i = 0; i < MAX_PLAYERS; i++)
10294       {
10295         if (trigger_player_bits & (1 << i))
10296         {
10297           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10298
10299           if (action_arg == CA_ARG_SPEED_FASTER &&
10300               stored_player[i].cannot_move)
10301           {
10302             action_arg_number = STEPSIZE_VERY_SLOW;
10303           }
10304           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10305                    action_arg == CA_ARG_SPEED_FASTER)
10306           {
10307             action_arg_number = 2;
10308             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10309                            CA_MODE_MULTIPLY);
10310           }
10311           else if (action_arg == CA_ARG_NUMBER_RESET)
10312           {
10313             action_arg_number = level.initial_player_stepsize[i];
10314           }
10315
10316           move_stepsize =
10317             getModifiedActionNumber(move_stepsize,
10318                                     action_mode,
10319                                     action_arg_number,
10320                                     action_arg_number_min,
10321                                     action_arg_number_max);
10322
10323           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10324         }
10325       }
10326
10327       break;
10328     }
10329
10330     case CA_SET_PLAYER_SHIELD:
10331     {
10332       for (i = 0; i < MAX_PLAYERS; i++)
10333       {
10334         if (trigger_player_bits & (1 << i))
10335         {
10336           if (action_arg == CA_ARG_SHIELD_OFF)
10337           {
10338             stored_player[i].shield_normal_time_left = 0;
10339             stored_player[i].shield_deadly_time_left = 0;
10340           }
10341           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10342           {
10343             stored_player[i].shield_normal_time_left = 999999;
10344           }
10345           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10346           {
10347             stored_player[i].shield_normal_time_left = 999999;
10348             stored_player[i].shield_deadly_time_left = 999999;
10349           }
10350         }
10351       }
10352
10353       break;
10354     }
10355
10356     case CA_SET_PLAYER_GRAVITY:
10357     {
10358       for (i = 0; i < MAX_PLAYERS; i++)
10359       {
10360         if (trigger_player_bits & (1 << i))
10361         {
10362           stored_player[i].gravity =
10363             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10364              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10365              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10366              stored_player[i].gravity);
10367         }
10368       }
10369
10370       break;
10371     }
10372
10373     case CA_SET_PLAYER_ARTWORK:
10374     {
10375       for (i = 0; i < MAX_PLAYERS; i++)
10376       {
10377         if (trigger_player_bits & (1 << i))
10378         {
10379           int artwork_element = action_arg_element;
10380
10381           if (action_arg == CA_ARG_ELEMENT_RESET)
10382             artwork_element =
10383               (level.use_artwork_element[i] ? level.artwork_element[i] :
10384                stored_player[i].element_nr);
10385
10386           if (stored_player[i].artwork_element != artwork_element)
10387             stored_player[i].Frame = 0;
10388
10389           stored_player[i].artwork_element = artwork_element;
10390
10391           SetPlayerWaiting(&stored_player[i], FALSE);
10392
10393           // set number of special actions for bored and sleeping animation
10394           stored_player[i].num_special_action_bored =
10395             get_num_special_action(artwork_element,
10396                                    ACTION_BORING_1, ACTION_BORING_LAST);
10397           stored_player[i].num_special_action_sleeping =
10398             get_num_special_action(artwork_element,
10399                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10400         }
10401       }
10402
10403       break;
10404     }
10405
10406     case CA_SET_PLAYER_INVENTORY:
10407     {
10408       for (i = 0; i < MAX_PLAYERS; i++)
10409       {
10410         struct PlayerInfo *player = &stored_player[i];
10411         int j, k;
10412
10413         if (trigger_player_bits & (1 << i))
10414         {
10415           int inventory_element = action_arg_element;
10416
10417           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10418               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10419               action_arg == CA_ARG_ELEMENT_ACTION)
10420           {
10421             int element = inventory_element;
10422             int collect_count = element_info[element].collect_count_initial;
10423
10424             if (!IS_CUSTOM_ELEMENT(element))
10425               collect_count = 1;
10426
10427             if (collect_count == 0)
10428               player->inventory_infinite_element = element;
10429             else
10430               for (k = 0; k < collect_count; k++)
10431                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10432                   player->inventory_element[player->inventory_size++] =
10433                     element;
10434           }
10435           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10436                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10437                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10438           {
10439             if (player->inventory_infinite_element != EL_UNDEFINED &&
10440                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10441                                      action_arg_element_raw))
10442               player->inventory_infinite_element = EL_UNDEFINED;
10443
10444             for (k = 0, j = 0; j < player->inventory_size; j++)
10445             {
10446               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10447                                         action_arg_element_raw))
10448                 player->inventory_element[k++] = player->inventory_element[j];
10449             }
10450
10451             player->inventory_size = k;
10452           }
10453           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10454           {
10455             if (player->inventory_size > 0)
10456             {
10457               for (j = 0; j < player->inventory_size - 1; j++)
10458                 player->inventory_element[j] = player->inventory_element[j + 1];
10459
10460               player->inventory_size--;
10461             }
10462           }
10463           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10464           {
10465             if (player->inventory_size > 0)
10466               player->inventory_size--;
10467           }
10468           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10469           {
10470             player->inventory_infinite_element = EL_UNDEFINED;
10471             player->inventory_size = 0;
10472           }
10473           else if (action_arg == CA_ARG_INVENTORY_RESET)
10474           {
10475             player->inventory_infinite_element = EL_UNDEFINED;
10476             player->inventory_size = 0;
10477
10478             if (level.use_initial_inventory[i])
10479             {
10480               for (j = 0; j < level.initial_inventory_size[i]; j++)
10481               {
10482                 int element = level.initial_inventory_content[i][j];
10483                 int collect_count = element_info[element].collect_count_initial;
10484
10485                 if (!IS_CUSTOM_ELEMENT(element))
10486                   collect_count = 1;
10487
10488                 if (collect_count == 0)
10489                   player->inventory_infinite_element = element;
10490                 else
10491                   for (k = 0; k < collect_count; k++)
10492                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10493                       player->inventory_element[player->inventory_size++] =
10494                         element;
10495               }
10496             }
10497           }
10498         }
10499       }
10500
10501       break;
10502     }
10503
10504     // ---------- CE actions  -------------------------------------------------
10505
10506     case CA_SET_CE_VALUE:
10507     {
10508       int last_ce_value = CustomValue[x][y];
10509
10510       CustomValue[x][y] = action_arg_number_new;
10511
10512       if (CustomValue[x][y] != last_ce_value)
10513       {
10514         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10515         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10516
10517         if (CustomValue[x][y] == 0)
10518         {
10519           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10520           ChangeCount[x][y] = 0;        // allow at least one more change
10521
10522           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10523           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10524         }
10525       }
10526
10527       break;
10528     }
10529
10530     case CA_SET_CE_SCORE:
10531     {
10532       int last_ce_score = ei->collect_score;
10533
10534       ei->collect_score = action_arg_number_new;
10535
10536       if (ei->collect_score != last_ce_score)
10537       {
10538         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10539         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10540
10541         if (ei->collect_score == 0)
10542         {
10543           int xx, yy;
10544
10545           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10546           ChangeCount[x][y] = 0;        // allow at least one more change
10547
10548           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10549           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10550
10551           /*
10552             This is a very special case that seems to be a mixture between
10553             CheckElementChange() and CheckTriggeredElementChange(): while
10554             the first one only affects single elements that are triggered
10555             directly, the second one affects multiple elements in the playfield
10556             that are triggered indirectly by another element. This is a third
10557             case: Changing the CE score always affects multiple identical CEs,
10558             so every affected CE must be checked, not only the single CE for
10559             which the CE score was changed in the first place (as every instance
10560             of that CE shares the same CE score, and therefore also can change)!
10561           */
10562           SCAN_PLAYFIELD(xx, yy)
10563           {
10564             if (Tile[xx][yy] == element)
10565               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10566                                  CE_SCORE_GETS_ZERO);
10567           }
10568         }
10569       }
10570
10571       break;
10572     }
10573
10574     case CA_SET_CE_ARTWORK:
10575     {
10576       int artwork_element = action_arg_element;
10577       boolean reset_frame = FALSE;
10578       int xx, yy;
10579
10580       if (action_arg == CA_ARG_ELEMENT_RESET)
10581         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10582                            element);
10583
10584       if (ei->gfx_element != artwork_element)
10585         reset_frame = TRUE;
10586
10587       ei->gfx_element = artwork_element;
10588
10589       SCAN_PLAYFIELD(xx, yy)
10590       {
10591         if (Tile[xx][yy] == element)
10592         {
10593           if (reset_frame)
10594           {
10595             ResetGfxAnimation(xx, yy);
10596             ResetRandomAnimationValue(xx, yy);
10597           }
10598
10599           TEST_DrawLevelField(xx, yy);
10600         }
10601       }
10602
10603       break;
10604     }
10605
10606     // ---------- engine actions  ---------------------------------------------
10607
10608     case CA_SET_ENGINE_SCAN_MODE:
10609     {
10610       InitPlayfieldScanMode(action_arg);
10611
10612       break;
10613     }
10614
10615     default:
10616       break;
10617   }
10618 }
10619
10620 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10621 {
10622   int old_element = Tile[x][y];
10623   int new_element = GetElementFromGroupElement(element);
10624   int previous_move_direction = MovDir[x][y];
10625   int last_ce_value = CustomValue[x][y];
10626   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10627   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10628   boolean add_player_onto_element = (new_element_is_player &&
10629                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10630                                      IS_WALKABLE(old_element));
10631
10632   if (!add_player_onto_element)
10633   {
10634     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10635       RemoveMovingField(x, y);
10636     else
10637       RemoveField(x, y);
10638
10639     Tile[x][y] = new_element;
10640
10641     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10642       MovDir[x][y] = previous_move_direction;
10643
10644     if (element_info[new_element].use_last_ce_value)
10645       CustomValue[x][y] = last_ce_value;
10646
10647     InitField_WithBug1(x, y, FALSE);
10648
10649     new_element = Tile[x][y];   // element may have changed
10650
10651     ResetGfxAnimation(x, y);
10652     ResetRandomAnimationValue(x, y);
10653
10654     TEST_DrawLevelField(x, y);
10655
10656     if (GFX_CRUMBLED(new_element))
10657       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10658   }
10659
10660   // check if element under the player changes from accessible to unaccessible
10661   // (needed for special case of dropping element which then changes)
10662   // (must be checked after creating new element for walkable group elements)
10663   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10664       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10665   {
10666     Bang(x, y);
10667
10668     return;
10669   }
10670
10671   // "ChangeCount" not set yet to allow "entered by player" change one time
10672   if (new_element_is_player)
10673     RelocatePlayer(x, y, new_element);
10674
10675   if (is_change)
10676     ChangeCount[x][y]++;        // count number of changes in the same frame
10677
10678   TestIfBadThingTouchesPlayer(x, y);
10679   TestIfPlayerTouchesCustomElement(x, y);
10680   TestIfElementTouchesCustomElement(x, y);
10681 }
10682
10683 static void CreateField(int x, int y, int element)
10684 {
10685   CreateFieldExt(x, y, element, FALSE);
10686 }
10687
10688 static void CreateElementFromChange(int x, int y, int element)
10689 {
10690   element = GET_VALID_RUNTIME_ELEMENT(element);
10691
10692   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10693   {
10694     int old_element = Tile[x][y];
10695
10696     // prevent changed element from moving in same engine frame
10697     // unless both old and new element can either fall or move
10698     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10699         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10700       Stop[x][y] = TRUE;
10701   }
10702
10703   CreateFieldExt(x, y, element, TRUE);
10704 }
10705
10706 static boolean ChangeElement(int x, int y, int element, int page)
10707 {
10708   struct ElementInfo *ei = &element_info[element];
10709   struct ElementChangeInfo *change = &ei->change_page[page];
10710   int ce_value = CustomValue[x][y];
10711   int ce_score = ei->collect_score;
10712   int target_element;
10713   int old_element = Tile[x][y];
10714
10715   // always use default change event to prevent running into a loop
10716   if (ChangeEvent[x][y] == -1)
10717     ChangeEvent[x][y] = CE_DELAY;
10718
10719   if (ChangeEvent[x][y] == CE_DELAY)
10720   {
10721     // reset actual trigger element, trigger player and action element
10722     change->actual_trigger_element = EL_EMPTY;
10723     change->actual_trigger_player = EL_EMPTY;
10724     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10725     change->actual_trigger_side = CH_SIDE_NONE;
10726     change->actual_trigger_ce_value = 0;
10727     change->actual_trigger_ce_score = 0;
10728   }
10729
10730   // do not change elements more than a specified maximum number of changes
10731   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10732     return FALSE;
10733
10734   ChangeCount[x][y]++;          // count number of changes in the same frame
10735
10736   if (change->explode)
10737   {
10738     Bang(x, y);
10739
10740     return TRUE;
10741   }
10742
10743   if (change->use_target_content)
10744   {
10745     boolean complete_replace = TRUE;
10746     boolean can_replace[3][3];
10747     int xx, yy;
10748
10749     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10750     {
10751       boolean is_empty;
10752       boolean is_walkable;
10753       boolean is_diggable;
10754       boolean is_collectible;
10755       boolean is_removable;
10756       boolean is_destructible;
10757       int ex = x + xx - 1;
10758       int ey = y + yy - 1;
10759       int content_element = change->target_content.e[xx][yy];
10760       int e;
10761
10762       can_replace[xx][yy] = TRUE;
10763
10764       if (ex == x && ey == y)   // do not check changing element itself
10765         continue;
10766
10767       if (content_element == EL_EMPTY_SPACE)
10768       {
10769         can_replace[xx][yy] = FALSE;    // do not replace border with space
10770
10771         continue;
10772       }
10773
10774       if (!IN_LEV_FIELD(ex, ey))
10775       {
10776         can_replace[xx][yy] = FALSE;
10777         complete_replace = FALSE;
10778
10779         continue;
10780       }
10781
10782       e = Tile[ex][ey];
10783
10784       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10785         e = MovingOrBlocked2Element(ex, ey);
10786
10787       is_empty = (IS_FREE(ex, ey) ||
10788                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10789
10790       is_walkable     = (is_empty || IS_WALKABLE(e));
10791       is_diggable     = (is_empty || IS_DIGGABLE(e));
10792       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10793       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10794       is_removable    = (is_diggable || is_collectible);
10795
10796       can_replace[xx][yy] =
10797         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10798           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10799           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10800           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10801           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10802           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10803          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10804
10805       if (!can_replace[xx][yy])
10806         complete_replace = FALSE;
10807     }
10808
10809     if (!change->only_if_complete || complete_replace)
10810     {
10811       boolean something_has_changed = FALSE;
10812
10813       if (change->only_if_complete && change->use_random_replace &&
10814           RND(100) < change->random_percentage)
10815         return FALSE;
10816
10817       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10818       {
10819         int ex = x + xx - 1;
10820         int ey = y + yy - 1;
10821         int content_element;
10822
10823         if (can_replace[xx][yy] && (!change->use_random_replace ||
10824                                     RND(100) < change->random_percentage))
10825         {
10826           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10827             RemoveMovingField(ex, ey);
10828
10829           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10830
10831           content_element = change->target_content.e[xx][yy];
10832           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10833                                               ce_value, ce_score);
10834
10835           CreateElementFromChange(ex, ey, target_element);
10836
10837           something_has_changed = TRUE;
10838
10839           // for symmetry reasons, freeze newly created border elements
10840           if (ex != x || ey != y)
10841             Stop[ex][ey] = TRUE;        // no more moving in this frame
10842         }
10843       }
10844
10845       if (something_has_changed)
10846       {
10847         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10848         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10849       }
10850     }
10851   }
10852   else
10853   {
10854     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10855                                         ce_value, ce_score);
10856
10857     if (element == EL_DIAGONAL_GROWING ||
10858         element == EL_DIAGONAL_SHRINKING)
10859     {
10860       target_element = Store[x][y];
10861
10862       Store[x][y] = EL_EMPTY;
10863     }
10864
10865     // special case: element changes to player (and may be kept if walkable)
10866     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10867       CreateElementFromChange(x, y, EL_EMPTY);
10868
10869     CreateElementFromChange(x, y, target_element);
10870
10871     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10872     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10873   }
10874
10875   // this uses direct change before indirect change
10876   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10877
10878   return TRUE;
10879 }
10880
10881 static void HandleElementChange(int x, int y, int page)
10882 {
10883   int element = MovingOrBlocked2Element(x, y);
10884   struct ElementInfo *ei = &element_info[element];
10885   struct ElementChangeInfo *change = &ei->change_page[page];
10886   boolean handle_action_before_change = FALSE;
10887
10888 #ifdef DEBUG
10889   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10890       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10891   {
10892     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10893           x, y, element, element_info[element].token_name);
10894     Debug("game:playing:HandleElementChange", "This should never happen!");
10895   }
10896 #endif
10897
10898   // this can happen with classic bombs on walkable, changing elements
10899   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10900   {
10901     return;
10902   }
10903
10904   if (ChangeDelay[x][y] == 0)           // initialize element change
10905   {
10906     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10907
10908     if (change->can_change)
10909     {
10910       // !!! not clear why graphic animation should be reset at all here !!!
10911       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10912       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10913
10914       /*
10915         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10916
10917         When using an animation frame delay of 1 (this only happens with
10918         "sp_zonk.moving.left/right" in the classic graphics), the default
10919         (non-moving) animation shows wrong animation frames (while the
10920         moving animation, like "sp_zonk.moving.left/right", is correct,
10921         so this graphical bug never shows up with the classic graphics).
10922         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10923         be drawn instead of the correct frames 0,1,2,3. This is caused by
10924         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10925         an element change: First when the change delay ("ChangeDelay[][]")
10926         counter has reached zero after decrementing, then a second time in
10927         the next frame (after "GfxFrame[][]" was already incremented) when
10928         "ChangeDelay[][]" is reset to the initial delay value again.
10929
10930         This causes frame 0 to be drawn twice, while the last frame won't
10931         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10932
10933         As some animations may already be cleverly designed around this bug
10934         (at least the "Snake Bite" snake tail animation does this), it cannot
10935         simply be fixed here without breaking such existing animations.
10936         Unfortunately, it cannot easily be detected if a graphics set was
10937         designed "before" or "after" the bug was fixed. As a workaround,
10938         a new graphics set option "game.graphics_engine_version" was added
10939         to be able to specify the game's major release version for which the
10940         graphics set was designed, which can then be used to decide if the
10941         bugfix should be used (version 4 and above) or not (version 3 or
10942         below, or if no version was specified at all, as with old sets).
10943
10944         (The wrong/fixed animation frames can be tested with the test level set
10945         "test_gfxframe" and level "000", which contains a specially prepared
10946         custom element at level position (x/y) == (11/9) which uses the zonk
10947         animation mentioned above. Using "game.graphics_engine_version: 4"
10948         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10949         This can also be seen from the debug output for this test element.)
10950       */
10951
10952       // when a custom element is about to change (for example by change delay),
10953       // do not reset graphic animation when the custom element is moving
10954       if (game.graphics_engine_version < 4 &&
10955           !IS_MOVING(x, y))
10956       {
10957         ResetGfxAnimation(x, y);
10958         ResetRandomAnimationValue(x, y);
10959       }
10960
10961       if (change->pre_change_function)
10962         change->pre_change_function(x, y);
10963     }
10964   }
10965
10966   ChangeDelay[x][y]--;
10967
10968   if (ChangeDelay[x][y] != 0)           // continue element change
10969   {
10970     if (change->can_change)
10971     {
10972       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10973
10974       if (IS_ANIMATED(graphic))
10975         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10976
10977       if (change->change_function)
10978         change->change_function(x, y);
10979     }
10980   }
10981   else                                  // finish element change
10982   {
10983     if (ChangePage[x][y] != -1)         // remember page from delayed change
10984     {
10985       page = ChangePage[x][y];
10986       ChangePage[x][y] = -1;
10987
10988       change = &ei->change_page[page];
10989     }
10990
10991     if (IS_MOVING(x, y))                // never change a running system ;-)
10992     {
10993       ChangeDelay[x][y] = 1;            // try change after next move step
10994       ChangePage[x][y] = page;          // remember page to use for change
10995
10996       return;
10997     }
10998
10999     // special case: set new level random seed before changing element
11000     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11001       handle_action_before_change = TRUE;
11002
11003     if (change->has_action && handle_action_before_change)
11004       ExecuteCustomElementAction(x, y, element, page);
11005
11006     if (change->can_change)
11007     {
11008       if (ChangeElement(x, y, element, page))
11009       {
11010         if (change->post_change_function)
11011           change->post_change_function(x, y);
11012       }
11013     }
11014
11015     if (change->has_action && !handle_action_before_change)
11016       ExecuteCustomElementAction(x, y, element, page);
11017   }
11018 }
11019
11020 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11021                                               int trigger_element,
11022                                               int trigger_event,
11023                                               int trigger_player,
11024                                               int trigger_side,
11025                                               int trigger_page)
11026 {
11027   boolean change_done_any = FALSE;
11028   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11029   int i;
11030
11031   if (!(trigger_events[trigger_element][trigger_event]))
11032     return FALSE;
11033
11034   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11035
11036   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11037   {
11038     int element = EL_CUSTOM_START + i;
11039     boolean change_done = FALSE;
11040     int p;
11041
11042     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11043         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11044       continue;
11045
11046     for (p = 0; p < element_info[element].num_change_pages; p++)
11047     {
11048       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11049
11050       if (change->can_change_or_has_action &&
11051           change->has_event[trigger_event] &&
11052           change->trigger_side & trigger_side &&
11053           change->trigger_player & trigger_player &&
11054           change->trigger_page & trigger_page_bits &&
11055           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11056       {
11057         change->actual_trigger_element = trigger_element;
11058         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11059         change->actual_trigger_player_bits = trigger_player;
11060         change->actual_trigger_side = trigger_side;
11061         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11062         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11063
11064         if ((change->can_change && !change_done) || change->has_action)
11065         {
11066           int x, y;
11067
11068           SCAN_PLAYFIELD(x, y)
11069           {
11070             if (Tile[x][y] == element)
11071             {
11072               if (change->can_change && !change_done)
11073               {
11074                 // if element already changed in this frame, not only prevent
11075                 // another element change (checked in ChangeElement()), but
11076                 // also prevent additional element actions for this element
11077
11078                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11079                     !level.use_action_after_change_bug)
11080                   continue;
11081
11082                 ChangeDelay[x][y] = 1;
11083                 ChangeEvent[x][y] = trigger_event;
11084
11085                 HandleElementChange(x, y, p);
11086               }
11087               else if (change->has_action)
11088               {
11089                 // if element already changed in this frame, not only prevent
11090                 // another element change (checked in ChangeElement()), but
11091                 // also prevent additional element actions for this element
11092
11093                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11094                     !level.use_action_after_change_bug)
11095                   continue;
11096
11097                 ExecuteCustomElementAction(x, y, element, p);
11098                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11099               }
11100             }
11101           }
11102
11103           if (change->can_change)
11104           {
11105             change_done = TRUE;
11106             change_done_any = TRUE;
11107           }
11108         }
11109       }
11110     }
11111   }
11112
11113   RECURSION_LOOP_DETECTION_END();
11114
11115   return change_done_any;
11116 }
11117
11118 static boolean CheckElementChangeExt(int x, int y,
11119                                      int element,
11120                                      int trigger_element,
11121                                      int trigger_event,
11122                                      int trigger_player,
11123                                      int trigger_side)
11124 {
11125   boolean change_done = FALSE;
11126   int p;
11127
11128   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11129       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11130     return FALSE;
11131
11132   if (Tile[x][y] == EL_BLOCKED)
11133   {
11134     Blocked2Moving(x, y, &x, &y);
11135     element = Tile[x][y];
11136   }
11137
11138   // check if element has already changed or is about to change after moving
11139   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11140        Tile[x][y] != element) ||
11141
11142       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11143        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11144         ChangePage[x][y] != -1)))
11145     return FALSE;
11146
11147   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11148
11149   for (p = 0; p < element_info[element].num_change_pages; p++)
11150   {
11151     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11152
11153     /* check trigger element for all events where the element that is checked
11154        for changing interacts with a directly adjacent element -- this is
11155        different to element changes that affect other elements to change on the
11156        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11157     boolean check_trigger_element =
11158       (trigger_event == CE_NEXT_TO_X ||
11159        trigger_event == CE_TOUCHING_X ||
11160        trigger_event == CE_HITTING_X ||
11161        trigger_event == CE_HIT_BY_X ||
11162        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11163
11164     if (change->can_change_or_has_action &&
11165         change->has_event[trigger_event] &&
11166         change->trigger_side & trigger_side &&
11167         change->trigger_player & trigger_player &&
11168         (!check_trigger_element ||
11169          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11170     {
11171       change->actual_trigger_element = trigger_element;
11172       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11173       change->actual_trigger_player_bits = trigger_player;
11174       change->actual_trigger_side = trigger_side;
11175       change->actual_trigger_ce_value = CustomValue[x][y];
11176       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11177
11178       // special case: trigger element not at (x,y) position for some events
11179       if (check_trigger_element)
11180       {
11181         static struct
11182         {
11183           int dx, dy;
11184         } move_xy[] =
11185           {
11186             {  0,  0 },
11187             { -1,  0 },
11188             { +1,  0 },
11189             {  0,  0 },
11190             {  0, -1 },
11191             {  0,  0 }, { 0, 0 }, { 0, 0 },
11192             {  0, +1 }
11193           };
11194
11195         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11196         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11197
11198         change->actual_trigger_ce_value = CustomValue[xx][yy];
11199         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11200       }
11201
11202       if (change->can_change && !change_done)
11203       {
11204         ChangeDelay[x][y] = 1;
11205         ChangeEvent[x][y] = trigger_event;
11206
11207         HandleElementChange(x, y, p);
11208
11209         change_done = TRUE;
11210       }
11211       else if (change->has_action)
11212       {
11213         ExecuteCustomElementAction(x, y, element, p);
11214         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11215       }
11216     }
11217   }
11218
11219   RECURSION_LOOP_DETECTION_END();
11220
11221   return change_done;
11222 }
11223
11224 static void PlayPlayerSound(struct PlayerInfo *player)
11225 {
11226   int jx = player->jx, jy = player->jy;
11227   int sound_element = player->artwork_element;
11228   int last_action = player->last_action_waiting;
11229   int action = player->action_waiting;
11230
11231   if (player->is_waiting)
11232   {
11233     if (action != last_action)
11234       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11235     else
11236       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11237   }
11238   else
11239   {
11240     if (action != last_action)
11241       StopSound(element_info[sound_element].sound[last_action]);
11242
11243     if (last_action == ACTION_SLEEPING)
11244       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11245   }
11246 }
11247
11248 static void PlayAllPlayersSound(void)
11249 {
11250   int i;
11251
11252   for (i = 0; i < MAX_PLAYERS; i++)
11253     if (stored_player[i].active)
11254       PlayPlayerSound(&stored_player[i]);
11255 }
11256
11257 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11258 {
11259   boolean last_waiting = player->is_waiting;
11260   int move_dir = player->MovDir;
11261
11262   player->dir_waiting = move_dir;
11263   player->last_action_waiting = player->action_waiting;
11264
11265   if (is_waiting)
11266   {
11267     if (!last_waiting)          // not waiting -> waiting
11268     {
11269       player->is_waiting = TRUE;
11270
11271       player->frame_counter_bored =
11272         FrameCounter +
11273         game.player_boring_delay_fixed +
11274         GetSimpleRandom(game.player_boring_delay_random);
11275       player->frame_counter_sleeping =
11276         FrameCounter +
11277         game.player_sleeping_delay_fixed +
11278         GetSimpleRandom(game.player_sleeping_delay_random);
11279
11280       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11281     }
11282
11283     if (game.player_sleeping_delay_fixed +
11284         game.player_sleeping_delay_random > 0 &&
11285         player->anim_delay_counter == 0 &&
11286         player->post_delay_counter == 0 &&
11287         FrameCounter >= player->frame_counter_sleeping)
11288       player->is_sleeping = TRUE;
11289     else if (game.player_boring_delay_fixed +
11290              game.player_boring_delay_random > 0 &&
11291              FrameCounter >= player->frame_counter_bored)
11292       player->is_bored = TRUE;
11293
11294     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11295                               player->is_bored ? ACTION_BORING :
11296                               ACTION_WAITING);
11297
11298     if (player->is_sleeping && player->use_murphy)
11299     {
11300       // special case for sleeping Murphy when leaning against non-free tile
11301
11302       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11303           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11304            !IS_MOVING(player->jx - 1, player->jy)))
11305         move_dir = MV_LEFT;
11306       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11307                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11308                 !IS_MOVING(player->jx + 1, player->jy)))
11309         move_dir = MV_RIGHT;
11310       else
11311         player->is_sleeping = FALSE;
11312
11313       player->dir_waiting = move_dir;
11314     }
11315
11316     if (player->is_sleeping)
11317     {
11318       if (player->num_special_action_sleeping > 0)
11319       {
11320         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11321         {
11322           int last_special_action = player->special_action_sleeping;
11323           int num_special_action = player->num_special_action_sleeping;
11324           int special_action =
11325             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11326              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11327              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11328              last_special_action + 1 : ACTION_SLEEPING);
11329           int special_graphic =
11330             el_act_dir2img(player->artwork_element, special_action, move_dir);
11331
11332           player->anim_delay_counter =
11333             graphic_info[special_graphic].anim_delay_fixed +
11334             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11335           player->post_delay_counter =
11336             graphic_info[special_graphic].post_delay_fixed +
11337             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11338
11339           player->special_action_sleeping = special_action;
11340         }
11341
11342         if (player->anim_delay_counter > 0)
11343         {
11344           player->action_waiting = player->special_action_sleeping;
11345           player->anim_delay_counter--;
11346         }
11347         else if (player->post_delay_counter > 0)
11348         {
11349           player->post_delay_counter--;
11350         }
11351       }
11352     }
11353     else if (player->is_bored)
11354     {
11355       if (player->num_special_action_bored > 0)
11356       {
11357         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11358         {
11359           int special_action =
11360             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11361           int special_graphic =
11362             el_act_dir2img(player->artwork_element, special_action, move_dir);
11363
11364           player->anim_delay_counter =
11365             graphic_info[special_graphic].anim_delay_fixed +
11366             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11367           player->post_delay_counter =
11368             graphic_info[special_graphic].post_delay_fixed +
11369             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11370
11371           player->special_action_bored = special_action;
11372         }
11373
11374         if (player->anim_delay_counter > 0)
11375         {
11376           player->action_waiting = player->special_action_bored;
11377           player->anim_delay_counter--;
11378         }
11379         else if (player->post_delay_counter > 0)
11380         {
11381           player->post_delay_counter--;
11382         }
11383       }
11384     }
11385   }
11386   else if (last_waiting)        // waiting -> not waiting
11387   {
11388     player->is_waiting = FALSE;
11389     player->is_bored = FALSE;
11390     player->is_sleeping = FALSE;
11391
11392     player->frame_counter_bored = -1;
11393     player->frame_counter_sleeping = -1;
11394
11395     player->anim_delay_counter = 0;
11396     player->post_delay_counter = 0;
11397
11398     player->dir_waiting = player->MovDir;
11399     player->action_waiting = ACTION_DEFAULT;
11400
11401     player->special_action_bored = ACTION_DEFAULT;
11402     player->special_action_sleeping = ACTION_DEFAULT;
11403   }
11404 }
11405
11406 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11407 {
11408   if ((!player->is_moving  && player->was_moving) ||
11409       (player->MovPos == 0 && player->was_moving) ||
11410       (player->is_snapping && !player->was_snapping) ||
11411       (player->is_dropping && !player->was_dropping))
11412   {
11413     if (!CheckSaveEngineSnapshotToList())
11414       return;
11415
11416     player->was_moving = FALSE;
11417     player->was_snapping = TRUE;
11418     player->was_dropping = TRUE;
11419   }
11420   else
11421   {
11422     if (player->is_moving)
11423       player->was_moving = TRUE;
11424
11425     if (!player->is_snapping)
11426       player->was_snapping = FALSE;
11427
11428     if (!player->is_dropping)
11429       player->was_dropping = FALSE;
11430   }
11431
11432   static struct MouseActionInfo mouse_action_last = { 0 };
11433   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11434   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11435
11436   if (new_released)
11437     CheckSaveEngineSnapshotToList();
11438
11439   mouse_action_last = mouse_action;
11440 }
11441
11442 static void CheckSingleStepMode(struct PlayerInfo *player)
11443 {
11444   if (tape.single_step && tape.recording && !tape.pausing)
11445   {
11446     // as it is called "single step mode", just return to pause mode when the
11447     // player stopped moving after one tile (or never starts moving at all)
11448     // (reverse logic needed here in case single step mode used in team mode)
11449     if (player->is_moving ||
11450         player->is_pushing ||
11451         player->is_dropping_pressed ||
11452         player->effective_mouse_action.button)
11453       game.enter_single_step_mode = FALSE;
11454   }
11455
11456   CheckSaveEngineSnapshot(player);
11457 }
11458
11459 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11460 {
11461   int left      = player_action & JOY_LEFT;
11462   int right     = player_action & JOY_RIGHT;
11463   int up        = player_action & JOY_UP;
11464   int down      = player_action & JOY_DOWN;
11465   int button1   = player_action & JOY_BUTTON_1;
11466   int button2   = player_action & JOY_BUTTON_2;
11467   int dx        = (left ? -1 : right ? 1 : 0);
11468   int dy        = (up   ? -1 : down  ? 1 : 0);
11469
11470   if (!player->active || tape.pausing)
11471     return 0;
11472
11473   if (player_action)
11474   {
11475     if (button1)
11476       SnapField(player, dx, dy);
11477     else
11478     {
11479       if (button2)
11480         DropElement(player);
11481
11482       MovePlayer(player, dx, dy);
11483     }
11484
11485     CheckSingleStepMode(player);
11486
11487     SetPlayerWaiting(player, FALSE);
11488
11489     return player_action;
11490   }
11491   else
11492   {
11493     // no actions for this player (no input at player's configured device)
11494
11495     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11496     SnapField(player, 0, 0);
11497     CheckGravityMovementWhenNotMoving(player);
11498
11499     if (player->MovPos == 0)
11500       SetPlayerWaiting(player, TRUE);
11501
11502     if (player->MovPos == 0)    // needed for tape.playing
11503       player->is_moving = FALSE;
11504
11505     player->is_dropping = FALSE;
11506     player->is_dropping_pressed = FALSE;
11507     player->drop_pressed_delay = 0;
11508
11509     CheckSingleStepMode(player);
11510
11511     return 0;
11512   }
11513 }
11514
11515 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11516                                          byte *tape_action)
11517 {
11518   if (!tape.use_mouse_actions)
11519     return;
11520
11521   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11522   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11523   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11524 }
11525
11526 static void SetTapeActionFromMouseAction(byte *tape_action,
11527                                          struct MouseActionInfo *mouse_action)
11528 {
11529   if (!tape.use_mouse_actions)
11530     return;
11531
11532   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11533   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11534   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11535 }
11536
11537 static void CheckLevelSolved(void)
11538 {
11539   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11540   {
11541     if (game_em.level_solved &&
11542         !game_em.game_over)                             // game won
11543     {
11544       LevelSolved();
11545
11546       game_em.game_over = TRUE;
11547
11548       game.all_players_gone = TRUE;
11549     }
11550
11551     if (game_em.game_over)                              // game lost
11552       game.all_players_gone = TRUE;
11553   }
11554   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11555   {
11556     if (game_sp.level_solved &&
11557         !game_sp.game_over)                             // game won
11558     {
11559       LevelSolved();
11560
11561       game_sp.game_over = TRUE;
11562
11563       game.all_players_gone = TRUE;
11564     }
11565
11566     if (game_sp.game_over)                              // game lost
11567       game.all_players_gone = TRUE;
11568   }
11569   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11570   {
11571     if (game_mm.level_solved &&
11572         !game_mm.game_over)                             // game won
11573     {
11574       LevelSolved();
11575
11576       game_mm.game_over = TRUE;
11577
11578       game.all_players_gone = TRUE;
11579     }
11580
11581     if (game_mm.game_over)                              // game lost
11582       game.all_players_gone = TRUE;
11583   }
11584 }
11585
11586 static void CheckLevelTime(void)
11587 {
11588   int i;
11589
11590   if (TimeFrames >= FRAMES_PER_SECOND)
11591   {
11592     TimeFrames = 0;
11593     TapeTime++;
11594
11595     for (i = 0; i < MAX_PLAYERS; i++)
11596     {
11597       struct PlayerInfo *player = &stored_player[i];
11598
11599       if (SHIELD_ON(player))
11600       {
11601         player->shield_normal_time_left--;
11602
11603         if (player->shield_deadly_time_left > 0)
11604           player->shield_deadly_time_left--;
11605       }
11606     }
11607
11608     if (!game.LevelSolved && !level.use_step_counter)
11609     {
11610       TimePlayed++;
11611
11612       if (TimeLeft > 0)
11613       {
11614         TimeLeft--;
11615
11616         if (TimeLeft <= 10 && setup.time_limit)
11617           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11618
11619         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11620            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11621
11622         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11623
11624         if (!TimeLeft && setup.time_limit)
11625         {
11626           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11627             game_em.lev->killed_out_of_time = TRUE;
11628           else
11629             for (i = 0; i < MAX_PLAYERS; i++)
11630               KillPlayer(&stored_player[i]);
11631         }
11632       }
11633       else if (game.no_time_limit && !game.all_players_gone)
11634       {
11635         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11636       }
11637
11638       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11639     }
11640
11641     if (tape.recording || tape.playing)
11642       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11643   }
11644
11645   if (tape.recording || tape.playing)
11646     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11647
11648   UpdateAndDisplayGameControlValues();
11649 }
11650
11651 void AdvanceFrameAndPlayerCounters(int player_nr)
11652 {
11653   int i;
11654
11655   // advance frame counters (global frame counter and time frame counter)
11656   FrameCounter++;
11657   TimeFrames++;
11658
11659   // advance player counters (counters for move delay, move animation etc.)
11660   for (i = 0; i < MAX_PLAYERS; i++)
11661   {
11662     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11663     int move_delay_value = stored_player[i].move_delay_value;
11664     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11665
11666     if (!advance_player_counters)       // not all players may be affected
11667       continue;
11668
11669     if (move_frames == 0)       // less than one move per game frame
11670     {
11671       int stepsize = TILEX / move_delay_value;
11672       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11673       int count = (stored_player[i].is_moving ?
11674                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11675
11676       if (count % delay == 0)
11677         move_frames = 1;
11678     }
11679
11680     stored_player[i].Frame += move_frames;
11681
11682     if (stored_player[i].MovPos != 0)
11683       stored_player[i].StepFrame += move_frames;
11684
11685     if (stored_player[i].move_delay > 0)
11686       stored_player[i].move_delay--;
11687
11688     // due to bugs in previous versions, counter must count up, not down
11689     if (stored_player[i].push_delay != -1)
11690       stored_player[i].push_delay++;
11691
11692     if (stored_player[i].drop_delay > 0)
11693       stored_player[i].drop_delay--;
11694
11695     if (stored_player[i].is_dropping_pressed)
11696       stored_player[i].drop_pressed_delay++;
11697   }
11698 }
11699
11700 void StartGameActions(boolean init_network_game, boolean record_tape,
11701                       int random_seed)
11702 {
11703   unsigned int new_random_seed = InitRND(random_seed);
11704
11705   if (record_tape)
11706     TapeStartRecording(new_random_seed);
11707
11708   if (init_network_game)
11709   {
11710     SendToServer_LevelFile();
11711     SendToServer_StartPlaying();
11712
11713     return;
11714   }
11715
11716   InitGame();
11717 }
11718
11719 static void GameActionsExt(void)
11720 {
11721 #if 0
11722   static unsigned int game_frame_delay = 0;
11723 #endif
11724   unsigned int game_frame_delay_value;
11725   byte *recorded_player_action;
11726   byte summarized_player_action = 0;
11727   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11728   int i;
11729
11730   // detect endless loops, caused by custom element programming
11731   if (recursion_loop_detected && recursion_loop_depth == 0)
11732   {
11733     char *message = getStringCat3("Internal Error! Element ",
11734                                   EL_NAME(recursion_loop_element),
11735                                   " caused endless loop! Quit the game?");
11736
11737     Warn("element '%s' caused endless loop in game engine",
11738          EL_NAME(recursion_loop_element));
11739
11740     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11741
11742     recursion_loop_detected = FALSE;    // if game should be continued
11743
11744     free(message);
11745
11746     return;
11747   }
11748
11749   if (game.restart_level)
11750     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11751
11752   CheckLevelSolved();
11753
11754   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11755     GameWon();
11756
11757   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11758     TapeStop();
11759
11760   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11761     return;
11762
11763   game_frame_delay_value =
11764     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11765
11766   if (tape.playing && tape.warp_forward && !tape.pausing)
11767     game_frame_delay_value = 0;
11768
11769   SetVideoFrameDelay(game_frame_delay_value);
11770
11771   // (de)activate virtual buttons depending on current game status
11772   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11773   {
11774     if (game.all_players_gone)  // if no players there to be controlled anymore
11775       SetOverlayActive(FALSE);
11776     else if (!tape.playing)     // if game continues after tape stopped playing
11777       SetOverlayActive(TRUE);
11778   }
11779
11780 #if 0
11781 #if 0
11782   // ---------- main game synchronization point ----------
11783
11784   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11785
11786   Debug("game:playing:skip", "skip == %d", skip);
11787
11788 #else
11789   // ---------- main game synchronization point ----------
11790
11791   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11792 #endif
11793 #endif
11794
11795   if (network_playing && !network_player_action_received)
11796   {
11797     // try to get network player actions in time
11798
11799     // last chance to get network player actions without main loop delay
11800     HandleNetworking();
11801
11802     // game was quit by network peer
11803     if (game_status != GAME_MODE_PLAYING)
11804       return;
11805
11806     // check if network player actions still missing and game still running
11807     if (!network_player_action_received && !checkGameEnded())
11808       return;           // failed to get network player actions in time
11809
11810     // do not yet reset "network_player_action_received" (for tape.pausing)
11811   }
11812
11813   if (tape.pausing)
11814     return;
11815
11816   // at this point we know that we really continue executing the game
11817
11818   network_player_action_received = FALSE;
11819
11820   // when playing tape, read previously recorded player input from tape data
11821   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11822
11823   local_player->effective_mouse_action = local_player->mouse_action;
11824
11825   if (recorded_player_action != NULL)
11826     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11827                                  recorded_player_action);
11828
11829   // TapePlayAction() may return NULL when toggling to "pause before death"
11830   if (tape.pausing)
11831     return;
11832
11833   if (tape.set_centered_player)
11834   {
11835     game.centered_player_nr_next = tape.centered_player_nr_next;
11836     game.set_centered_player = TRUE;
11837   }
11838
11839   for (i = 0; i < MAX_PLAYERS; i++)
11840   {
11841     summarized_player_action |= stored_player[i].action;
11842
11843     if (!network_playing && (game.team_mode || tape.playing))
11844       stored_player[i].effective_action = stored_player[i].action;
11845   }
11846
11847   if (network_playing && !checkGameEnded())
11848     SendToServer_MovePlayer(summarized_player_action);
11849
11850   // summarize all actions at local players mapped input device position
11851   // (this allows using different input devices in single player mode)
11852   if (!network.enabled && !game.team_mode)
11853     stored_player[map_player_action[local_player->index_nr]].effective_action =
11854       summarized_player_action;
11855
11856   // summarize all actions at centered player in local team mode
11857   if (tape.recording &&
11858       setup.team_mode && !network.enabled &&
11859       setup.input_on_focus &&
11860       game.centered_player_nr != -1)
11861   {
11862     for (i = 0; i < MAX_PLAYERS; i++)
11863       stored_player[map_player_action[i]].effective_action =
11864         (i == game.centered_player_nr ? summarized_player_action : 0);
11865   }
11866
11867   if (recorded_player_action != NULL)
11868     for (i = 0; i < MAX_PLAYERS; i++)
11869       stored_player[i].effective_action = recorded_player_action[i];
11870
11871   for (i = 0; i < MAX_PLAYERS; i++)
11872   {
11873     tape_action[i] = stored_player[i].effective_action;
11874
11875     /* (this may happen in the RND game engine if a player was not present on
11876        the playfield on level start, but appeared later from a custom element */
11877     if (setup.team_mode &&
11878         tape.recording &&
11879         tape_action[i] &&
11880         !tape.player_participates[i])
11881       tape.player_participates[i] = TRUE;
11882   }
11883
11884   SetTapeActionFromMouseAction(tape_action,
11885                                &local_player->effective_mouse_action);
11886
11887   // only record actions from input devices, but not programmed actions
11888   if (tape.recording)
11889     TapeRecordAction(tape_action);
11890
11891   // remember if game was played (especially after tape stopped playing)
11892   if (!tape.playing && summarized_player_action)
11893     game.GamePlayed = TRUE;
11894
11895 #if USE_NEW_PLAYER_ASSIGNMENTS
11896   // !!! also map player actions in single player mode !!!
11897   // if (game.team_mode)
11898   if (1)
11899   {
11900     byte mapped_action[MAX_PLAYERS];
11901
11902 #if DEBUG_PLAYER_ACTIONS
11903     for (i = 0; i < MAX_PLAYERS; i++)
11904       DebugContinued("", "%d, ", stored_player[i].effective_action);
11905 #endif
11906
11907     for (i = 0; i < MAX_PLAYERS; i++)
11908       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11909
11910     for (i = 0; i < MAX_PLAYERS; i++)
11911       stored_player[i].effective_action = mapped_action[i];
11912
11913 #if DEBUG_PLAYER_ACTIONS
11914     DebugContinued("", "=> ");
11915     for (i = 0; i < MAX_PLAYERS; i++)
11916       DebugContinued("", "%d, ", stored_player[i].effective_action);
11917     DebugContinued("game:playing:player", "\n");
11918 #endif
11919   }
11920 #if DEBUG_PLAYER_ACTIONS
11921   else
11922   {
11923     for (i = 0; i < MAX_PLAYERS; i++)
11924       DebugContinued("", "%d, ", stored_player[i].effective_action);
11925     DebugContinued("game:playing:player", "\n");
11926   }
11927 #endif
11928 #endif
11929
11930   for (i = 0; i < MAX_PLAYERS; i++)
11931   {
11932     // allow engine snapshot in case of changed movement attempt
11933     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11934         (stored_player[i].effective_action & KEY_MOTION))
11935       game.snapshot.changed_action = TRUE;
11936
11937     // allow engine snapshot in case of snapping/dropping attempt
11938     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11939         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11940       game.snapshot.changed_action = TRUE;
11941
11942     game.snapshot.last_action[i] = stored_player[i].effective_action;
11943   }
11944
11945   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11946   {
11947     GameActions_EM_Main();
11948   }
11949   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11950   {
11951     GameActions_SP_Main();
11952   }
11953   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11954   {
11955     GameActions_MM_Main();
11956   }
11957   else
11958   {
11959     GameActions_RND_Main();
11960   }
11961
11962   BlitScreenToBitmap(backbuffer);
11963
11964   CheckLevelSolved();
11965   CheckLevelTime();
11966
11967   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11968
11969   if (global.show_frames_per_second)
11970   {
11971     static unsigned int fps_counter = 0;
11972     static int fps_frames = 0;
11973     unsigned int fps_delay_ms = Counter() - fps_counter;
11974
11975     fps_frames++;
11976
11977     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11978     {
11979       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11980
11981       fps_frames = 0;
11982       fps_counter = Counter();
11983
11984       // always draw FPS to screen after FPS value was updated
11985       redraw_mask |= REDRAW_FPS;
11986     }
11987
11988     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11989     if (GetDrawDeactivationMask() == REDRAW_NONE)
11990       redraw_mask |= REDRAW_FPS;
11991   }
11992 }
11993
11994 static void GameActions_CheckSaveEngineSnapshot(void)
11995 {
11996   if (!game.snapshot.save_snapshot)
11997     return;
11998
11999   // clear flag for saving snapshot _before_ saving snapshot
12000   game.snapshot.save_snapshot = FALSE;
12001
12002   SaveEngineSnapshotToList();
12003 }
12004
12005 void GameActions(void)
12006 {
12007   GameActionsExt();
12008
12009   GameActions_CheckSaveEngineSnapshot();
12010 }
12011
12012 void GameActions_EM_Main(void)
12013 {
12014   byte effective_action[MAX_PLAYERS];
12015   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12016   int i;
12017
12018   for (i = 0; i < MAX_PLAYERS; i++)
12019     effective_action[i] = stored_player[i].effective_action;
12020
12021   GameActions_EM(effective_action, warp_mode);
12022 }
12023
12024 void GameActions_SP_Main(void)
12025 {
12026   byte effective_action[MAX_PLAYERS];
12027   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12028   int i;
12029
12030   for (i = 0; i < MAX_PLAYERS; i++)
12031     effective_action[i] = stored_player[i].effective_action;
12032
12033   GameActions_SP(effective_action, warp_mode);
12034
12035   for (i = 0; i < MAX_PLAYERS; i++)
12036   {
12037     if (stored_player[i].force_dropping)
12038       stored_player[i].action |= KEY_BUTTON_DROP;
12039
12040     stored_player[i].force_dropping = FALSE;
12041   }
12042 }
12043
12044 void GameActions_MM_Main(void)
12045 {
12046   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12047
12048   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12049 }
12050
12051 void GameActions_RND_Main(void)
12052 {
12053   GameActions_RND();
12054 }
12055
12056 void GameActions_RND(void)
12057 {
12058   static struct MouseActionInfo mouse_action_last = { 0 };
12059   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12060   int magic_wall_x = 0, magic_wall_y = 0;
12061   int i, x, y, element, graphic, last_gfx_frame;
12062
12063   InitPlayfieldScanModeVars();
12064
12065   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12066   {
12067     SCAN_PLAYFIELD(x, y)
12068     {
12069       ChangeCount[x][y] = 0;
12070       ChangeEvent[x][y] = -1;
12071     }
12072   }
12073
12074   if (game.set_centered_player)
12075   {
12076     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12077
12078     // switching to "all players" only possible if all players fit to screen
12079     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12080     {
12081       game.centered_player_nr_next = game.centered_player_nr;
12082       game.set_centered_player = FALSE;
12083     }
12084
12085     // do not switch focus to non-existing (or non-active) player
12086     if (game.centered_player_nr_next >= 0 &&
12087         !stored_player[game.centered_player_nr_next].active)
12088     {
12089       game.centered_player_nr_next = game.centered_player_nr;
12090       game.set_centered_player = FALSE;
12091     }
12092   }
12093
12094   if (game.set_centered_player &&
12095       ScreenMovPos == 0)        // screen currently aligned at tile position
12096   {
12097     int sx, sy;
12098
12099     if (game.centered_player_nr_next == -1)
12100     {
12101       setScreenCenteredToAllPlayers(&sx, &sy);
12102     }
12103     else
12104     {
12105       sx = stored_player[game.centered_player_nr_next].jx;
12106       sy = stored_player[game.centered_player_nr_next].jy;
12107     }
12108
12109     game.centered_player_nr = game.centered_player_nr_next;
12110     game.set_centered_player = FALSE;
12111
12112     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12113     DrawGameDoorValues();
12114   }
12115
12116   // check single step mode (set flag and clear again if any player is active)
12117   game.enter_single_step_mode =
12118     (tape.single_step && tape.recording && !tape.pausing);
12119
12120   for (i = 0; i < MAX_PLAYERS; i++)
12121   {
12122     int actual_player_action = stored_player[i].effective_action;
12123
12124 #if 1
12125     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12126        - rnd_equinox_tetrachloride 048
12127        - rnd_equinox_tetrachloride_ii 096
12128        - rnd_emanuel_schmieg 002
12129        - doctor_sloan_ww 001, 020
12130     */
12131     if (stored_player[i].MovPos == 0)
12132       CheckGravityMovement(&stored_player[i]);
12133 #endif
12134
12135     // overwrite programmed action with tape action
12136     if (stored_player[i].programmed_action)
12137       actual_player_action = stored_player[i].programmed_action;
12138
12139     PlayerActions(&stored_player[i], actual_player_action);
12140
12141     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12142   }
12143
12144   // single step pause mode may already have been toggled by "ScrollPlayer()"
12145   if (game.enter_single_step_mode && !tape.pausing)
12146     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12147
12148   ScrollScreen(NULL, SCROLL_GO_ON);
12149
12150   /* for backwards compatibility, the following code emulates a fixed bug that
12151      occured when pushing elements (causing elements that just made their last
12152      pushing step to already (if possible) make their first falling step in the
12153      same game frame, which is bad); this code is also needed to use the famous
12154      "spring push bug" which is used in older levels and might be wanted to be
12155      used also in newer levels, but in this case the buggy pushing code is only
12156      affecting the "spring" element and no other elements */
12157
12158   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12159   {
12160     for (i = 0; i < MAX_PLAYERS; i++)
12161     {
12162       struct PlayerInfo *player = &stored_player[i];
12163       int x = player->jx;
12164       int y = player->jy;
12165
12166       if (player->active && player->is_pushing && player->is_moving &&
12167           IS_MOVING(x, y) &&
12168           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12169            Tile[x][y] == EL_SPRING))
12170       {
12171         ContinueMoving(x, y);
12172
12173         // continue moving after pushing (this is actually a bug)
12174         if (!IS_MOVING(x, y))
12175           Stop[x][y] = FALSE;
12176       }
12177     }
12178   }
12179
12180   SCAN_PLAYFIELD(x, y)
12181   {
12182     Last[x][y] = Tile[x][y];
12183
12184     ChangeCount[x][y] = 0;
12185     ChangeEvent[x][y] = -1;
12186
12187     // this must be handled before main playfield loop
12188     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12189     {
12190       MovDelay[x][y]--;
12191       if (MovDelay[x][y] <= 0)
12192         RemoveField(x, y);
12193     }
12194
12195     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12196     {
12197       MovDelay[x][y]--;
12198       if (MovDelay[x][y] <= 0)
12199       {
12200         int element = Store[x][y];
12201         int move_direction = MovDir[x][y];
12202         int player_index_bit = Store2[x][y];
12203
12204         Store[x][y] = 0;
12205         Store2[x][y] = 0;
12206
12207         RemoveField(x, y);
12208         TEST_DrawLevelField(x, y);
12209
12210         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12211
12212         if (IS_ENVELOPE(element))
12213           local_player->show_envelope = element;
12214       }
12215     }
12216
12217 #if DEBUG
12218     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12219     {
12220       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12221             x, y);
12222       Debug("game:playing:GameActions_RND", "This should never happen!");
12223
12224       ChangePage[x][y] = -1;
12225     }
12226 #endif
12227
12228     Stop[x][y] = FALSE;
12229     if (WasJustMoving[x][y] > 0)
12230       WasJustMoving[x][y]--;
12231     if (WasJustFalling[x][y] > 0)
12232       WasJustFalling[x][y]--;
12233     if (CheckCollision[x][y] > 0)
12234       CheckCollision[x][y]--;
12235     if (CheckImpact[x][y] > 0)
12236       CheckImpact[x][y]--;
12237
12238     GfxFrame[x][y]++;
12239
12240     /* reset finished pushing action (not done in ContinueMoving() to allow
12241        continuous pushing animation for elements with zero push delay) */
12242     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12243     {
12244       ResetGfxAnimation(x, y);
12245       TEST_DrawLevelField(x, y);
12246     }
12247
12248 #if DEBUG
12249     if (IS_BLOCKED(x, y))
12250     {
12251       int oldx, oldy;
12252
12253       Blocked2Moving(x, y, &oldx, &oldy);
12254       if (!IS_MOVING(oldx, oldy))
12255       {
12256         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12257         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12258         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12259         Debug("game:playing:GameActions_RND", "This should never happen!");
12260       }
12261     }
12262 #endif
12263   }
12264
12265   if (mouse_action.button)
12266   {
12267     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12268     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12269
12270     x = mouse_action.lx;
12271     y = mouse_action.ly;
12272     element = Tile[x][y];
12273
12274     if (new_button)
12275     {
12276       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12277       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12278                                          ch_button);
12279     }
12280
12281     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12282     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12283                                        ch_button);
12284   }
12285
12286   SCAN_PLAYFIELD(x, y)
12287   {
12288     element = Tile[x][y];
12289     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12290     last_gfx_frame = GfxFrame[x][y];
12291
12292     ResetGfxFrame(x, y);
12293
12294     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12295       DrawLevelGraphicAnimation(x, y, graphic);
12296
12297     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12298         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12299       ResetRandomAnimationValue(x, y);
12300
12301     SetRandomAnimationValue(x, y);
12302
12303     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12304
12305     if (IS_INACTIVE(element))
12306     {
12307       if (IS_ANIMATED(graphic))
12308         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12309
12310       continue;
12311     }
12312
12313     // this may take place after moving, so 'element' may have changed
12314     if (IS_CHANGING(x, y) &&
12315         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12316     {
12317       int page = element_info[element].event_page_nr[CE_DELAY];
12318
12319       HandleElementChange(x, y, page);
12320
12321       element = Tile[x][y];
12322       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12323     }
12324
12325     CheckNextToConditions(x, y);
12326
12327     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12328     {
12329       StartMoving(x, y);
12330
12331       element = Tile[x][y];
12332       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12333
12334       if (IS_ANIMATED(graphic) &&
12335           !IS_MOVING(x, y) &&
12336           !Stop[x][y])
12337         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12338
12339       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12340         TEST_DrawTwinkleOnField(x, y);
12341     }
12342     else if (element == EL_ACID)
12343     {
12344       if (!Stop[x][y])
12345         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12346     }
12347     else if ((element == EL_EXIT_OPEN ||
12348               element == EL_EM_EXIT_OPEN ||
12349               element == EL_SP_EXIT_OPEN ||
12350               element == EL_STEEL_EXIT_OPEN ||
12351               element == EL_EM_STEEL_EXIT_OPEN ||
12352               element == EL_SP_TERMINAL ||
12353               element == EL_SP_TERMINAL_ACTIVE ||
12354               element == EL_EXTRA_TIME ||
12355               element == EL_SHIELD_NORMAL ||
12356               element == EL_SHIELD_DEADLY) &&
12357              IS_ANIMATED(graphic))
12358       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12359     else if (IS_MOVING(x, y))
12360       ContinueMoving(x, y);
12361     else if (IS_ACTIVE_BOMB(element))
12362       CheckDynamite(x, y);
12363     else if (element == EL_AMOEBA_GROWING)
12364       AmoebaGrowing(x, y);
12365     else if (element == EL_AMOEBA_SHRINKING)
12366       AmoebaShrinking(x, y);
12367
12368 #if !USE_NEW_AMOEBA_CODE
12369     else if (IS_AMOEBALIVE(element))
12370       AmoebaReproduce(x, y);
12371 #endif
12372
12373     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12374       Life(x, y);
12375     else if (element == EL_EXIT_CLOSED)
12376       CheckExit(x, y);
12377     else if (element == EL_EM_EXIT_CLOSED)
12378       CheckExitEM(x, y);
12379     else if (element == EL_STEEL_EXIT_CLOSED)
12380       CheckExitSteel(x, y);
12381     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12382       CheckExitSteelEM(x, y);
12383     else if (element == EL_SP_EXIT_CLOSED)
12384       CheckExitSP(x, y);
12385     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12386              element == EL_EXPANDABLE_STEELWALL_GROWING)
12387       MauerWaechst(x, y);
12388     else if (element == EL_EXPANDABLE_WALL ||
12389              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12390              element == EL_EXPANDABLE_WALL_VERTICAL ||
12391              element == EL_EXPANDABLE_WALL_ANY ||
12392              element == EL_BD_EXPANDABLE_WALL)
12393       MauerAbleger(x, y);
12394     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12395              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12396              element == EL_EXPANDABLE_STEELWALL_ANY)
12397       MauerAblegerStahl(x, y);
12398     else if (element == EL_FLAMES)
12399       CheckForDragon(x, y);
12400     else if (element == EL_EXPLOSION)
12401       ; // drawing of correct explosion animation is handled separately
12402     else if (element == EL_ELEMENT_SNAPPING ||
12403              element == EL_DIAGONAL_SHRINKING ||
12404              element == EL_DIAGONAL_GROWING)
12405     {
12406       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12407
12408       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12409     }
12410     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12411       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12412
12413     if (IS_BELT_ACTIVE(element))
12414       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12415
12416     if (game.magic_wall_active)
12417     {
12418       int jx = local_player->jx, jy = local_player->jy;
12419
12420       // play the element sound at the position nearest to the player
12421       if ((element == EL_MAGIC_WALL_FULL ||
12422            element == EL_MAGIC_WALL_ACTIVE ||
12423            element == EL_MAGIC_WALL_EMPTYING ||
12424            element == EL_BD_MAGIC_WALL_FULL ||
12425            element == EL_BD_MAGIC_WALL_ACTIVE ||
12426            element == EL_BD_MAGIC_WALL_EMPTYING ||
12427            element == EL_DC_MAGIC_WALL_FULL ||
12428            element == EL_DC_MAGIC_WALL_ACTIVE ||
12429            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12430           ABS(x - jx) + ABS(y - jy) <
12431           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12432       {
12433         magic_wall_x = x;
12434         magic_wall_y = y;
12435       }
12436     }
12437   }
12438
12439 #if USE_NEW_AMOEBA_CODE
12440   // new experimental amoeba growth stuff
12441   if (!(FrameCounter % 8))
12442   {
12443     static unsigned int random = 1684108901;
12444
12445     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12446     {
12447       x = RND(lev_fieldx);
12448       y = RND(lev_fieldy);
12449       element = Tile[x][y];
12450
12451       if (!IS_PLAYER(x,y) &&
12452           (element == EL_EMPTY ||
12453            CAN_GROW_INTO(element) ||
12454            element == EL_QUICKSAND_EMPTY ||
12455            element == EL_QUICKSAND_FAST_EMPTY ||
12456            element == EL_ACID_SPLASH_LEFT ||
12457            element == EL_ACID_SPLASH_RIGHT))
12458       {
12459         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12460             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12461             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12462             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12463           Tile[x][y] = EL_AMOEBA_DROP;
12464       }
12465
12466       random = random * 129 + 1;
12467     }
12468   }
12469 #endif
12470
12471   game.explosions_delayed = FALSE;
12472
12473   SCAN_PLAYFIELD(x, y)
12474   {
12475     element = Tile[x][y];
12476
12477     if (ExplodeField[x][y])
12478       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12479     else if (element == EL_EXPLOSION)
12480       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12481
12482     ExplodeField[x][y] = EX_TYPE_NONE;
12483   }
12484
12485   game.explosions_delayed = TRUE;
12486
12487   if (game.magic_wall_active)
12488   {
12489     if (!(game.magic_wall_time_left % 4))
12490     {
12491       int element = Tile[magic_wall_x][magic_wall_y];
12492
12493       if (element == EL_BD_MAGIC_WALL_FULL ||
12494           element == EL_BD_MAGIC_WALL_ACTIVE ||
12495           element == EL_BD_MAGIC_WALL_EMPTYING)
12496         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12497       else if (element == EL_DC_MAGIC_WALL_FULL ||
12498                element == EL_DC_MAGIC_WALL_ACTIVE ||
12499                element == EL_DC_MAGIC_WALL_EMPTYING)
12500         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12501       else
12502         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12503     }
12504
12505     if (game.magic_wall_time_left > 0)
12506     {
12507       game.magic_wall_time_left--;
12508
12509       if (!game.magic_wall_time_left)
12510       {
12511         SCAN_PLAYFIELD(x, y)
12512         {
12513           element = Tile[x][y];
12514
12515           if (element == EL_MAGIC_WALL_ACTIVE ||
12516               element == EL_MAGIC_WALL_FULL)
12517           {
12518             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12519             TEST_DrawLevelField(x, y);
12520           }
12521           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12522                    element == EL_BD_MAGIC_WALL_FULL)
12523           {
12524             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12525             TEST_DrawLevelField(x, y);
12526           }
12527           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12528                    element == EL_DC_MAGIC_WALL_FULL)
12529           {
12530             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12531             TEST_DrawLevelField(x, y);
12532           }
12533         }
12534
12535         game.magic_wall_active = FALSE;
12536       }
12537     }
12538   }
12539
12540   if (game.light_time_left > 0)
12541   {
12542     game.light_time_left--;
12543
12544     if (game.light_time_left == 0)
12545       RedrawAllLightSwitchesAndInvisibleElements();
12546   }
12547
12548   if (game.timegate_time_left > 0)
12549   {
12550     game.timegate_time_left--;
12551
12552     if (game.timegate_time_left == 0)
12553       CloseAllOpenTimegates();
12554   }
12555
12556   if (game.lenses_time_left > 0)
12557   {
12558     game.lenses_time_left--;
12559
12560     if (game.lenses_time_left == 0)
12561       RedrawAllInvisibleElementsForLenses();
12562   }
12563
12564   if (game.magnify_time_left > 0)
12565   {
12566     game.magnify_time_left--;
12567
12568     if (game.magnify_time_left == 0)
12569       RedrawAllInvisibleElementsForMagnifier();
12570   }
12571
12572   for (i = 0; i < MAX_PLAYERS; i++)
12573   {
12574     struct PlayerInfo *player = &stored_player[i];
12575
12576     if (SHIELD_ON(player))
12577     {
12578       if (player->shield_deadly_time_left)
12579         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12580       else if (player->shield_normal_time_left)
12581         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12582     }
12583   }
12584
12585 #if USE_DELAYED_GFX_REDRAW
12586   SCAN_PLAYFIELD(x, y)
12587   {
12588     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12589     {
12590       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12591          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12592
12593       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12594         DrawLevelField(x, y);
12595
12596       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12597         DrawLevelFieldCrumbled(x, y);
12598
12599       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12600         DrawLevelFieldCrumbledNeighbours(x, y);
12601
12602       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12603         DrawTwinkleOnField(x, y);
12604     }
12605
12606     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12607   }
12608 #endif
12609
12610   DrawAllPlayers();
12611   PlayAllPlayersSound();
12612
12613   for (i = 0; i < MAX_PLAYERS; i++)
12614   {
12615     struct PlayerInfo *player = &stored_player[i];
12616
12617     if (player->show_envelope != 0 && (!player->active ||
12618                                        player->MovPos == 0))
12619     {
12620       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12621
12622       player->show_envelope = 0;
12623     }
12624   }
12625
12626   // use random number generator in every frame to make it less predictable
12627   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12628     RND(1);
12629
12630   mouse_action_last = mouse_action;
12631 }
12632
12633 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12634 {
12635   int min_x = x, min_y = y, max_x = x, max_y = y;
12636   int scr_fieldx = getScreenFieldSizeX();
12637   int scr_fieldy = getScreenFieldSizeY();
12638   int i;
12639
12640   for (i = 0; i < MAX_PLAYERS; i++)
12641   {
12642     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12643
12644     if (!stored_player[i].active || &stored_player[i] == player)
12645       continue;
12646
12647     min_x = MIN(min_x, jx);
12648     min_y = MIN(min_y, jy);
12649     max_x = MAX(max_x, jx);
12650     max_y = MAX(max_y, jy);
12651   }
12652
12653   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12654 }
12655
12656 static boolean AllPlayersInVisibleScreen(void)
12657 {
12658   int i;
12659
12660   for (i = 0; i < MAX_PLAYERS; i++)
12661   {
12662     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12663
12664     if (!stored_player[i].active)
12665       continue;
12666
12667     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12668       return FALSE;
12669   }
12670
12671   return TRUE;
12672 }
12673
12674 void ScrollLevel(int dx, int dy)
12675 {
12676   int scroll_offset = 2 * TILEX_VAR;
12677   int x, y;
12678
12679   BlitBitmap(drawto_field, drawto_field,
12680              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12681              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12682              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12683              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12684              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12685              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12686
12687   if (dx != 0)
12688   {
12689     x = (dx == 1 ? BX1 : BX2);
12690     for (y = BY1; y <= BY2; y++)
12691       DrawScreenField(x, y);
12692   }
12693
12694   if (dy != 0)
12695   {
12696     y = (dy == 1 ? BY1 : BY2);
12697     for (x = BX1; x <= BX2; x++)
12698       DrawScreenField(x, y);
12699   }
12700
12701   redraw_mask |= REDRAW_FIELD;
12702 }
12703
12704 static boolean canFallDown(struct PlayerInfo *player)
12705 {
12706   int jx = player->jx, jy = player->jy;
12707
12708   return (IN_LEV_FIELD(jx, jy + 1) &&
12709           (IS_FREE(jx, jy + 1) ||
12710            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12711           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12712           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12713 }
12714
12715 static boolean canPassField(int x, int y, int move_dir)
12716 {
12717   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12718   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12719   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12720   int nextx = x + dx;
12721   int nexty = y + dy;
12722   int element = Tile[x][y];
12723
12724   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12725           !CAN_MOVE(element) &&
12726           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12727           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12728           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12729 }
12730
12731 static boolean canMoveToValidFieldWithGravity(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 newx = x + dx;
12737   int newy = y + dy;
12738
12739   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12740           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12741           (IS_DIGGABLE(Tile[newx][newy]) ||
12742            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12743            canPassField(newx, newy, move_dir)));
12744 }
12745
12746 static void CheckGravityMovement(struct PlayerInfo *player)
12747 {
12748   if (player->gravity && !player->programmed_action)
12749   {
12750     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12751     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12752     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12753     int jx = player->jx, jy = player->jy;
12754     boolean player_is_moving_to_valid_field =
12755       (!player_is_snapping &&
12756        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12757         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12758     boolean player_can_fall_down = canFallDown(player);
12759
12760     if (player_can_fall_down &&
12761         !player_is_moving_to_valid_field)
12762       player->programmed_action = MV_DOWN;
12763   }
12764 }
12765
12766 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12767 {
12768   return CheckGravityMovement(player);
12769
12770   if (player->gravity && !player->programmed_action)
12771   {
12772     int jx = player->jx, jy = player->jy;
12773     boolean field_under_player_is_free =
12774       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12775     boolean player_is_standing_on_valid_field =
12776       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12777        (IS_WALKABLE(Tile[jx][jy]) &&
12778         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12779
12780     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12781       player->programmed_action = MV_DOWN;
12782   }
12783 }
12784
12785 /*
12786   MovePlayerOneStep()
12787   -----------------------------------------------------------------------------
12788   dx, dy:               direction (non-diagonal) to try to move the player to
12789   real_dx, real_dy:     direction as read from input device (can be diagonal)
12790 */
12791
12792 boolean MovePlayerOneStep(struct PlayerInfo *player,
12793                           int dx, int dy, int real_dx, int real_dy)
12794 {
12795   int jx = player->jx, jy = player->jy;
12796   int new_jx = jx + dx, new_jy = jy + dy;
12797   int can_move;
12798   boolean player_can_move = !player->cannot_move;
12799
12800   if (!player->active || (!dx && !dy))
12801     return MP_NO_ACTION;
12802
12803   player->MovDir = (dx < 0 ? MV_LEFT :
12804                     dx > 0 ? MV_RIGHT :
12805                     dy < 0 ? MV_UP :
12806                     dy > 0 ? MV_DOWN :  MV_NONE);
12807
12808   if (!IN_LEV_FIELD(new_jx, new_jy))
12809     return MP_NO_ACTION;
12810
12811   if (!player_can_move)
12812   {
12813     if (player->MovPos == 0)
12814     {
12815       player->is_moving = FALSE;
12816       player->is_digging = FALSE;
12817       player->is_collecting = FALSE;
12818       player->is_snapping = FALSE;
12819       player->is_pushing = FALSE;
12820     }
12821   }
12822
12823   if (!network.enabled && game.centered_player_nr == -1 &&
12824       !AllPlayersInSight(player, new_jx, new_jy))
12825     return MP_NO_ACTION;
12826
12827   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12828   if (can_move != MP_MOVING)
12829     return can_move;
12830
12831   // check if DigField() has caused relocation of the player
12832   if (player->jx != jx || player->jy != jy)
12833     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12834
12835   StorePlayer[jx][jy] = 0;
12836   player->last_jx = jx;
12837   player->last_jy = jy;
12838   player->jx = new_jx;
12839   player->jy = new_jy;
12840   StorePlayer[new_jx][new_jy] = player->element_nr;
12841
12842   if (player->move_delay_value_next != -1)
12843   {
12844     player->move_delay_value = player->move_delay_value_next;
12845     player->move_delay_value_next = -1;
12846   }
12847
12848   player->MovPos =
12849     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12850
12851   player->step_counter++;
12852
12853   PlayerVisit[jx][jy] = FrameCounter;
12854
12855   player->is_moving = TRUE;
12856
12857 #if 1
12858   // should better be called in MovePlayer(), but this breaks some tapes
12859   ScrollPlayer(player, SCROLL_INIT);
12860 #endif
12861
12862   return MP_MOVING;
12863 }
12864
12865 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12866 {
12867   int jx = player->jx, jy = player->jy;
12868   int old_jx = jx, old_jy = jy;
12869   int moved = MP_NO_ACTION;
12870
12871   if (!player->active)
12872     return FALSE;
12873
12874   if (!dx && !dy)
12875   {
12876     if (player->MovPos == 0)
12877     {
12878       player->is_moving = FALSE;
12879       player->is_digging = FALSE;
12880       player->is_collecting = FALSE;
12881       player->is_snapping = FALSE;
12882       player->is_pushing = FALSE;
12883     }
12884
12885     return FALSE;
12886   }
12887
12888   if (player->move_delay > 0)
12889     return FALSE;
12890
12891   player->move_delay = -1;              // set to "uninitialized" value
12892
12893   // store if player is automatically moved to next field
12894   player->is_auto_moving = (player->programmed_action != MV_NONE);
12895
12896   // remove the last programmed player action
12897   player->programmed_action = 0;
12898
12899   if (player->MovPos)
12900   {
12901     // should only happen if pre-1.2 tape recordings are played
12902     // this is only for backward compatibility
12903
12904     int original_move_delay_value = player->move_delay_value;
12905
12906 #if DEBUG
12907     Debug("game:playing:MovePlayer",
12908           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12909           tape.counter);
12910 #endif
12911
12912     // scroll remaining steps with finest movement resolution
12913     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12914
12915     while (player->MovPos)
12916     {
12917       ScrollPlayer(player, SCROLL_GO_ON);
12918       ScrollScreen(NULL, SCROLL_GO_ON);
12919
12920       AdvanceFrameAndPlayerCounters(player->index_nr);
12921
12922       DrawAllPlayers();
12923       BackToFront_WithFrameDelay(0);
12924     }
12925
12926     player->move_delay_value = original_move_delay_value;
12927   }
12928
12929   player->is_active = FALSE;
12930
12931   if (player->last_move_dir & MV_HORIZONTAL)
12932   {
12933     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12934       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12935   }
12936   else
12937   {
12938     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12939       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12940   }
12941
12942   if (!moved && !player->is_active)
12943   {
12944     player->is_moving = FALSE;
12945     player->is_digging = FALSE;
12946     player->is_collecting = FALSE;
12947     player->is_snapping = FALSE;
12948     player->is_pushing = FALSE;
12949   }
12950
12951   jx = player->jx;
12952   jy = player->jy;
12953
12954   if (moved & MP_MOVING && !ScreenMovPos &&
12955       (player->index_nr == game.centered_player_nr ||
12956        game.centered_player_nr == -1))
12957   {
12958     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12959
12960     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12961     {
12962       // actual player has left the screen -- scroll in that direction
12963       if (jx != old_jx)         // player has moved horizontally
12964         scroll_x += (jx - old_jx);
12965       else                      // player has moved vertically
12966         scroll_y += (jy - old_jy);
12967     }
12968     else
12969     {
12970       int offset_raw = game.scroll_delay_value;
12971
12972       if (jx != old_jx)         // player has moved horizontally
12973       {
12974         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12975         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12976         int new_scroll_x = jx - MIDPOSX + offset_x;
12977
12978         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12979             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12980           scroll_x = new_scroll_x;
12981
12982         // don't scroll over playfield boundaries
12983         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12984
12985         // don't scroll more than one field at a time
12986         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12987
12988         // don't scroll against the player's moving direction
12989         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12990             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12991           scroll_x = old_scroll_x;
12992       }
12993       else                      // player has moved vertically
12994       {
12995         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12996         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12997         int new_scroll_y = jy - MIDPOSY + offset_y;
12998
12999         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13000             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13001           scroll_y = new_scroll_y;
13002
13003         // don't scroll over playfield boundaries
13004         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13005
13006         // don't scroll more than one field at a time
13007         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13008
13009         // don't scroll against the player's moving direction
13010         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13011             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13012           scroll_y = old_scroll_y;
13013       }
13014     }
13015
13016     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13017     {
13018       if (!network.enabled && game.centered_player_nr == -1 &&
13019           !AllPlayersInVisibleScreen())
13020       {
13021         scroll_x = old_scroll_x;
13022         scroll_y = old_scroll_y;
13023       }
13024       else
13025       {
13026         ScrollScreen(player, SCROLL_INIT);
13027         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13028       }
13029     }
13030   }
13031
13032   player->StepFrame = 0;
13033
13034   if (moved & MP_MOVING)
13035   {
13036     if (old_jx != jx && old_jy == jy)
13037       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13038     else if (old_jx == jx && old_jy != jy)
13039       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13040
13041     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13042
13043     player->last_move_dir = player->MovDir;
13044     player->is_moving = TRUE;
13045     player->is_snapping = FALSE;
13046     player->is_switching = FALSE;
13047     player->is_dropping = FALSE;
13048     player->is_dropping_pressed = FALSE;
13049     player->drop_pressed_delay = 0;
13050
13051 #if 0
13052     // should better be called here than above, but this breaks some tapes
13053     ScrollPlayer(player, SCROLL_INIT);
13054 #endif
13055   }
13056   else
13057   {
13058     CheckGravityMovementWhenNotMoving(player);
13059
13060     player->is_moving = FALSE;
13061
13062     /* at this point, the player is allowed to move, but cannot move right now
13063        (e.g. because of something blocking the way) -- ensure that the player
13064        is also allowed to move in the next frame (in old versions before 3.1.1,
13065        the player was forced to wait again for eight frames before next try) */
13066
13067     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13068       player->move_delay = 0;   // allow direct movement in the next frame
13069   }
13070
13071   if (player->move_delay == -1)         // not yet initialized by DigField()
13072     player->move_delay = player->move_delay_value;
13073
13074   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13075   {
13076     TestIfPlayerTouchesBadThing(jx, jy);
13077     TestIfPlayerTouchesCustomElement(jx, jy);
13078   }
13079
13080   if (!player->active)
13081     RemovePlayer(player);
13082
13083   return moved;
13084 }
13085
13086 void ScrollPlayer(struct PlayerInfo *player, int mode)
13087 {
13088   int jx = player->jx, jy = player->jy;
13089   int last_jx = player->last_jx, last_jy = player->last_jy;
13090   int move_stepsize = TILEX / player->move_delay_value;
13091
13092   if (!player->active)
13093     return;
13094
13095   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13096     return;
13097
13098   if (mode == SCROLL_INIT)
13099   {
13100     player->actual_frame_counter = FrameCounter;
13101     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13102
13103     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13104         Tile[last_jx][last_jy] == EL_EMPTY)
13105     {
13106       int last_field_block_delay = 0;   // start with no blocking at all
13107       int block_delay_adjustment = player->block_delay_adjustment;
13108
13109       // if player blocks last field, add delay for exactly one move
13110       if (player->block_last_field)
13111       {
13112         last_field_block_delay += player->move_delay_value;
13113
13114         // when blocking enabled, prevent moving up despite gravity
13115         if (player->gravity && player->MovDir == MV_UP)
13116           block_delay_adjustment = -1;
13117       }
13118
13119       // add block delay adjustment (also possible when not blocking)
13120       last_field_block_delay += block_delay_adjustment;
13121
13122       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13123       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13124     }
13125
13126     if (player->MovPos != 0)    // player has not yet reached destination
13127       return;
13128   }
13129   else if (!FrameReached(&player->actual_frame_counter, 1))
13130     return;
13131
13132   if (player->MovPos != 0)
13133   {
13134     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13135     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13136
13137     // before DrawPlayer() to draw correct player graphic for this case
13138     if (player->MovPos == 0)
13139       CheckGravityMovement(player);
13140   }
13141
13142   if (player->MovPos == 0)      // player reached destination field
13143   {
13144     if (player->move_delay_reset_counter > 0)
13145     {
13146       player->move_delay_reset_counter--;
13147
13148       if (player->move_delay_reset_counter == 0)
13149       {
13150         // continue with normal speed after quickly moving through gate
13151         HALVE_PLAYER_SPEED(player);
13152
13153         // be able to make the next move without delay
13154         player->move_delay = 0;
13155       }
13156     }
13157
13158     player->last_jx = jx;
13159     player->last_jy = jy;
13160
13161     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13162         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13163         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13164         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13165         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13166         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13167         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13168         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13169     {
13170       ExitPlayer(player);
13171
13172       if (game.players_still_needed == 0 &&
13173           (game.friends_still_needed == 0 ||
13174            IS_SP_ELEMENT(Tile[jx][jy])))
13175         LevelSolved();
13176     }
13177
13178     // this breaks one level: "machine", level 000
13179     {
13180       int move_direction = player->MovDir;
13181       int enter_side = MV_DIR_OPPOSITE(move_direction);
13182       int leave_side = move_direction;
13183       int old_jx = last_jx;
13184       int old_jy = last_jy;
13185       int old_element = Tile[old_jx][old_jy];
13186       int new_element = Tile[jx][jy];
13187
13188       if (IS_CUSTOM_ELEMENT(old_element))
13189         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13190                                    CE_LEFT_BY_PLAYER,
13191                                    player->index_bit, leave_side);
13192
13193       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13194                                           CE_PLAYER_LEAVES_X,
13195                                           player->index_bit, leave_side);
13196
13197       if (IS_CUSTOM_ELEMENT(new_element))
13198         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13199                                    player->index_bit, enter_side);
13200
13201       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13202                                           CE_PLAYER_ENTERS_X,
13203                                           player->index_bit, enter_side);
13204
13205       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13206                                         CE_MOVE_OF_X, move_direction);
13207     }
13208
13209     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13210     {
13211       TestIfPlayerTouchesBadThing(jx, jy);
13212       TestIfPlayerTouchesCustomElement(jx, jy);
13213
13214       /* needed because pushed element has not yet reached its destination,
13215          so it would trigger a change event at its previous field location */
13216       if (!player->is_pushing)
13217         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13218
13219       if (level.finish_dig_collect &&
13220           (player->is_digging || player->is_collecting))
13221       {
13222         int last_element = player->last_removed_element;
13223         int move_direction = player->MovDir;
13224         int enter_side = MV_DIR_OPPOSITE(move_direction);
13225         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13226                             CE_PLAYER_COLLECTS_X);
13227
13228         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13229                                             player->index_bit, enter_side);
13230
13231         player->last_removed_element = EL_UNDEFINED;
13232       }
13233
13234       if (!player->active)
13235         RemovePlayer(player);
13236     }
13237
13238     if (level.use_step_counter)
13239     {
13240       int i;
13241
13242       TimePlayed++;
13243
13244       if (TimeLeft > 0)
13245       {
13246         TimeLeft--;
13247
13248         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13249           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13250
13251         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13252
13253         DisplayGameControlValues();
13254
13255         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13256           for (i = 0; i < MAX_PLAYERS; i++)
13257             KillPlayer(&stored_player[i]);
13258       }
13259       else if (game.no_time_limit && !game.all_players_gone)
13260       {
13261         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13262
13263         DisplayGameControlValues();
13264       }
13265     }
13266
13267     if (tape.single_step && tape.recording && !tape.pausing &&
13268         !player->programmed_action)
13269       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13270
13271     if (!player->programmed_action)
13272       CheckSaveEngineSnapshot(player);
13273   }
13274 }
13275
13276 void ScrollScreen(struct PlayerInfo *player, int mode)
13277 {
13278   static unsigned int screen_frame_counter = 0;
13279
13280   if (mode == SCROLL_INIT)
13281   {
13282     // set scrolling step size according to actual player's moving speed
13283     ScrollStepSize = TILEX / player->move_delay_value;
13284
13285     screen_frame_counter = FrameCounter;
13286     ScreenMovDir = player->MovDir;
13287     ScreenMovPos = player->MovPos;
13288     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13289     return;
13290   }
13291   else if (!FrameReached(&screen_frame_counter, 1))
13292     return;
13293
13294   if (ScreenMovPos)
13295   {
13296     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13297     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13298     redraw_mask |= REDRAW_FIELD;
13299   }
13300   else
13301     ScreenMovDir = MV_NONE;
13302 }
13303
13304 void CheckNextToConditions(int x, int y)
13305 {
13306   int element = Tile[x][y];
13307
13308   if (IS_PLAYER(x, y))
13309     TestIfPlayerNextToCustomElement(x, y);
13310
13311   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13312       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13313     TestIfElementNextToCustomElement(x, y);
13314 }
13315
13316 void TestIfPlayerNextToCustomElement(int x, int y)
13317 {
13318   static int xy[4][2] =
13319   {
13320     { 0, -1 },
13321     { -1, 0 },
13322     { +1, 0 },
13323     { 0, +1 }
13324   };
13325   static int trigger_sides[4][2] =
13326   {
13327     // center side       border side
13328     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13329     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13330     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13331     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13332   };
13333   int i;
13334
13335   if (!IS_PLAYER(x, y))
13336     return;
13337
13338   struct PlayerInfo *player = PLAYERINFO(x, y);
13339
13340   if (player->is_moving)
13341     return;
13342
13343   for (i = 0; i < NUM_DIRECTIONS; i++)
13344   {
13345     int xx = x + xy[i][0];
13346     int yy = y + xy[i][1];
13347     int border_side = trigger_sides[i][1];
13348     int border_element;
13349
13350     if (!IN_LEV_FIELD(xx, yy))
13351       continue;
13352
13353     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13354       continue;         // center and border element not connected
13355
13356     border_element = Tile[xx][yy];
13357
13358     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13359                                player->index_bit, border_side);
13360     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13361                                         CE_PLAYER_NEXT_TO_X,
13362                                         player->index_bit, border_side);
13363
13364     /* use player element that is initially defined in the level playfield,
13365        not the player element that corresponds to the runtime player number
13366        (example: a level that contains EL_PLAYER_3 as the only player would
13367        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13368
13369     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13370                              CE_NEXT_TO_X, border_side);
13371   }
13372 }
13373
13374 void TestIfPlayerTouchesCustomElement(int x, int y)
13375 {
13376   static int xy[4][2] =
13377   {
13378     { 0, -1 },
13379     { -1, 0 },
13380     { +1, 0 },
13381     { 0, +1 }
13382   };
13383   static int trigger_sides[4][2] =
13384   {
13385     // center side       border side
13386     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13387     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13388     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13389     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13390   };
13391   static int touch_dir[4] =
13392   {
13393     MV_LEFT | MV_RIGHT,
13394     MV_UP   | MV_DOWN,
13395     MV_UP   | MV_DOWN,
13396     MV_LEFT | MV_RIGHT
13397   };
13398   int center_element = Tile[x][y];      // should always be non-moving!
13399   int i;
13400
13401   for (i = 0; i < NUM_DIRECTIONS; i++)
13402   {
13403     int xx = x + xy[i][0];
13404     int yy = y + xy[i][1];
13405     int center_side = trigger_sides[i][0];
13406     int border_side = trigger_sides[i][1];
13407     int border_element;
13408
13409     if (!IN_LEV_FIELD(xx, yy))
13410       continue;
13411
13412     if (IS_PLAYER(x, y))                // player found at center element
13413     {
13414       struct PlayerInfo *player = PLAYERINFO(x, y);
13415
13416       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13417         border_element = Tile[xx][yy];          // may be moving!
13418       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13419         border_element = Tile[xx][yy];
13420       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13421         border_element = MovingOrBlocked2Element(xx, yy);
13422       else
13423         continue;               // center and border element do not touch
13424
13425       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13426                                  player->index_bit, border_side);
13427       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13428                                           CE_PLAYER_TOUCHES_X,
13429                                           player->index_bit, border_side);
13430
13431       {
13432         /* use player element that is initially defined in the level playfield,
13433            not the player element that corresponds to the runtime player number
13434            (example: a level that contains EL_PLAYER_3 as the only player would
13435            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13436         int player_element = PLAYERINFO(x, y)->initial_element;
13437
13438         CheckElementChangeBySide(xx, yy, border_element, player_element,
13439                                  CE_TOUCHING_X, border_side);
13440       }
13441     }
13442     else if (IS_PLAYER(xx, yy))         // player found at border element
13443     {
13444       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13445
13446       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13447       {
13448         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13449           continue;             // center and border element do not touch
13450       }
13451
13452       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13453                                  player->index_bit, center_side);
13454       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13455                                           CE_PLAYER_TOUCHES_X,
13456                                           player->index_bit, center_side);
13457
13458       {
13459         /* use player element that is initially defined in the level playfield,
13460            not the player element that corresponds to the runtime player number
13461            (example: a level that contains EL_PLAYER_3 as the only player would
13462            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13463         int player_element = PLAYERINFO(xx, yy)->initial_element;
13464
13465         CheckElementChangeBySide(x, y, center_element, player_element,
13466                                  CE_TOUCHING_X, center_side);
13467       }
13468
13469       break;
13470     }
13471   }
13472 }
13473
13474 void TestIfElementNextToCustomElement(int x, int y)
13475 {
13476   static int xy[4][2] =
13477   {
13478     { 0, -1 },
13479     { -1, 0 },
13480     { +1, 0 },
13481     { 0, +1 }
13482   };
13483   static int trigger_sides[4][2] =
13484   {
13485     // center side      border side
13486     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13487     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13488     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13489     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13490   };
13491   int center_element = Tile[x][y];      // should always be non-moving!
13492   int i;
13493
13494   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13495     return;
13496
13497   for (i = 0; i < NUM_DIRECTIONS; i++)
13498   {
13499     int xx = x + xy[i][0];
13500     int yy = y + xy[i][1];
13501     int border_side = trigger_sides[i][1];
13502     int border_element;
13503
13504     if (!IN_LEV_FIELD(xx, yy))
13505       continue;
13506
13507     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13508       continue;                 // center and border element not connected
13509
13510     border_element = Tile[xx][yy];
13511
13512     // check for change of center element (but change it only once)
13513     if (CheckElementChangeBySide(x, y, center_element, border_element,
13514                                  CE_NEXT_TO_X, border_side))
13515       break;
13516   }
13517 }
13518
13519 void TestIfElementTouchesCustomElement(int x, int y)
13520 {
13521   static int xy[4][2] =
13522   {
13523     { 0, -1 },
13524     { -1, 0 },
13525     { +1, 0 },
13526     { 0, +1 }
13527   };
13528   static int trigger_sides[4][2] =
13529   {
13530     // center side      border side
13531     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13532     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13533     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13534     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13535   };
13536   static int touch_dir[4] =
13537   {
13538     MV_LEFT | MV_RIGHT,
13539     MV_UP   | MV_DOWN,
13540     MV_UP   | MV_DOWN,
13541     MV_LEFT | MV_RIGHT
13542   };
13543   boolean change_center_element = FALSE;
13544   int center_element = Tile[x][y];      // should always be non-moving!
13545   int border_element_old[NUM_DIRECTIONS];
13546   int i;
13547
13548   for (i = 0; i < NUM_DIRECTIONS; i++)
13549   {
13550     int xx = x + xy[i][0];
13551     int yy = y + xy[i][1];
13552     int border_element;
13553
13554     border_element_old[i] = -1;
13555
13556     if (!IN_LEV_FIELD(xx, yy))
13557       continue;
13558
13559     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13560       border_element = Tile[xx][yy];    // may be moving!
13561     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13562       border_element = Tile[xx][yy];
13563     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13564       border_element = MovingOrBlocked2Element(xx, yy);
13565     else
13566       continue;                 // center and border element do not touch
13567
13568     border_element_old[i] = border_element;
13569   }
13570
13571   for (i = 0; i < NUM_DIRECTIONS; i++)
13572   {
13573     int xx = x + xy[i][0];
13574     int yy = y + xy[i][1];
13575     int center_side = trigger_sides[i][0];
13576     int border_element = border_element_old[i];
13577
13578     if (border_element == -1)
13579       continue;
13580
13581     // check for change of border element
13582     CheckElementChangeBySide(xx, yy, border_element, center_element,
13583                              CE_TOUCHING_X, center_side);
13584
13585     // (center element cannot be player, so we dont have to check this here)
13586   }
13587
13588   for (i = 0; i < NUM_DIRECTIONS; i++)
13589   {
13590     int xx = x + xy[i][0];
13591     int yy = y + xy[i][1];
13592     int border_side = trigger_sides[i][1];
13593     int border_element = border_element_old[i];
13594
13595     if (border_element == -1)
13596       continue;
13597
13598     // check for change of center element (but change it only once)
13599     if (!change_center_element)
13600       change_center_element =
13601         CheckElementChangeBySide(x, y, center_element, border_element,
13602                                  CE_TOUCHING_X, border_side);
13603
13604     if (IS_PLAYER(xx, yy))
13605     {
13606       /* use player element that is initially defined in the level playfield,
13607          not the player element that corresponds to the runtime player number
13608          (example: a level that contains EL_PLAYER_3 as the only player would
13609          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13610       int player_element = PLAYERINFO(xx, yy)->initial_element;
13611
13612       CheckElementChangeBySide(x, y, center_element, player_element,
13613                                CE_TOUCHING_X, border_side);
13614     }
13615   }
13616 }
13617
13618 void TestIfElementHitsCustomElement(int x, int y, int direction)
13619 {
13620   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13621   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13622   int hitx = x + dx, hity = y + dy;
13623   int hitting_element = Tile[x][y];
13624   int touched_element;
13625
13626   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13627     return;
13628
13629   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13630                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13631
13632   if (IN_LEV_FIELD(hitx, hity))
13633   {
13634     int opposite_direction = MV_DIR_OPPOSITE(direction);
13635     int hitting_side = direction;
13636     int touched_side = opposite_direction;
13637     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13638                           MovDir[hitx][hity] != direction ||
13639                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13640
13641     object_hit = TRUE;
13642
13643     if (object_hit)
13644     {
13645       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13646                                CE_HITTING_X, touched_side);
13647
13648       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13649                                CE_HIT_BY_X, hitting_side);
13650
13651       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13652                                CE_HIT_BY_SOMETHING, opposite_direction);
13653
13654       if (IS_PLAYER(hitx, hity))
13655       {
13656         /* use player element that is initially defined in the level playfield,
13657            not the player element that corresponds to the runtime player number
13658            (example: a level that contains EL_PLAYER_3 as the only player would
13659            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13660         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13661
13662         CheckElementChangeBySide(x, y, hitting_element, player_element,
13663                                  CE_HITTING_X, touched_side);
13664       }
13665     }
13666   }
13667
13668   // "hitting something" is also true when hitting the playfield border
13669   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13670                            CE_HITTING_SOMETHING, direction);
13671 }
13672
13673 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13674 {
13675   int i, kill_x = -1, kill_y = -1;
13676
13677   int bad_element = -1;
13678   static int test_xy[4][2] =
13679   {
13680     { 0, -1 },
13681     { -1, 0 },
13682     { +1, 0 },
13683     { 0, +1 }
13684   };
13685   static int test_dir[4] =
13686   {
13687     MV_UP,
13688     MV_LEFT,
13689     MV_RIGHT,
13690     MV_DOWN
13691   };
13692
13693   for (i = 0; i < NUM_DIRECTIONS; i++)
13694   {
13695     int test_x, test_y, test_move_dir, test_element;
13696
13697     test_x = good_x + test_xy[i][0];
13698     test_y = good_y + test_xy[i][1];
13699
13700     if (!IN_LEV_FIELD(test_x, test_y))
13701       continue;
13702
13703     test_move_dir =
13704       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13705
13706     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13707
13708     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13709        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13710     */
13711     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13712         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13713     {
13714       kill_x = test_x;
13715       kill_y = test_y;
13716       bad_element = test_element;
13717
13718       break;
13719     }
13720   }
13721
13722   if (kill_x != -1 || kill_y != -1)
13723   {
13724     if (IS_PLAYER(good_x, good_y))
13725     {
13726       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13727
13728       if (player->shield_deadly_time_left > 0 &&
13729           !IS_INDESTRUCTIBLE(bad_element))
13730         Bang(kill_x, kill_y);
13731       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13732         KillPlayer(player);
13733     }
13734     else
13735       Bang(good_x, good_y);
13736   }
13737 }
13738
13739 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13740 {
13741   int i, kill_x = -1, kill_y = -1;
13742   int bad_element = Tile[bad_x][bad_y];
13743   static int test_xy[4][2] =
13744   {
13745     { 0, -1 },
13746     { -1, 0 },
13747     { +1, 0 },
13748     { 0, +1 }
13749   };
13750   static int touch_dir[4] =
13751   {
13752     MV_LEFT | MV_RIGHT,
13753     MV_UP   | MV_DOWN,
13754     MV_UP   | MV_DOWN,
13755     MV_LEFT | MV_RIGHT
13756   };
13757   static int test_dir[4] =
13758   {
13759     MV_UP,
13760     MV_LEFT,
13761     MV_RIGHT,
13762     MV_DOWN
13763   };
13764
13765   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13766     return;
13767
13768   for (i = 0; i < NUM_DIRECTIONS; i++)
13769   {
13770     int test_x, test_y, test_move_dir, test_element;
13771
13772     test_x = bad_x + test_xy[i][0];
13773     test_y = bad_y + test_xy[i][1];
13774
13775     if (!IN_LEV_FIELD(test_x, test_y))
13776       continue;
13777
13778     test_move_dir =
13779       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13780
13781     test_element = Tile[test_x][test_y];
13782
13783     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13784        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13785     */
13786     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13787         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13788     {
13789       // good thing is player or penguin that does not move away
13790       if (IS_PLAYER(test_x, test_y))
13791       {
13792         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13793
13794         if (bad_element == EL_ROBOT && player->is_moving)
13795           continue;     // robot does not kill player if he is moving
13796
13797         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13798         {
13799           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13800             continue;           // center and border element do not touch
13801         }
13802
13803         kill_x = test_x;
13804         kill_y = test_y;
13805
13806         break;
13807       }
13808       else if (test_element == EL_PENGUIN)
13809       {
13810         kill_x = test_x;
13811         kill_y = test_y;
13812
13813         break;
13814       }
13815     }
13816   }
13817
13818   if (kill_x != -1 || kill_y != -1)
13819   {
13820     if (IS_PLAYER(kill_x, kill_y))
13821     {
13822       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13823
13824       if (player->shield_deadly_time_left > 0 &&
13825           !IS_INDESTRUCTIBLE(bad_element))
13826         Bang(bad_x, bad_y);
13827       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13828         KillPlayer(player);
13829     }
13830     else
13831       Bang(kill_x, kill_y);
13832   }
13833 }
13834
13835 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13836 {
13837   int bad_element = Tile[bad_x][bad_y];
13838   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13839   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13840   int test_x = bad_x + dx, test_y = bad_y + dy;
13841   int test_move_dir, test_element;
13842   int kill_x = -1, kill_y = -1;
13843
13844   if (!IN_LEV_FIELD(test_x, test_y))
13845     return;
13846
13847   test_move_dir =
13848     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13849
13850   test_element = Tile[test_x][test_y];
13851
13852   if (test_move_dir != bad_move_dir)
13853   {
13854     // good thing can be player or penguin that does not move away
13855     if (IS_PLAYER(test_x, test_y))
13856     {
13857       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13858
13859       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13860          player as being hit when he is moving towards the bad thing, because
13861          the "get hit by" condition would be lost after the player stops) */
13862       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13863         return;         // player moves away from bad thing
13864
13865       kill_x = test_x;
13866       kill_y = test_y;
13867     }
13868     else if (test_element == EL_PENGUIN)
13869     {
13870       kill_x = test_x;
13871       kill_y = test_y;
13872     }
13873   }
13874
13875   if (kill_x != -1 || kill_y != -1)
13876   {
13877     if (IS_PLAYER(kill_x, kill_y))
13878     {
13879       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13880
13881       if (player->shield_deadly_time_left > 0 &&
13882           !IS_INDESTRUCTIBLE(bad_element))
13883         Bang(bad_x, bad_y);
13884       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13885         KillPlayer(player);
13886     }
13887     else
13888       Bang(kill_x, kill_y);
13889   }
13890 }
13891
13892 void TestIfPlayerTouchesBadThing(int x, int y)
13893 {
13894   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13895 }
13896
13897 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13898 {
13899   TestIfGoodThingHitsBadThing(x, y, move_dir);
13900 }
13901
13902 void TestIfBadThingTouchesPlayer(int x, int y)
13903 {
13904   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13905 }
13906
13907 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13908 {
13909   TestIfBadThingHitsGoodThing(x, y, move_dir);
13910 }
13911
13912 void TestIfFriendTouchesBadThing(int x, int y)
13913 {
13914   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13915 }
13916
13917 void TestIfBadThingTouchesFriend(int x, int y)
13918 {
13919   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13920 }
13921
13922 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13923 {
13924   int i, kill_x = bad_x, kill_y = bad_y;
13925   static int xy[4][2] =
13926   {
13927     { 0, -1 },
13928     { -1, 0 },
13929     { +1, 0 },
13930     { 0, +1 }
13931   };
13932
13933   for (i = 0; i < NUM_DIRECTIONS; i++)
13934   {
13935     int x, y, element;
13936
13937     x = bad_x + xy[i][0];
13938     y = bad_y + xy[i][1];
13939     if (!IN_LEV_FIELD(x, y))
13940       continue;
13941
13942     element = Tile[x][y];
13943     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13944         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13945     {
13946       kill_x = x;
13947       kill_y = y;
13948       break;
13949     }
13950   }
13951
13952   if (kill_x != bad_x || kill_y != bad_y)
13953     Bang(bad_x, bad_y);
13954 }
13955
13956 void KillPlayer(struct PlayerInfo *player)
13957 {
13958   int jx = player->jx, jy = player->jy;
13959
13960   if (!player->active)
13961     return;
13962
13963 #if 0
13964   Debug("game:playing:KillPlayer",
13965         "0: killed == %d, active == %d, reanimated == %d",
13966         player->killed, player->active, player->reanimated);
13967 #endif
13968
13969   /* the following code was introduced to prevent an infinite loop when calling
13970      -> Bang()
13971      -> CheckTriggeredElementChangeExt()
13972      -> ExecuteCustomElementAction()
13973      -> KillPlayer()
13974      -> (infinitely repeating the above sequence of function calls)
13975      which occurs when killing the player while having a CE with the setting
13976      "kill player X when explosion of <player X>"; the solution using a new
13977      field "player->killed" was chosen for backwards compatibility, although
13978      clever use of the fields "player->active" etc. would probably also work */
13979 #if 1
13980   if (player->killed)
13981     return;
13982 #endif
13983
13984   player->killed = TRUE;
13985
13986   // remove accessible field at the player's position
13987   Tile[jx][jy] = EL_EMPTY;
13988
13989   // deactivate shield (else Bang()/Explode() would not work right)
13990   player->shield_normal_time_left = 0;
13991   player->shield_deadly_time_left = 0;
13992
13993 #if 0
13994   Debug("game:playing:KillPlayer",
13995         "1: killed == %d, active == %d, reanimated == %d",
13996         player->killed, player->active, player->reanimated);
13997 #endif
13998
13999   Bang(jx, jy);
14000
14001 #if 0
14002   Debug("game:playing:KillPlayer",
14003         "2: killed == %d, active == %d, reanimated == %d",
14004         player->killed, player->active, player->reanimated);
14005 #endif
14006
14007   if (player->reanimated)       // killed player may have been reanimated
14008     player->killed = player->reanimated = FALSE;
14009   else
14010     BuryPlayer(player);
14011 }
14012
14013 static void KillPlayerUnlessEnemyProtected(int x, int y)
14014 {
14015   if (!PLAYER_ENEMY_PROTECTED(x, y))
14016     KillPlayer(PLAYERINFO(x, y));
14017 }
14018
14019 static void KillPlayerUnlessExplosionProtected(int x, int y)
14020 {
14021   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14022     KillPlayer(PLAYERINFO(x, y));
14023 }
14024
14025 void BuryPlayer(struct PlayerInfo *player)
14026 {
14027   int jx = player->jx, jy = player->jy;
14028
14029   if (!player->active)
14030     return;
14031
14032   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14033   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14034
14035   RemovePlayer(player);
14036
14037   player->buried = TRUE;
14038
14039   if (game.all_players_gone)
14040     game.GameOver = TRUE;
14041 }
14042
14043 void RemovePlayer(struct PlayerInfo *player)
14044 {
14045   int jx = player->jx, jy = player->jy;
14046   int i, found = FALSE;
14047
14048   player->present = FALSE;
14049   player->active = FALSE;
14050
14051   // required for some CE actions (even if the player is not active anymore)
14052   player->MovPos = 0;
14053
14054   if (!ExplodeField[jx][jy])
14055     StorePlayer[jx][jy] = 0;
14056
14057   if (player->is_moving)
14058     TEST_DrawLevelField(player->last_jx, player->last_jy);
14059
14060   for (i = 0; i < MAX_PLAYERS; i++)
14061     if (stored_player[i].active)
14062       found = TRUE;
14063
14064   if (!found)
14065   {
14066     game.all_players_gone = TRUE;
14067     game.GameOver = TRUE;
14068   }
14069
14070   game.exit_x = game.robot_wheel_x = jx;
14071   game.exit_y = game.robot_wheel_y = jy;
14072 }
14073
14074 void ExitPlayer(struct PlayerInfo *player)
14075 {
14076   DrawPlayer(player);   // needed here only to cleanup last field
14077   RemovePlayer(player);
14078
14079   if (game.players_still_needed > 0)
14080     game.players_still_needed--;
14081 }
14082
14083 static void SetFieldForSnapping(int x, int y, int element, int direction,
14084                                 int player_index_bit)
14085 {
14086   struct ElementInfo *ei = &element_info[element];
14087   int direction_bit = MV_DIR_TO_BIT(direction);
14088   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14089   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14090                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14091
14092   Tile[x][y] = EL_ELEMENT_SNAPPING;
14093   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14094   MovDir[x][y] = direction;
14095   Store[x][y] = element;
14096   Store2[x][y] = player_index_bit;
14097
14098   ResetGfxAnimation(x, y);
14099
14100   GfxElement[x][y] = element;
14101   GfxAction[x][y] = action;
14102   GfxDir[x][y] = direction;
14103   GfxFrame[x][y] = -1;
14104 }
14105
14106 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14107                                    int player_index_bit)
14108 {
14109   TestIfElementTouchesCustomElement(x, y);      // for empty space
14110
14111   if (level.finish_dig_collect)
14112   {
14113     int dig_side = MV_DIR_OPPOSITE(direction);
14114     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14115                         CE_PLAYER_COLLECTS_X);
14116
14117     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14118                                         player_index_bit, dig_side);
14119     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14120                                         player_index_bit, dig_side);
14121   }
14122 }
14123
14124 /*
14125   =============================================================================
14126   checkDiagonalPushing()
14127   -----------------------------------------------------------------------------
14128   check if diagonal input device direction results in pushing of object
14129   (by checking if the alternative direction is walkable, diggable, ...)
14130   =============================================================================
14131 */
14132
14133 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14134                                     int x, int y, int real_dx, int real_dy)
14135 {
14136   int jx, jy, dx, dy, xx, yy;
14137
14138   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14139     return TRUE;
14140
14141   // diagonal direction: check alternative direction
14142   jx = player->jx;
14143   jy = player->jy;
14144   dx = x - jx;
14145   dy = y - jy;
14146   xx = jx + (dx == 0 ? real_dx : 0);
14147   yy = jy + (dy == 0 ? real_dy : 0);
14148
14149   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14150 }
14151
14152 /*
14153   =============================================================================
14154   DigField()
14155   -----------------------------------------------------------------------------
14156   x, y:                 field next to player (non-diagonal) to try to dig to
14157   real_dx, real_dy:     direction as read from input device (can be diagonal)
14158   =============================================================================
14159 */
14160
14161 static int DigField(struct PlayerInfo *player,
14162                     int oldx, int oldy, int x, int y,
14163                     int real_dx, int real_dy, int mode)
14164 {
14165   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14166   boolean player_was_pushing = player->is_pushing;
14167   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14168   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14169   int jx = oldx, jy = oldy;
14170   int dx = x - jx, dy = y - jy;
14171   int nextx = x + dx, nexty = y + dy;
14172   int move_direction = (dx == -1 ? MV_LEFT  :
14173                         dx == +1 ? MV_RIGHT :
14174                         dy == -1 ? MV_UP    :
14175                         dy == +1 ? MV_DOWN  : MV_NONE);
14176   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14177   int dig_side = MV_DIR_OPPOSITE(move_direction);
14178   int old_element = Tile[jx][jy];
14179   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14180   int collect_count;
14181
14182   if (is_player)                // function can also be called by EL_PENGUIN
14183   {
14184     if (player->MovPos == 0)
14185     {
14186       player->is_digging = FALSE;
14187       player->is_collecting = FALSE;
14188     }
14189
14190     if (player->MovPos == 0)    // last pushing move finished
14191       player->is_pushing = FALSE;
14192
14193     if (mode == DF_NO_PUSH)     // player just stopped pushing
14194     {
14195       player->is_switching = FALSE;
14196       player->push_delay = -1;
14197
14198       return MP_NO_ACTION;
14199     }
14200   }
14201
14202   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14203     old_element = Back[jx][jy];
14204
14205   // in case of element dropped at player position, check background
14206   else if (Back[jx][jy] != EL_EMPTY &&
14207            game.engine_version >= VERSION_IDENT(2,2,0,0))
14208     old_element = Back[jx][jy];
14209
14210   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14211     return MP_NO_ACTION;        // field has no opening in this direction
14212
14213   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14214     return MP_NO_ACTION;        // field has no opening in this direction
14215
14216   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14217   {
14218     SplashAcid(x, y);
14219
14220     Tile[jx][jy] = player->artwork_element;
14221     InitMovingField(jx, jy, MV_DOWN);
14222     Store[jx][jy] = EL_ACID;
14223     ContinueMoving(jx, jy);
14224     BuryPlayer(player);
14225
14226     return MP_DONT_RUN_INTO;
14227   }
14228
14229   if (player_can_move && DONT_RUN_INTO(element))
14230   {
14231     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14232
14233     return MP_DONT_RUN_INTO;
14234   }
14235
14236   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14237     return MP_NO_ACTION;
14238
14239   collect_count = element_info[element].collect_count_initial;
14240
14241   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14242     return MP_NO_ACTION;
14243
14244   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14245     player_can_move = player_can_move_or_snap;
14246
14247   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14248       game.engine_version >= VERSION_IDENT(2,2,0,0))
14249   {
14250     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14251                                player->index_bit, dig_side);
14252     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14253                                         player->index_bit, dig_side);
14254
14255     if (element == EL_DC_LANDMINE)
14256       Bang(x, y);
14257
14258     if (Tile[x][y] != element)          // field changed by snapping
14259       return MP_ACTION;
14260
14261     return MP_NO_ACTION;
14262   }
14263
14264   if (player->gravity && is_player && !player->is_auto_moving &&
14265       canFallDown(player) && move_direction != MV_DOWN &&
14266       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14267     return MP_NO_ACTION;        // player cannot walk here due to gravity
14268
14269   if (player_can_move &&
14270       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14271   {
14272     int sound_element = SND_ELEMENT(element);
14273     int sound_action = ACTION_WALKING;
14274
14275     if (IS_RND_GATE(element))
14276     {
14277       if (!player->key[RND_GATE_NR(element)])
14278         return MP_NO_ACTION;
14279     }
14280     else if (IS_RND_GATE_GRAY(element))
14281     {
14282       if (!player->key[RND_GATE_GRAY_NR(element)])
14283         return MP_NO_ACTION;
14284     }
14285     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14286     {
14287       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14288         return MP_NO_ACTION;
14289     }
14290     else if (element == EL_EXIT_OPEN ||
14291              element == EL_EM_EXIT_OPEN ||
14292              element == EL_EM_EXIT_OPENING ||
14293              element == EL_STEEL_EXIT_OPEN ||
14294              element == EL_EM_STEEL_EXIT_OPEN ||
14295              element == EL_EM_STEEL_EXIT_OPENING ||
14296              element == EL_SP_EXIT_OPEN ||
14297              element == EL_SP_EXIT_OPENING)
14298     {
14299       sound_action = ACTION_PASSING;    // player is passing exit
14300     }
14301     else if (element == EL_EMPTY)
14302     {
14303       sound_action = ACTION_MOVING;             // nothing to walk on
14304     }
14305
14306     // play sound from background or player, whatever is available
14307     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14308       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14309     else
14310       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14311   }
14312   else if (player_can_move &&
14313            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14314   {
14315     if (!ACCESS_FROM(element, opposite_direction))
14316       return MP_NO_ACTION;      // field not accessible from this direction
14317
14318     if (CAN_MOVE(element))      // only fixed elements can be passed!
14319       return MP_NO_ACTION;
14320
14321     if (IS_EM_GATE(element))
14322     {
14323       if (!player->key[EM_GATE_NR(element)])
14324         return MP_NO_ACTION;
14325     }
14326     else if (IS_EM_GATE_GRAY(element))
14327     {
14328       if (!player->key[EM_GATE_GRAY_NR(element)])
14329         return MP_NO_ACTION;
14330     }
14331     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14332     {
14333       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14334         return MP_NO_ACTION;
14335     }
14336     else if (IS_EMC_GATE(element))
14337     {
14338       if (!player->key[EMC_GATE_NR(element)])
14339         return MP_NO_ACTION;
14340     }
14341     else if (IS_EMC_GATE_GRAY(element))
14342     {
14343       if (!player->key[EMC_GATE_GRAY_NR(element)])
14344         return MP_NO_ACTION;
14345     }
14346     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14347     {
14348       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14349         return MP_NO_ACTION;
14350     }
14351     else if (element == EL_DC_GATE_WHITE ||
14352              element == EL_DC_GATE_WHITE_GRAY ||
14353              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14354     {
14355       if (player->num_white_keys == 0)
14356         return MP_NO_ACTION;
14357
14358       player->num_white_keys--;
14359     }
14360     else if (IS_SP_PORT(element))
14361     {
14362       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14363           element == EL_SP_GRAVITY_PORT_RIGHT ||
14364           element == EL_SP_GRAVITY_PORT_UP ||
14365           element == EL_SP_GRAVITY_PORT_DOWN)
14366         player->gravity = !player->gravity;
14367       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14368                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14369                element == EL_SP_GRAVITY_ON_PORT_UP ||
14370                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14371         player->gravity = TRUE;
14372       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14373                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14374                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14375                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14376         player->gravity = FALSE;
14377     }
14378
14379     // automatically move to the next field with double speed
14380     player->programmed_action = move_direction;
14381
14382     if (player->move_delay_reset_counter == 0)
14383     {
14384       player->move_delay_reset_counter = 2;     // two double speed steps
14385
14386       DOUBLE_PLAYER_SPEED(player);
14387     }
14388
14389     PlayLevelSoundAction(x, y, ACTION_PASSING);
14390   }
14391   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14392   {
14393     RemoveField(x, y);
14394
14395     if (mode != DF_SNAP)
14396     {
14397       GfxElement[x][y] = GFX_ELEMENT(element);
14398       player->is_digging = TRUE;
14399     }
14400
14401     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14402
14403     // use old behaviour for old levels (digging)
14404     if (!level.finish_dig_collect)
14405     {
14406       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14407                                           player->index_bit, dig_side);
14408
14409       // if digging triggered player relocation, finish digging tile
14410       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14411         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14412     }
14413
14414     if (mode == DF_SNAP)
14415     {
14416       if (level.block_snap_field)
14417         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14418       else
14419         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14420
14421       // use old behaviour for old levels (snapping)
14422       if (!level.finish_dig_collect)
14423         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14424                                             player->index_bit, dig_side);
14425     }
14426   }
14427   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14428   {
14429     RemoveField(x, y);
14430
14431     if (is_player && mode != DF_SNAP)
14432     {
14433       GfxElement[x][y] = element;
14434       player->is_collecting = TRUE;
14435     }
14436
14437     if (element == EL_SPEED_PILL)
14438     {
14439       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14440     }
14441     else if (element == EL_EXTRA_TIME && level.time > 0)
14442     {
14443       TimeLeft += level.extra_time;
14444
14445       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14446
14447       DisplayGameControlValues();
14448     }
14449     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14450     {
14451       player->shield_normal_time_left += level.shield_normal_time;
14452       if (element == EL_SHIELD_DEADLY)
14453         player->shield_deadly_time_left += level.shield_deadly_time;
14454     }
14455     else if (element == EL_DYNAMITE ||
14456              element == EL_EM_DYNAMITE ||
14457              element == EL_SP_DISK_RED)
14458     {
14459       if (player->inventory_size < MAX_INVENTORY_SIZE)
14460         player->inventory_element[player->inventory_size++] = element;
14461
14462       DrawGameDoorValues();
14463     }
14464     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14465     {
14466       player->dynabomb_count++;
14467       player->dynabombs_left++;
14468     }
14469     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14470     {
14471       player->dynabomb_size++;
14472     }
14473     else if (element == EL_DYNABOMB_INCREASE_POWER)
14474     {
14475       player->dynabomb_xl = TRUE;
14476     }
14477     else if (IS_KEY(element))
14478     {
14479       player->key[KEY_NR(element)] = TRUE;
14480
14481       DrawGameDoorValues();
14482     }
14483     else if (element == EL_DC_KEY_WHITE)
14484     {
14485       player->num_white_keys++;
14486
14487       // display white keys?
14488       // DrawGameDoorValues();
14489     }
14490     else if (IS_ENVELOPE(element))
14491     {
14492       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14493
14494       if (!wait_for_snapping)
14495         player->show_envelope = element;
14496     }
14497     else if (element == EL_EMC_LENSES)
14498     {
14499       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14500
14501       RedrawAllInvisibleElementsForLenses();
14502     }
14503     else if (element == EL_EMC_MAGNIFIER)
14504     {
14505       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14506
14507       RedrawAllInvisibleElementsForMagnifier();
14508     }
14509     else if (IS_DROPPABLE(element) ||
14510              IS_THROWABLE(element))     // can be collected and dropped
14511     {
14512       int i;
14513
14514       if (collect_count == 0)
14515         player->inventory_infinite_element = element;
14516       else
14517         for (i = 0; i < collect_count; i++)
14518           if (player->inventory_size < MAX_INVENTORY_SIZE)
14519             player->inventory_element[player->inventory_size++] = element;
14520
14521       DrawGameDoorValues();
14522     }
14523     else if (collect_count > 0)
14524     {
14525       game.gems_still_needed -= collect_count;
14526       if (game.gems_still_needed < 0)
14527         game.gems_still_needed = 0;
14528
14529       game.snapshot.collected_item = TRUE;
14530
14531       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14532
14533       DisplayGameControlValues();
14534     }
14535
14536     RaiseScoreElement(element);
14537     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14538
14539     // use old behaviour for old levels (collecting)
14540     if (!level.finish_dig_collect && is_player)
14541     {
14542       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14543                                           player->index_bit, dig_side);
14544
14545       // if collecting triggered player relocation, finish collecting tile
14546       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14547         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14548     }
14549
14550     if (mode == DF_SNAP)
14551     {
14552       if (level.block_snap_field)
14553         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14554       else
14555         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14556
14557       // use old behaviour for old levels (snapping)
14558       if (!level.finish_dig_collect)
14559         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14560                                             player->index_bit, dig_side);
14561     }
14562   }
14563   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14564   {
14565     if (mode == DF_SNAP && element != EL_BD_ROCK)
14566       return MP_NO_ACTION;
14567
14568     if (CAN_FALL(element) && dy)
14569       return MP_NO_ACTION;
14570
14571     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14572         !(element == EL_SPRING && level.use_spring_bug))
14573       return MP_NO_ACTION;
14574
14575     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14576         ((move_direction & MV_VERTICAL &&
14577           ((element_info[element].move_pattern & MV_LEFT &&
14578             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14579            (element_info[element].move_pattern & MV_RIGHT &&
14580             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14581          (move_direction & MV_HORIZONTAL &&
14582           ((element_info[element].move_pattern & MV_UP &&
14583             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14584            (element_info[element].move_pattern & MV_DOWN &&
14585             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14586       return MP_NO_ACTION;
14587
14588     // do not push elements already moving away faster than player
14589     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14590         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14591       return MP_NO_ACTION;
14592
14593     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14594     {
14595       if (player->push_delay_value == -1 || !player_was_pushing)
14596         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14597     }
14598     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14599     {
14600       if (player->push_delay_value == -1)
14601         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14602     }
14603     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14604     {
14605       if (!player->is_pushing)
14606         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14607     }
14608
14609     player->is_pushing = TRUE;
14610     player->is_active = TRUE;
14611
14612     if (!(IN_LEV_FIELD(nextx, nexty) &&
14613           (IS_FREE(nextx, nexty) ||
14614            (IS_SB_ELEMENT(element) &&
14615             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14616            (IS_CUSTOM_ELEMENT(element) &&
14617             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14618       return MP_NO_ACTION;
14619
14620     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14621       return MP_NO_ACTION;
14622
14623     if (player->push_delay == -1)       // new pushing; restart delay
14624       player->push_delay = 0;
14625
14626     if (player->push_delay < player->push_delay_value &&
14627         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14628         element != EL_SPRING && element != EL_BALLOON)
14629     {
14630       // make sure that there is no move delay before next try to push
14631       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14632         player->move_delay = 0;
14633
14634       return MP_NO_ACTION;
14635     }
14636
14637     if (IS_CUSTOM_ELEMENT(element) &&
14638         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14639     {
14640       if (!DigFieldByCE(nextx, nexty, element))
14641         return MP_NO_ACTION;
14642     }
14643
14644     if (IS_SB_ELEMENT(element))
14645     {
14646       boolean sokoban_task_solved = FALSE;
14647
14648       if (element == EL_SOKOBAN_FIELD_FULL)
14649       {
14650         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14651
14652         IncrementSokobanFieldsNeeded();
14653         IncrementSokobanObjectsNeeded();
14654       }
14655
14656       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14657       {
14658         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14659
14660         DecrementSokobanFieldsNeeded();
14661         DecrementSokobanObjectsNeeded();
14662
14663         // sokoban object was pushed from empty field to sokoban field
14664         if (Back[x][y] == EL_EMPTY)
14665           sokoban_task_solved = TRUE;
14666       }
14667
14668       Tile[x][y] = EL_SOKOBAN_OBJECT;
14669
14670       if (Back[x][y] == Back[nextx][nexty])
14671         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14672       else if (Back[x][y] != 0)
14673         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14674                                     ACTION_EMPTYING);
14675       else
14676         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14677                                     ACTION_FILLING);
14678
14679       if (sokoban_task_solved &&
14680           game.sokoban_fields_still_needed == 0 &&
14681           game.sokoban_objects_still_needed == 0 &&
14682           level.auto_exit_sokoban)
14683       {
14684         game.players_still_needed = 0;
14685
14686         LevelSolved();
14687
14688         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14689       }
14690     }
14691     else
14692       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14693
14694     InitMovingField(x, y, move_direction);
14695     GfxAction[x][y] = ACTION_PUSHING;
14696
14697     if (mode == DF_SNAP)
14698       ContinueMoving(x, y);
14699     else
14700       MovPos[x][y] = (dx != 0 ? dx : dy);
14701
14702     Pushed[x][y] = TRUE;
14703     Pushed[nextx][nexty] = TRUE;
14704
14705     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14706       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14707     else
14708       player->push_delay_value = -1;    // get new value later
14709
14710     // check for element change _after_ element has been pushed
14711     if (game.use_change_when_pushing_bug)
14712     {
14713       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14714                                  player->index_bit, dig_side);
14715       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14716                                           player->index_bit, dig_side);
14717     }
14718   }
14719   else if (IS_SWITCHABLE(element))
14720   {
14721     if (PLAYER_SWITCHING(player, x, y))
14722     {
14723       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14724                                           player->index_bit, dig_side);
14725
14726       return MP_ACTION;
14727     }
14728
14729     player->is_switching = TRUE;
14730     player->switch_x = x;
14731     player->switch_y = y;
14732
14733     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14734
14735     if (element == EL_ROBOT_WHEEL)
14736     {
14737       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14738
14739       game.robot_wheel_x = x;
14740       game.robot_wheel_y = y;
14741       game.robot_wheel_active = TRUE;
14742
14743       TEST_DrawLevelField(x, y);
14744     }
14745     else if (element == EL_SP_TERMINAL)
14746     {
14747       int xx, yy;
14748
14749       SCAN_PLAYFIELD(xx, yy)
14750       {
14751         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14752         {
14753           Bang(xx, yy);
14754         }
14755         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14756         {
14757           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14758
14759           ResetGfxAnimation(xx, yy);
14760           TEST_DrawLevelField(xx, yy);
14761         }
14762       }
14763     }
14764     else if (IS_BELT_SWITCH(element))
14765     {
14766       ToggleBeltSwitch(x, y);
14767     }
14768     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14769              element == EL_SWITCHGATE_SWITCH_DOWN ||
14770              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14771              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14772     {
14773       ToggleSwitchgateSwitch(x, y);
14774     }
14775     else if (element == EL_LIGHT_SWITCH ||
14776              element == EL_LIGHT_SWITCH_ACTIVE)
14777     {
14778       ToggleLightSwitch(x, y);
14779     }
14780     else if (element == EL_TIMEGATE_SWITCH ||
14781              element == EL_DC_TIMEGATE_SWITCH)
14782     {
14783       ActivateTimegateSwitch(x, y);
14784     }
14785     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14786              element == EL_BALLOON_SWITCH_RIGHT ||
14787              element == EL_BALLOON_SWITCH_UP    ||
14788              element == EL_BALLOON_SWITCH_DOWN  ||
14789              element == EL_BALLOON_SWITCH_NONE  ||
14790              element == EL_BALLOON_SWITCH_ANY)
14791     {
14792       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14793                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14794                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14795                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14796                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14797                              move_direction);
14798     }
14799     else if (element == EL_LAMP)
14800     {
14801       Tile[x][y] = EL_LAMP_ACTIVE;
14802       game.lights_still_needed--;
14803
14804       ResetGfxAnimation(x, y);
14805       TEST_DrawLevelField(x, y);
14806     }
14807     else if (element == EL_TIME_ORB_FULL)
14808     {
14809       Tile[x][y] = EL_TIME_ORB_EMPTY;
14810
14811       if (level.time > 0 || level.use_time_orb_bug)
14812       {
14813         TimeLeft += level.time_orb_time;
14814         game.no_time_limit = FALSE;
14815
14816         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14817
14818         DisplayGameControlValues();
14819       }
14820
14821       ResetGfxAnimation(x, y);
14822       TEST_DrawLevelField(x, y);
14823     }
14824     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14825              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14826     {
14827       int xx, yy;
14828
14829       game.ball_active = !game.ball_active;
14830
14831       SCAN_PLAYFIELD(xx, yy)
14832       {
14833         int e = Tile[xx][yy];
14834
14835         if (game.ball_active)
14836         {
14837           if (e == EL_EMC_MAGIC_BALL)
14838             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14839           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14840             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14841         }
14842         else
14843         {
14844           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14845             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14846           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14847             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14848         }
14849       }
14850     }
14851
14852     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14853                                         player->index_bit, dig_side);
14854
14855     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14856                                         player->index_bit, dig_side);
14857
14858     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14859                                         player->index_bit, dig_side);
14860
14861     return MP_ACTION;
14862   }
14863   else
14864   {
14865     if (!PLAYER_SWITCHING(player, x, y))
14866     {
14867       player->is_switching = TRUE;
14868       player->switch_x = x;
14869       player->switch_y = y;
14870
14871       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14872                                  player->index_bit, dig_side);
14873       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14874                                           player->index_bit, dig_side);
14875
14876       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14877                                  player->index_bit, dig_side);
14878       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14879                                           player->index_bit, dig_side);
14880     }
14881
14882     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14883                                player->index_bit, dig_side);
14884     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14885                                         player->index_bit, dig_side);
14886
14887     return MP_NO_ACTION;
14888   }
14889
14890   player->push_delay = -1;
14891
14892   if (is_player)                // function can also be called by EL_PENGUIN
14893   {
14894     if (Tile[x][y] != element)          // really digged/collected something
14895     {
14896       player->is_collecting = !player->is_digging;
14897       player->is_active = TRUE;
14898
14899       player->last_removed_element = element;
14900     }
14901   }
14902
14903   return MP_MOVING;
14904 }
14905
14906 static boolean DigFieldByCE(int x, int y, int digging_element)
14907 {
14908   int element = Tile[x][y];
14909
14910   if (!IS_FREE(x, y))
14911   {
14912     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14913                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14914                   ACTION_BREAKING);
14915
14916     // no element can dig solid indestructible elements
14917     if (IS_INDESTRUCTIBLE(element) &&
14918         !IS_DIGGABLE(element) &&
14919         !IS_COLLECTIBLE(element))
14920       return FALSE;
14921
14922     if (AmoebaNr[x][y] &&
14923         (element == EL_AMOEBA_FULL ||
14924          element == EL_BD_AMOEBA ||
14925          element == EL_AMOEBA_GROWING))
14926     {
14927       AmoebaCnt[AmoebaNr[x][y]]--;
14928       AmoebaCnt2[AmoebaNr[x][y]]--;
14929     }
14930
14931     if (IS_MOVING(x, y))
14932       RemoveMovingField(x, y);
14933     else
14934     {
14935       RemoveField(x, y);
14936       TEST_DrawLevelField(x, y);
14937     }
14938
14939     // if digged element was about to explode, prevent the explosion
14940     ExplodeField[x][y] = EX_TYPE_NONE;
14941
14942     PlayLevelSoundAction(x, y, action);
14943   }
14944
14945   Store[x][y] = EL_EMPTY;
14946
14947   // this makes it possible to leave the removed element again
14948   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14949     Store[x][y] = element;
14950
14951   return TRUE;
14952 }
14953
14954 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14955 {
14956   int jx = player->jx, jy = player->jy;
14957   int x = jx + dx, y = jy + dy;
14958   int snap_direction = (dx == -1 ? MV_LEFT  :
14959                         dx == +1 ? MV_RIGHT :
14960                         dy == -1 ? MV_UP    :
14961                         dy == +1 ? MV_DOWN  : MV_NONE);
14962   boolean can_continue_snapping = (level.continuous_snapping &&
14963                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14964
14965   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14966     return FALSE;
14967
14968   if (!player->active || !IN_LEV_FIELD(x, y))
14969     return FALSE;
14970
14971   if (dx && dy)
14972     return FALSE;
14973
14974   if (!dx && !dy)
14975   {
14976     if (player->MovPos == 0)
14977       player->is_pushing = FALSE;
14978
14979     player->is_snapping = FALSE;
14980
14981     if (player->MovPos == 0)
14982     {
14983       player->is_moving = FALSE;
14984       player->is_digging = FALSE;
14985       player->is_collecting = FALSE;
14986     }
14987
14988     return FALSE;
14989   }
14990
14991   // prevent snapping with already pressed snap key when not allowed
14992   if (player->is_snapping && !can_continue_snapping)
14993     return FALSE;
14994
14995   player->MovDir = snap_direction;
14996
14997   if (player->MovPos == 0)
14998   {
14999     player->is_moving = FALSE;
15000     player->is_digging = FALSE;
15001     player->is_collecting = FALSE;
15002   }
15003
15004   player->is_dropping = FALSE;
15005   player->is_dropping_pressed = FALSE;
15006   player->drop_pressed_delay = 0;
15007
15008   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15009     return FALSE;
15010
15011   player->is_snapping = TRUE;
15012   player->is_active = TRUE;
15013
15014   if (player->MovPos == 0)
15015   {
15016     player->is_moving = FALSE;
15017     player->is_digging = FALSE;
15018     player->is_collecting = FALSE;
15019   }
15020
15021   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15022     TEST_DrawLevelField(player->last_jx, player->last_jy);
15023
15024   TEST_DrawLevelField(x, y);
15025
15026   return TRUE;
15027 }
15028
15029 static boolean DropElement(struct PlayerInfo *player)
15030 {
15031   int old_element, new_element;
15032   int dropx = player->jx, dropy = player->jy;
15033   int drop_direction = player->MovDir;
15034   int drop_side = drop_direction;
15035   int drop_element = get_next_dropped_element(player);
15036
15037   /* do not drop an element on top of another element; when holding drop key
15038      pressed without moving, dropped element must move away before the next
15039      element can be dropped (this is especially important if the next element
15040      is dynamite, which can be placed on background for historical reasons) */
15041   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15042     return MP_ACTION;
15043
15044   if (IS_THROWABLE(drop_element))
15045   {
15046     dropx += GET_DX_FROM_DIR(drop_direction);
15047     dropy += GET_DY_FROM_DIR(drop_direction);
15048
15049     if (!IN_LEV_FIELD(dropx, dropy))
15050       return FALSE;
15051   }
15052
15053   old_element = Tile[dropx][dropy];     // old element at dropping position
15054   new_element = drop_element;           // default: no change when dropping
15055
15056   // check if player is active, not moving and ready to drop
15057   if (!player->active || player->MovPos || player->drop_delay > 0)
15058     return FALSE;
15059
15060   // check if player has anything that can be dropped
15061   if (new_element == EL_UNDEFINED)
15062     return FALSE;
15063
15064   // only set if player has anything that can be dropped
15065   player->is_dropping_pressed = TRUE;
15066
15067   // check if drop key was pressed long enough for EM style dynamite
15068   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15069     return FALSE;
15070
15071   // check if anything can be dropped at the current position
15072   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15073     return FALSE;
15074
15075   // collected custom elements can only be dropped on empty fields
15076   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15077     return FALSE;
15078
15079   if (old_element != EL_EMPTY)
15080     Back[dropx][dropy] = old_element;   // store old element on this field
15081
15082   ResetGfxAnimation(dropx, dropy);
15083   ResetRandomAnimationValue(dropx, dropy);
15084
15085   if (player->inventory_size > 0 ||
15086       player->inventory_infinite_element != EL_UNDEFINED)
15087   {
15088     if (player->inventory_size > 0)
15089     {
15090       player->inventory_size--;
15091
15092       DrawGameDoorValues();
15093
15094       if (new_element == EL_DYNAMITE)
15095         new_element = EL_DYNAMITE_ACTIVE;
15096       else if (new_element == EL_EM_DYNAMITE)
15097         new_element = EL_EM_DYNAMITE_ACTIVE;
15098       else if (new_element == EL_SP_DISK_RED)
15099         new_element = EL_SP_DISK_RED_ACTIVE;
15100     }
15101
15102     Tile[dropx][dropy] = new_element;
15103
15104     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15105       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15106                           el2img(Tile[dropx][dropy]), 0);
15107
15108     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15109
15110     // needed if previous element just changed to "empty" in the last frame
15111     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15112
15113     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15114                                player->index_bit, drop_side);
15115     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15116                                         CE_PLAYER_DROPS_X,
15117                                         player->index_bit, drop_side);
15118
15119     TestIfElementTouchesCustomElement(dropx, dropy);
15120   }
15121   else          // player is dropping a dyna bomb
15122   {
15123     player->dynabombs_left--;
15124
15125     Tile[dropx][dropy] = new_element;
15126
15127     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15128       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15129                           el2img(Tile[dropx][dropy]), 0);
15130
15131     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15132   }
15133
15134   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15135     InitField_WithBug1(dropx, dropy, FALSE);
15136
15137   new_element = Tile[dropx][dropy];     // element might have changed
15138
15139   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15140       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15141   {
15142     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15143       MovDir[dropx][dropy] = drop_direction;
15144
15145     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15146
15147     // do not cause impact style collision by dropping elements that can fall
15148     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15149   }
15150
15151   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15152   player->is_dropping = TRUE;
15153
15154   player->drop_pressed_delay = 0;
15155   player->is_dropping_pressed = FALSE;
15156
15157   player->drop_x = dropx;
15158   player->drop_y = dropy;
15159
15160   return TRUE;
15161 }
15162
15163 // ----------------------------------------------------------------------------
15164 // game sound playing functions
15165 // ----------------------------------------------------------------------------
15166
15167 static int *loop_sound_frame = NULL;
15168 static int *loop_sound_volume = NULL;
15169
15170 void InitPlayLevelSound(void)
15171 {
15172   int num_sounds = getSoundListSize();
15173
15174   checked_free(loop_sound_frame);
15175   checked_free(loop_sound_volume);
15176
15177   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15178   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15179 }
15180
15181 static void PlayLevelSound(int x, int y, int nr)
15182 {
15183   int sx = SCREENX(x), sy = SCREENY(y);
15184   int volume, stereo_position;
15185   int max_distance = 8;
15186   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15187
15188   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15189       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15190     return;
15191
15192   if (!IN_LEV_FIELD(x, y) ||
15193       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15194       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15195     return;
15196
15197   volume = SOUND_MAX_VOLUME;
15198
15199   if (!IN_SCR_FIELD(sx, sy))
15200   {
15201     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15202     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15203
15204     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15205   }
15206
15207   stereo_position = (SOUND_MAX_LEFT +
15208                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15209                      (SCR_FIELDX + 2 * max_distance));
15210
15211   if (IS_LOOP_SOUND(nr))
15212   {
15213     /* This assures that quieter loop sounds do not overwrite louder ones,
15214        while restarting sound volume comparison with each new game frame. */
15215
15216     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15217       return;
15218
15219     loop_sound_volume[nr] = volume;
15220     loop_sound_frame[nr] = FrameCounter;
15221   }
15222
15223   PlaySoundExt(nr, volume, stereo_position, type);
15224 }
15225
15226 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15227 {
15228   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15229                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15230                  y < LEVELY(BY1) ? LEVELY(BY1) :
15231                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15232                  sound_action);
15233 }
15234
15235 static void PlayLevelSoundAction(int x, int y, int action)
15236 {
15237   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15238 }
15239
15240 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15241 {
15242   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15243
15244   if (sound_effect != SND_UNDEFINED)
15245     PlayLevelSound(x, y, sound_effect);
15246 }
15247
15248 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15249                                               int action)
15250 {
15251   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15252
15253   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15254     PlayLevelSound(x, y, sound_effect);
15255 }
15256
15257 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15258 {
15259   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15260
15261   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15262     PlayLevelSound(x, y, sound_effect);
15263 }
15264
15265 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15266 {
15267   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15268
15269   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15270     StopSound(sound_effect);
15271 }
15272
15273 static int getLevelMusicNr(void)
15274 {
15275   if (levelset.music[level_nr] != MUS_UNDEFINED)
15276     return levelset.music[level_nr];            // from config file
15277   else
15278     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15279 }
15280
15281 static void FadeLevelSounds(void)
15282 {
15283   FadeSounds();
15284 }
15285
15286 static void FadeLevelMusic(void)
15287 {
15288   int music_nr = getLevelMusicNr();
15289   char *curr_music = getCurrentlyPlayingMusicFilename();
15290   char *next_music = getMusicInfoEntryFilename(music_nr);
15291
15292   if (!strEqual(curr_music, next_music))
15293     FadeMusic();
15294 }
15295
15296 void FadeLevelSoundsAndMusic(void)
15297 {
15298   FadeLevelSounds();
15299   FadeLevelMusic();
15300 }
15301
15302 static void PlayLevelMusic(void)
15303 {
15304   int music_nr = getLevelMusicNr();
15305   char *curr_music = getCurrentlyPlayingMusicFilename();
15306   char *next_music = getMusicInfoEntryFilename(music_nr);
15307
15308   if (!strEqual(curr_music, next_music))
15309     PlayMusicLoop(music_nr);
15310 }
15311
15312 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15313 {
15314   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15315   int offset = 0;
15316   int x = xx - offset;
15317   int y = yy - offset;
15318
15319   switch (sample)
15320   {
15321     case SOUND_blank:
15322       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15323       break;
15324
15325     case SOUND_roll:
15326       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15327       break;
15328
15329     case SOUND_stone:
15330       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15331       break;
15332
15333     case SOUND_nut:
15334       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15335       break;
15336
15337     case SOUND_crack:
15338       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15339       break;
15340
15341     case SOUND_bug:
15342       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15343       break;
15344
15345     case SOUND_tank:
15346       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15347       break;
15348
15349     case SOUND_android_clone:
15350       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15351       break;
15352
15353     case SOUND_android_move:
15354       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15355       break;
15356
15357     case SOUND_spring:
15358       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15359       break;
15360
15361     case SOUND_slurp:
15362       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15363       break;
15364
15365     case SOUND_eater:
15366       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15367       break;
15368
15369     case SOUND_eater_eat:
15370       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15371       break;
15372
15373     case SOUND_alien:
15374       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15375       break;
15376
15377     case SOUND_collect:
15378       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15379       break;
15380
15381     case SOUND_diamond:
15382       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15383       break;
15384
15385     case SOUND_squash:
15386       // !!! CHECK THIS !!!
15387 #if 1
15388       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15389 #else
15390       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15391 #endif
15392       break;
15393
15394     case SOUND_wonderfall:
15395       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15396       break;
15397
15398     case SOUND_drip:
15399       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15400       break;
15401
15402     case SOUND_push:
15403       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15404       break;
15405
15406     case SOUND_dirt:
15407       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15408       break;
15409
15410     case SOUND_acid:
15411       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15412       break;
15413
15414     case SOUND_ball:
15415       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15416       break;
15417
15418     case SOUND_slide:
15419       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15420       break;
15421
15422     case SOUND_wonder:
15423       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15424       break;
15425
15426     case SOUND_door:
15427       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15428       break;
15429
15430     case SOUND_exit_open:
15431       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15432       break;
15433
15434     case SOUND_exit_leave:
15435       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15436       break;
15437
15438     case SOUND_dynamite:
15439       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15440       break;
15441
15442     case SOUND_tick:
15443       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15444       break;
15445
15446     case SOUND_press:
15447       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15448       break;
15449
15450     case SOUND_wheel:
15451       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15452       break;
15453
15454     case SOUND_boom:
15455       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15456       break;
15457
15458     case SOUND_die:
15459       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15460       break;
15461
15462     case SOUND_time:
15463       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15464       break;
15465
15466     default:
15467       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15468       break;
15469   }
15470 }
15471
15472 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15473 {
15474   int element = map_element_SP_to_RND(element_sp);
15475   int action = map_action_SP_to_RND(action_sp);
15476   int offset = (setup.sp_show_border_elements ? 0 : 1);
15477   int x = xx - offset;
15478   int y = yy - offset;
15479
15480   PlayLevelSoundElementAction(x, y, element, action);
15481 }
15482
15483 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15484 {
15485   int element = map_element_MM_to_RND(element_mm);
15486   int action = map_action_MM_to_RND(action_mm);
15487   int offset = 0;
15488   int x = xx - offset;
15489   int y = yy - offset;
15490
15491   if (!IS_MM_ELEMENT(element))
15492     element = EL_MM_DEFAULT;
15493
15494   PlayLevelSoundElementAction(x, y, element, action);
15495 }
15496
15497 void PlaySound_MM(int sound_mm)
15498 {
15499   int sound = map_sound_MM_to_RND(sound_mm);
15500
15501   if (sound == SND_UNDEFINED)
15502     return;
15503
15504   PlaySound(sound);
15505 }
15506
15507 void PlaySoundLoop_MM(int sound_mm)
15508 {
15509   int sound = map_sound_MM_to_RND(sound_mm);
15510
15511   if (sound == SND_UNDEFINED)
15512     return;
15513
15514   PlaySoundLoop(sound);
15515 }
15516
15517 void StopSound_MM(int sound_mm)
15518 {
15519   int sound = map_sound_MM_to_RND(sound_mm);
15520
15521   if (sound == SND_UNDEFINED)
15522     return;
15523
15524   StopSound(sound);
15525 }
15526
15527 void RaiseScore(int value)
15528 {
15529   game.score += value;
15530
15531   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15532
15533   DisplayGameControlValues();
15534 }
15535
15536 void RaiseScoreElement(int element)
15537 {
15538   switch (element)
15539   {
15540     case EL_EMERALD:
15541     case EL_BD_DIAMOND:
15542     case EL_EMERALD_YELLOW:
15543     case EL_EMERALD_RED:
15544     case EL_EMERALD_PURPLE:
15545     case EL_SP_INFOTRON:
15546       RaiseScore(level.score[SC_EMERALD]);
15547       break;
15548     case EL_DIAMOND:
15549       RaiseScore(level.score[SC_DIAMOND]);
15550       break;
15551     case EL_CRYSTAL:
15552       RaiseScore(level.score[SC_CRYSTAL]);
15553       break;
15554     case EL_PEARL:
15555       RaiseScore(level.score[SC_PEARL]);
15556       break;
15557     case EL_BUG:
15558     case EL_BD_BUTTERFLY:
15559     case EL_SP_ELECTRON:
15560       RaiseScore(level.score[SC_BUG]);
15561       break;
15562     case EL_SPACESHIP:
15563     case EL_BD_FIREFLY:
15564     case EL_SP_SNIKSNAK:
15565       RaiseScore(level.score[SC_SPACESHIP]);
15566       break;
15567     case EL_YAMYAM:
15568     case EL_DARK_YAMYAM:
15569       RaiseScore(level.score[SC_YAMYAM]);
15570       break;
15571     case EL_ROBOT:
15572       RaiseScore(level.score[SC_ROBOT]);
15573       break;
15574     case EL_PACMAN:
15575       RaiseScore(level.score[SC_PACMAN]);
15576       break;
15577     case EL_NUT:
15578       RaiseScore(level.score[SC_NUT]);
15579       break;
15580     case EL_DYNAMITE:
15581     case EL_EM_DYNAMITE:
15582     case EL_SP_DISK_RED:
15583     case EL_DYNABOMB_INCREASE_NUMBER:
15584     case EL_DYNABOMB_INCREASE_SIZE:
15585     case EL_DYNABOMB_INCREASE_POWER:
15586       RaiseScore(level.score[SC_DYNAMITE]);
15587       break;
15588     case EL_SHIELD_NORMAL:
15589     case EL_SHIELD_DEADLY:
15590       RaiseScore(level.score[SC_SHIELD]);
15591       break;
15592     case EL_EXTRA_TIME:
15593       RaiseScore(level.extra_time_score);
15594       break;
15595     case EL_KEY_1:
15596     case EL_KEY_2:
15597     case EL_KEY_3:
15598     case EL_KEY_4:
15599     case EL_EM_KEY_1:
15600     case EL_EM_KEY_2:
15601     case EL_EM_KEY_3:
15602     case EL_EM_KEY_4:
15603     case EL_EMC_KEY_5:
15604     case EL_EMC_KEY_6:
15605     case EL_EMC_KEY_7:
15606     case EL_EMC_KEY_8:
15607     case EL_DC_KEY_WHITE:
15608       RaiseScore(level.score[SC_KEY]);
15609       break;
15610     default:
15611       RaiseScore(element_info[element].collect_score);
15612       break;
15613   }
15614 }
15615
15616 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15617 {
15618   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15619   {
15620     if (!quick_quit)
15621     {
15622       // prevent short reactivation of overlay buttons while closing door
15623       SetOverlayActive(FALSE);
15624
15625       // door may still be open due to skipped or envelope style request
15626       CloseDoor(DOOR_CLOSE_1);
15627     }
15628
15629     if (network.enabled)
15630       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15631     else
15632     {
15633       if (quick_quit)
15634         FadeSkipNextFadeIn();
15635
15636       SetGameStatus(GAME_MODE_MAIN);
15637
15638       DrawMainMenu();
15639     }
15640   }
15641   else          // continue playing the game
15642   {
15643     if (tape.playing && tape.deactivate_display)
15644       TapeDeactivateDisplayOff(TRUE);
15645
15646     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15647
15648     if (tape.playing && tape.deactivate_display)
15649       TapeDeactivateDisplayOn();
15650   }
15651 }
15652
15653 void RequestQuitGame(boolean escape_key_pressed)
15654 {
15655   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15656   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15657                         level_editor_test_game);
15658   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15659                           quick_quit);
15660
15661   RequestQuitGameExt(skip_request, quick_quit,
15662                      "Do you really want to quit the game?");
15663 }
15664
15665 void RequestRestartGame(char *message)
15666 {
15667   game.restart_game_message = NULL;
15668
15669   boolean has_started_game = hasStartedNetworkGame();
15670   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15671
15672   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15673   {
15674     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15675   }
15676   else
15677   {
15678     // needed in case of envelope request to close game panel
15679     CloseDoor(DOOR_CLOSE_1);
15680
15681     SetGameStatus(GAME_MODE_MAIN);
15682
15683     DrawMainMenu();
15684   }
15685 }
15686
15687 void CheckGameOver(void)
15688 {
15689   static boolean last_game_over = FALSE;
15690   static int game_over_delay = 0;
15691   int game_over_delay_value = 50;
15692   boolean game_over = checkGameFailed();
15693
15694   // do not handle game over if request dialog is already active
15695   if (game.request_active)
15696     return;
15697
15698   // do not ask to play again if game was never actually played
15699   if (!game.GamePlayed)
15700     return;
15701
15702   if (!game_over)
15703   {
15704     last_game_over = FALSE;
15705     game_over_delay = game_over_delay_value;
15706
15707     return;
15708   }
15709
15710   if (game_over_delay > 0)
15711   {
15712     game_over_delay--;
15713
15714     return;
15715   }
15716
15717   if (last_game_over != game_over)
15718     game.restart_game_message = (hasStartedNetworkGame() ?
15719                                  "Game over! Play it again?" :
15720                                  "Game over!");
15721
15722   last_game_over = game_over;
15723 }
15724
15725 boolean checkGameSolved(void)
15726 {
15727   // set for all game engines if level was solved
15728   return game.LevelSolved_GameEnd;
15729 }
15730
15731 boolean checkGameFailed(void)
15732 {
15733   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15734     return (game_em.game_over && !game_em.level_solved);
15735   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15736     return (game_sp.game_over && !game_sp.level_solved);
15737   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15738     return (game_mm.game_over && !game_mm.level_solved);
15739   else                          // GAME_ENGINE_TYPE_RND
15740     return (game.GameOver && !game.LevelSolved);
15741 }
15742
15743 boolean checkGameEnded(void)
15744 {
15745   return (checkGameSolved() || checkGameFailed());
15746 }
15747
15748
15749 // ----------------------------------------------------------------------------
15750 // random generator functions
15751 // ----------------------------------------------------------------------------
15752
15753 unsigned int InitEngineRandom_RND(int seed)
15754 {
15755   game.num_random_calls = 0;
15756
15757   return InitEngineRandom(seed);
15758 }
15759
15760 unsigned int RND(int max)
15761 {
15762   if (max > 0)
15763   {
15764     game.num_random_calls++;
15765
15766     return GetEngineRandom(max);
15767   }
15768
15769   return 0;
15770 }
15771
15772
15773 // ----------------------------------------------------------------------------
15774 // game engine snapshot handling functions
15775 // ----------------------------------------------------------------------------
15776
15777 struct EngineSnapshotInfo
15778 {
15779   // runtime values for custom element collect score
15780   int collect_score[NUM_CUSTOM_ELEMENTS];
15781
15782   // runtime values for group element choice position
15783   int choice_pos[NUM_GROUP_ELEMENTS];
15784
15785   // runtime values for belt position animations
15786   int belt_graphic[4][NUM_BELT_PARTS];
15787   int belt_anim_mode[4][NUM_BELT_PARTS];
15788 };
15789
15790 static struct EngineSnapshotInfo engine_snapshot_rnd;
15791 static char *snapshot_level_identifier = NULL;
15792 static int snapshot_level_nr = -1;
15793
15794 static void SaveEngineSnapshotValues_RND(void)
15795 {
15796   static int belt_base_active_element[4] =
15797   {
15798     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15799     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15800     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15801     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15802   };
15803   int i, j;
15804
15805   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15806   {
15807     int element = EL_CUSTOM_START + i;
15808
15809     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15810   }
15811
15812   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15813   {
15814     int element = EL_GROUP_START + i;
15815
15816     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15817   }
15818
15819   for (i = 0; i < 4; i++)
15820   {
15821     for (j = 0; j < NUM_BELT_PARTS; j++)
15822     {
15823       int element = belt_base_active_element[i] + j;
15824       int graphic = el2img(element);
15825       int anim_mode = graphic_info[graphic].anim_mode;
15826
15827       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15828       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15829     }
15830   }
15831 }
15832
15833 static void LoadEngineSnapshotValues_RND(void)
15834 {
15835   unsigned int num_random_calls = game.num_random_calls;
15836   int i, j;
15837
15838   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15839   {
15840     int element = EL_CUSTOM_START + i;
15841
15842     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15843   }
15844
15845   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15846   {
15847     int element = EL_GROUP_START + i;
15848
15849     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15850   }
15851
15852   for (i = 0; i < 4; i++)
15853   {
15854     for (j = 0; j < NUM_BELT_PARTS; j++)
15855     {
15856       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15857       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15858
15859       graphic_info[graphic].anim_mode = anim_mode;
15860     }
15861   }
15862
15863   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15864   {
15865     InitRND(tape.random_seed);
15866     for (i = 0; i < num_random_calls; i++)
15867       RND(1);
15868   }
15869
15870   if (game.num_random_calls != num_random_calls)
15871   {
15872     Error("number of random calls out of sync");
15873     Error("number of random calls should be %d", num_random_calls);
15874     Error("number of random calls is %d", game.num_random_calls);
15875
15876     Fail("this should not happen -- please debug");
15877   }
15878 }
15879
15880 void FreeEngineSnapshotSingle(void)
15881 {
15882   FreeSnapshotSingle();
15883
15884   setString(&snapshot_level_identifier, NULL);
15885   snapshot_level_nr = -1;
15886 }
15887
15888 void FreeEngineSnapshotList(void)
15889 {
15890   FreeSnapshotList();
15891 }
15892
15893 static ListNode *SaveEngineSnapshotBuffers(void)
15894 {
15895   ListNode *buffers = NULL;
15896
15897   // copy some special values to a structure better suited for the snapshot
15898
15899   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15900     SaveEngineSnapshotValues_RND();
15901   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15902     SaveEngineSnapshotValues_EM();
15903   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15904     SaveEngineSnapshotValues_SP(&buffers);
15905   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15906     SaveEngineSnapshotValues_MM(&buffers);
15907
15908   // save values stored in special snapshot structure
15909
15910   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15911     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15912   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15913     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15914   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15915     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15916   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15917     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15918
15919   // save further RND engine values
15920
15921   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15922   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15923   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15924
15925   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15926   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15927   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15928   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15929   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15930
15931   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15932   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15933   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15934
15935   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15936
15937   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15938   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15939
15940   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15941   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15942   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15944   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15945   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15946   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15948   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15949   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15950   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15951   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15952   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15953   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15956   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15958
15959   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15960   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15961
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15964   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15965
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15968
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15970   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15971   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15974
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15976   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15977
15978 #if 0
15979   ListNode *node = engine_snapshot_list_rnd;
15980   int num_bytes = 0;
15981
15982   while (node != NULL)
15983   {
15984     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15985
15986     node = node->next;
15987   }
15988
15989   Debug("game:playing:SaveEngineSnapshotBuffers",
15990         "size of engine snapshot: %d bytes", num_bytes);
15991 #endif
15992
15993   return buffers;
15994 }
15995
15996 void SaveEngineSnapshotSingle(void)
15997 {
15998   ListNode *buffers = SaveEngineSnapshotBuffers();
15999
16000   // finally save all snapshot buffers to single snapshot
16001   SaveSnapshotSingle(buffers);
16002
16003   // save level identification information
16004   setString(&snapshot_level_identifier, leveldir_current->identifier);
16005   snapshot_level_nr = level_nr;
16006 }
16007
16008 boolean CheckSaveEngineSnapshotToList(void)
16009 {
16010   boolean save_snapshot =
16011     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16012      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16013       game.snapshot.changed_action) ||
16014      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16015       game.snapshot.collected_item));
16016
16017   game.snapshot.changed_action = FALSE;
16018   game.snapshot.collected_item = FALSE;
16019   game.snapshot.save_snapshot = save_snapshot;
16020
16021   return save_snapshot;
16022 }
16023
16024 void SaveEngineSnapshotToList(void)
16025 {
16026   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16027       tape.quick_resume)
16028     return;
16029
16030   ListNode *buffers = SaveEngineSnapshotBuffers();
16031
16032   // finally save all snapshot buffers to snapshot list
16033   SaveSnapshotToList(buffers);
16034 }
16035
16036 void SaveEngineSnapshotToListInitial(void)
16037 {
16038   FreeEngineSnapshotList();
16039
16040   SaveEngineSnapshotToList();
16041 }
16042
16043 static void LoadEngineSnapshotValues(void)
16044 {
16045   // restore special values from snapshot structure
16046
16047   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16048     LoadEngineSnapshotValues_RND();
16049   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16050     LoadEngineSnapshotValues_EM();
16051   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16052     LoadEngineSnapshotValues_SP();
16053   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16054     LoadEngineSnapshotValues_MM();
16055 }
16056
16057 void LoadEngineSnapshotSingle(void)
16058 {
16059   LoadSnapshotSingle();
16060
16061   LoadEngineSnapshotValues();
16062 }
16063
16064 static void LoadEngineSnapshot_Undo(int steps)
16065 {
16066   LoadSnapshotFromList_Older(steps);
16067
16068   LoadEngineSnapshotValues();
16069 }
16070
16071 static void LoadEngineSnapshot_Redo(int steps)
16072 {
16073   LoadSnapshotFromList_Newer(steps);
16074
16075   LoadEngineSnapshotValues();
16076 }
16077
16078 boolean CheckEngineSnapshotSingle(void)
16079 {
16080   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16081           snapshot_level_nr == level_nr);
16082 }
16083
16084 boolean CheckEngineSnapshotList(void)
16085 {
16086   return CheckSnapshotList();
16087 }
16088
16089
16090 // ---------- new game button stuff -------------------------------------------
16091
16092 static struct
16093 {
16094   int graphic;
16095   struct XY *pos;
16096   int gadget_id;
16097   boolean *setup_value;
16098   boolean allowed_on_tape;
16099   boolean is_touch_button;
16100   char *infotext;
16101 } gamebutton_info[NUM_GAME_BUTTONS] =
16102 {
16103   {
16104     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16105     GAME_CTRL_ID_STOP,                          NULL,
16106     TRUE, FALSE,                                "stop game"
16107   },
16108   {
16109     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16110     GAME_CTRL_ID_PAUSE,                         NULL,
16111     TRUE, FALSE,                                "pause game"
16112   },
16113   {
16114     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16115     GAME_CTRL_ID_PLAY,                          NULL,
16116     TRUE, FALSE,                                "play game"
16117   },
16118   {
16119     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16120     GAME_CTRL_ID_UNDO,                          NULL,
16121     TRUE, FALSE,                                "undo step"
16122   },
16123   {
16124     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16125     GAME_CTRL_ID_REDO,                          NULL,
16126     TRUE, FALSE,                                "redo step"
16127   },
16128   {
16129     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16130     GAME_CTRL_ID_SAVE,                          NULL,
16131     TRUE, FALSE,                                "save game"
16132   },
16133   {
16134     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16135     GAME_CTRL_ID_PAUSE2,                        NULL,
16136     TRUE, FALSE,                                "pause game"
16137   },
16138   {
16139     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16140     GAME_CTRL_ID_LOAD,                          NULL,
16141     TRUE, FALSE,                                "load game"
16142   },
16143   {
16144     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16145     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16146     FALSE, FALSE,                               "stop game"
16147   },
16148   {
16149     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16150     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16151     FALSE, FALSE,                               "pause game"
16152   },
16153   {
16154     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16155     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16156     FALSE, FALSE,                               "play game"
16157   },
16158   {
16159     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16160     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16161     FALSE, TRUE,                                "stop game"
16162   },
16163   {
16164     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16165     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16166     FALSE, TRUE,                                "pause game"
16167   },
16168   {
16169     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16170     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16171     TRUE, FALSE,                                "background music on/off"
16172   },
16173   {
16174     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16175     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16176     TRUE, FALSE,                                "sound loops on/off"
16177   },
16178   {
16179     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16180     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16181     TRUE, FALSE,                                "normal sounds on/off"
16182   },
16183   {
16184     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16185     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16186     FALSE, FALSE,                               "background music on/off"
16187   },
16188   {
16189     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16190     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16191     FALSE, FALSE,                               "sound loops on/off"
16192   },
16193   {
16194     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16195     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16196     FALSE, FALSE,                               "normal sounds on/off"
16197   }
16198 };
16199
16200 void CreateGameButtons(void)
16201 {
16202   int i;
16203
16204   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16205   {
16206     int graphic = gamebutton_info[i].graphic;
16207     struct GraphicInfo *gfx = &graphic_info[graphic];
16208     struct XY *pos = gamebutton_info[i].pos;
16209     struct GadgetInfo *gi;
16210     int button_type;
16211     boolean checked;
16212     unsigned int event_mask;
16213     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16214     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16215     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16216     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16217     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16218     int gd_x   = gfx->src_x;
16219     int gd_y   = gfx->src_y;
16220     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16221     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16222     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16223     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16224     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16225     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16226     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16227     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16228     int id = i;
16229
16230     if (gfx->bitmap == NULL)
16231     {
16232       game_gadget[id] = NULL;
16233
16234       continue;
16235     }
16236
16237     if (id == GAME_CTRL_ID_STOP ||
16238         id == GAME_CTRL_ID_PANEL_STOP ||
16239         id == GAME_CTRL_ID_TOUCH_STOP ||
16240         id == GAME_CTRL_ID_PLAY ||
16241         id == GAME_CTRL_ID_PANEL_PLAY ||
16242         id == GAME_CTRL_ID_SAVE ||
16243         id == GAME_CTRL_ID_LOAD)
16244     {
16245       button_type = GD_TYPE_NORMAL_BUTTON;
16246       checked = FALSE;
16247       event_mask = GD_EVENT_RELEASED;
16248     }
16249     else if (id == GAME_CTRL_ID_UNDO ||
16250              id == GAME_CTRL_ID_REDO)
16251     {
16252       button_type = GD_TYPE_NORMAL_BUTTON;
16253       checked = FALSE;
16254       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16255     }
16256     else
16257     {
16258       button_type = GD_TYPE_CHECK_BUTTON;
16259       checked = (gamebutton_info[i].setup_value != NULL ?
16260                  *gamebutton_info[i].setup_value : FALSE);
16261       event_mask = GD_EVENT_PRESSED;
16262     }
16263
16264     gi = CreateGadget(GDI_CUSTOM_ID, id,
16265                       GDI_IMAGE_ID, graphic,
16266                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16267                       GDI_X, base_x + x,
16268                       GDI_Y, base_y + y,
16269                       GDI_WIDTH, gfx->width,
16270                       GDI_HEIGHT, gfx->height,
16271                       GDI_TYPE, button_type,
16272                       GDI_STATE, GD_BUTTON_UNPRESSED,
16273                       GDI_CHECKED, checked,
16274                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16275                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16276                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16277                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16278                       GDI_DIRECT_DRAW, FALSE,
16279                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16280                       GDI_EVENT_MASK, event_mask,
16281                       GDI_CALLBACK_ACTION, HandleGameButtons,
16282                       GDI_END);
16283
16284     if (gi == NULL)
16285       Fail("cannot create gadget");
16286
16287     game_gadget[id] = gi;
16288   }
16289 }
16290
16291 void FreeGameButtons(void)
16292 {
16293   int i;
16294
16295   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16296     FreeGadget(game_gadget[i]);
16297 }
16298
16299 static void UnmapGameButtonsAtSamePosition(int id)
16300 {
16301   int i;
16302
16303   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16304     if (i != id &&
16305         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16306         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16307       UnmapGadget(game_gadget[i]);
16308 }
16309
16310 static void UnmapGameButtonsAtSamePosition_All(void)
16311 {
16312   if (setup.show_load_save_buttons)
16313   {
16314     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16315     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16316     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16317   }
16318   else if (setup.show_undo_redo_buttons)
16319   {
16320     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16321     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16322     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16323   }
16324   else
16325   {
16326     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16327     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16328     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16329
16330     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16331     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16332     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16333   }
16334 }
16335
16336 void MapLoadSaveButtons(void)
16337 {
16338   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16339   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16340
16341   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16342   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16343 }
16344
16345 void MapUndoRedoButtons(void)
16346 {
16347   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16348   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16349
16350   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16351   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16352 }
16353
16354 void ModifyPauseButtons(void)
16355 {
16356   static int ids[] =
16357   {
16358     GAME_CTRL_ID_PAUSE,
16359     GAME_CTRL_ID_PAUSE2,
16360     GAME_CTRL_ID_PANEL_PAUSE,
16361     GAME_CTRL_ID_TOUCH_PAUSE,
16362     -1
16363   };
16364   int i;
16365
16366   for (i = 0; ids[i] > -1; i++)
16367     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16368 }
16369
16370 static void MapGameButtonsExt(boolean on_tape)
16371 {
16372   int i;
16373
16374   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16375     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16376       MapGadget(game_gadget[i]);
16377
16378   UnmapGameButtonsAtSamePosition_All();
16379
16380   RedrawGameButtons();
16381 }
16382
16383 static void UnmapGameButtonsExt(boolean on_tape)
16384 {
16385   int i;
16386
16387   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16388     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16389       UnmapGadget(game_gadget[i]);
16390 }
16391
16392 static void RedrawGameButtonsExt(boolean on_tape)
16393 {
16394   int i;
16395
16396   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16397     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16398       RedrawGadget(game_gadget[i]);
16399 }
16400
16401 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16402 {
16403   if (gi == NULL)
16404     return;
16405
16406   gi->checked = state;
16407 }
16408
16409 static void RedrawSoundButtonGadget(int id)
16410 {
16411   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16412              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16413              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16414              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16415              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16416              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16417              id);
16418
16419   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16420   RedrawGadget(game_gadget[id2]);
16421 }
16422
16423 void MapGameButtons(void)
16424 {
16425   MapGameButtonsExt(FALSE);
16426 }
16427
16428 void UnmapGameButtons(void)
16429 {
16430   UnmapGameButtonsExt(FALSE);
16431 }
16432
16433 void RedrawGameButtons(void)
16434 {
16435   RedrawGameButtonsExt(FALSE);
16436 }
16437
16438 void MapGameButtonsOnTape(void)
16439 {
16440   MapGameButtonsExt(TRUE);
16441 }
16442
16443 void UnmapGameButtonsOnTape(void)
16444 {
16445   UnmapGameButtonsExt(TRUE);
16446 }
16447
16448 void RedrawGameButtonsOnTape(void)
16449 {
16450   RedrawGameButtonsExt(TRUE);
16451 }
16452
16453 static void GameUndoRedoExt(void)
16454 {
16455   ClearPlayerAction();
16456
16457   tape.pausing = TRUE;
16458
16459   RedrawPlayfield();
16460   UpdateAndDisplayGameControlValues();
16461
16462   DrawCompleteVideoDisplay();
16463   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16464   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16465   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16466
16467   ModifyPauseButtons();
16468
16469   BackToFront();
16470 }
16471
16472 static void GameUndo(int steps)
16473 {
16474   if (!CheckEngineSnapshotList())
16475     return;
16476
16477   int tape_property_bits = tape.property_bits;
16478
16479   LoadEngineSnapshot_Undo(steps);
16480
16481   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16482
16483   GameUndoRedoExt();
16484 }
16485
16486 static void GameRedo(int steps)
16487 {
16488   if (!CheckEngineSnapshotList())
16489     return;
16490
16491   int tape_property_bits = tape.property_bits;
16492
16493   LoadEngineSnapshot_Redo(steps);
16494
16495   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16496
16497   GameUndoRedoExt();
16498 }
16499
16500 static void HandleGameButtonsExt(int id, int button)
16501 {
16502   static boolean game_undo_executed = FALSE;
16503   int steps = BUTTON_STEPSIZE(button);
16504   boolean handle_game_buttons =
16505     (game_status == GAME_MODE_PLAYING ||
16506      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16507
16508   if (!handle_game_buttons)
16509     return;
16510
16511   switch (id)
16512   {
16513     case GAME_CTRL_ID_STOP:
16514     case GAME_CTRL_ID_PANEL_STOP:
16515     case GAME_CTRL_ID_TOUCH_STOP:
16516       if (game_status == GAME_MODE_MAIN)
16517         break;
16518
16519       if (tape.playing)
16520         TapeStop();
16521       else
16522         RequestQuitGame(FALSE);
16523
16524       break;
16525
16526     case GAME_CTRL_ID_PAUSE:
16527     case GAME_CTRL_ID_PAUSE2:
16528     case GAME_CTRL_ID_PANEL_PAUSE:
16529     case GAME_CTRL_ID_TOUCH_PAUSE:
16530       if (network.enabled && game_status == GAME_MODE_PLAYING)
16531       {
16532         if (tape.pausing)
16533           SendToServer_ContinuePlaying();
16534         else
16535           SendToServer_PausePlaying();
16536       }
16537       else
16538         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16539
16540       game_undo_executed = FALSE;
16541
16542       break;
16543
16544     case GAME_CTRL_ID_PLAY:
16545     case GAME_CTRL_ID_PANEL_PLAY:
16546       if (game_status == GAME_MODE_MAIN)
16547       {
16548         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16549       }
16550       else if (tape.pausing)
16551       {
16552         if (network.enabled)
16553           SendToServer_ContinuePlaying();
16554         else
16555           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16556       }
16557       break;
16558
16559     case GAME_CTRL_ID_UNDO:
16560       // Important: When using "save snapshot when collecting an item" mode,
16561       // load last (current) snapshot for first "undo" after pressing "pause"
16562       // (else the last-but-one snapshot would be loaded, because the snapshot
16563       // pointer already points to the last snapshot when pressing "pause",
16564       // which is fine for "every step/move" mode, but not for "every collect")
16565       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16566           !game_undo_executed)
16567         steps--;
16568
16569       game_undo_executed = TRUE;
16570
16571       GameUndo(steps);
16572       break;
16573
16574     case GAME_CTRL_ID_REDO:
16575       GameRedo(steps);
16576       break;
16577
16578     case GAME_CTRL_ID_SAVE:
16579       TapeQuickSave();
16580       break;
16581
16582     case GAME_CTRL_ID_LOAD:
16583       TapeQuickLoad();
16584       break;
16585
16586     case SOUND_CTRL_ID_MUSIC:
16587     case SOUND_CTRL_ID_PANEL_MUSIC:
16588       if (setup.sound_music)
16589       { 
16590         setup.sound_music = FALSE;
16591
16592         FadeMusic();
16593       }
16594       else if (audio.music_available)
16595       { 
16596         setup.sound = setup.sound_music = TRUE;
16597
16598         SetAudioMode(setup.sound);
16599
16600         if (game_status == GAME_MODE_PLAYING)
16601           PlayLevelMusic();
16602       }
16603
16604       RedrawSoundButtonGadget(id);
16605
16606       break;
16607
16608     case SOUND_CTRL_ID_LOOPS:
16609     case SOUND_CTRL_ID_PANEL_LOOPS:
16610       if (setup.sound_loops)
16611         setup.sound_loops = FALSE;
16612       else if (audio.loops_available)
16613       {
16614         setup.sound = setup.sound_loops = TRUE;
16615
16616         SetAudioMode(setup.sound);
16617       }
16618
16619       RedrawSoundButtonGadget(id);
16620
16621       break;
16622
16623     case SOUND_CTRL_ID_SIMPLE:
16624     case SOUND_CTRL_ID_PANEL_SIMPLE:
16625       if (setup.sound_simple)
16626         setup.sound_simple = FALSE;
16627       else if (audio.sound_available)
16628       {
16629         setup.sound = setup.sound_simple = TRUE;
16630
16631         SetAudioMode(setup.sound);
16632       }
16633
16634       RedrawSoundButtonGadget(id);
16635
16636       break;
16637
16638     default:
16639       break;
16640   }
16641 }
16642
16643 static void HandleGameButtons(struct GadgetInfo *gi)
16644 {
16645   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16646 }
16647
16648 void HandleSoundButtonKeys(Key key)
16649 {
16650   if (key == setup.shortcut.sound_simple)
16651     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16652   else if (key == setup.shortcut.sound_loops)
16653     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16654   else if (key == setup.shortcut.sound_music)
16655     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16656 }