fixed skipping historic score entries with empty tape basename
[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 TestIfPlayerTouchesCustomElement(int, int);
1062 static void TestIfElementTouchesCustomElement(int, int);
1063 static void TestIfElementHitsCustomElement(int, int, int);
1064
1065 static void HandleElementChange(int, int, int);
1066 static void ExecuteCustomElementAction(int, int, int, int);
1067 static boolean ChangeElement(int, int, int, int);
1068
1069 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1070 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1072 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1073         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1074 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1075         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1076 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1077         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1078 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1079         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1080
1081 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1082 #define CheckElementChange(x, y, e, te, ev)                             \
1083         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1084 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1085         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1086 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1087         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1088 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1089         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1090
1091 static void PlayLevelSound(int, int, int);
1092 static void PlayLevelSoundNearest(int, int, int);
1093 static void PlayLevelSoundAction(int, int, int);
1094 static void PlayLevelSoundElementAction(int, int, int, int);
1095 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1096 static void PlayLevelSoundActionIfLoop(int, int, int);
1097 static void StopLevelSoundActionIfLoop(int, int, int);
1098 static void PlayLevelMusic(void);
1099 static void FadeLevelSoundsAndMusic(void);
1100
1101 static void HandleGameButtons(struct GadgetInfo *);
1102
1103 int AmoebaNeighbourNr(int, int);
1104 void AmoebaToDiamond(int, int);
1105 void ContinueMoving(int, int);
1106 void Bang(int, int);
1107 void InitMovDir(int, int);
1108 void InitAmoebaNr(int, int);
1109 void NewHighScore(int);
1110
1111 void TestIfGoodThingHitsBadThing(int, int, int);
1112 void TestIfBadThingHitsGoodThing(int, int, int);
1113 void TestIfPlayerTouchesBadThing(int, int);
1114 void TestIfPlayerRunsIntoBadThing(int, int, int);
1115 void TestIfBadThingTouchesPlayer(int, int);
1116 void TestIfBadThingRunsIntoPlayer(int, int, int);
1117 void TestIfFriendTouchesBadThing(int, int);
1118 void TestIfBadThingTouchesFriend(int, int);
1119 void TestIfBadThingTouchesOtherBadThing(int, int);
1120 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1121
1122 void KillPlayer(struct PlayerInfo *);
1123 void BuryPlayer(struct PlayerInfo *);
1124 void RemovePlayer(struct PlayerInfo *);
1125 void ExitPlayer(struct PlayerInfo *);
1126
1127 static int getInvisibleActiveFromInvisibleElement(int);
1128 static int getInvisibleFromInvisibleActiveElement(int);
1129
1130 static void TestFieldAfterSnapping(int, int, int, int, int);
1131
1132 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1133
1134 // for detection of endless loops, caused by custom element programming
1135 // (using maximal playfield width x 10 is just a rough approximation)
1136 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1137
1138 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1139 {                                                                       \
1140   if (recursion_loop_detected)                                          \
1141     return (rc);                                                        \
1142                                                                         \
1143   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1144   {                                                                     \
1145     recursion_loop_detected = TRUE;                                     \
1146     recursion_loop_element = (e);                                       \
1147   }                                                                     \
1148                                                                         \
1149   recursion_loop_depth++;                                               \
1150 }
1151
1152 #define RECURSION_LOOP_DETECTION_END()                                  \
1153 {                                                                       \
1154   recursion_loop_depth--;                                               \
1155 }
1156
1157 static int recursion_loop_depth;
1158 static boolean recursion_loop_detected;
1159 static boolean recursion_loop_element;
1160
1161 static int map_player_action[MAX_PLAYERS];
1162
1163
1164 // ----------------------------------------------------------------------------
1165 // definition of elements that automatically change to other elements after
1166 // a specified time, eventually calling a function when changing
1167 // ----------------------------------------------------------------------------
1168
1169 // forward declaration for changer functions
1170 static void InitBuggyBase(int, int);
1171 static void WarnBuggyBase(int, int);
1172
1173 static void InitTrap(int, int);
1174 static void ActivateTrap(int, int);
1175 static void ChangeActiveTrap(int, int);
1176
1177 static void InitRobotWheel(int, int);
1178 static void RunRobotWheel(int, int);
1179 static void StopRobotWheel(int, int);
1180
1181 static void InitTimegateWheel(int, int);
1182 static void RunTimegateWheel(int, int);
1183
1184 static void InitMagicBallDelay(int, int);
1185 static void ActivateMagicBall(int, int);
1186
1187 struct ChangingElementInfo
1188 {
1189   int element;
1190   int target_element;
1191   int change_delay;
1192   void (*pre_change_function)(int x, int y);
1193   void (*change_function)(int x, int y);
1194   void (*post_change_function)(int x, int y);
1195 };
1196
1197 static struct ChangingElementInfo change_delay_list[] =
1198 {
1199   {
1200     EL_NUT_BREAKING,
1201     EL_EMERALD,
1202     6,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_PEARL_BREAKING,
1209     EL_EMPTY,
1210     8,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_OPENING,
1217     EL_EXIT_OPEN,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_EXIT_CLOSING,
1225     EL_EXIT_CLOSED,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_OPENING,
1233     EL_STEEL_EXIT_OPEN,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_STEEL_EXIT_CLOSING,
1241     EL_STEEL_EXIT_CLOSED,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_OPENING,
1249     EL_EM_EXIT_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_EXIT_CLOSING,
1257     EL_EMPTY,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_OPENING,
1265     EL_EM_STEEL_EXIT_OPEN,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_STEEL_EXIT_CLOSING,
1273     EL_STEELWALL,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_OPENING,
1281     EL_SP_EXIT_OPEN,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_EXIT_CLOSING,
1289     EL_SP_EXIT_CLOSED,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_OPENING,
1297     EL_SWITCHGATE_OPEN,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SWITCHGATE_CLOSING,
1305     EL_SWITCHGATE_CLOSED,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_OPENING,
1313     EL_TIMEGATE_OPEN,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_TIMEGATE_CLOSING,
1321     EL_TIMEGATE_CLOSED,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327
1328   {
1329     EL_ACID_SPLASH_LEFT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_ACID_SPLASH_RIGHT,
1338     EL_EMPTY,
1339     8,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE,
1346     EL_SP_BUGGY_BASE_ACTIVATING,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVATING,
1354     EL_SP_BUGGY_BASE_ACTIVE,
1355     0,
1356     InitBuggyBase,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_SP_BUGGY_BASE_ACTIVE,
1362     EL_SP_BUGGY_BASE,
1363     0,
1364     InitBuggyBase,
1365     WarnBuggyBase,
1366     NULL
1367   },
1368   {
1369     EL_TRAP,
1370     EL_TRAP_ACTIVE,
1371     0,
1372     InitTrap,
1373     NULL,
1374     ActivateTrap
1375   },
1376   {
1377     EL_TRAP_ACTIVE,
1378     EL_TRAP,
1379     31,
1380     NULL,
1381     ChangeActiveTrap,
1382     NULL
1383   },
1384   {
1385     EL_ROBOT_WHEEL_ACTIVE,
1386     EL_ROBOT_WHEEL,
1387     0,
1388     InitRobotWheel,
1389     RunRobotWheel,
1390     StopRobotWheel
1391   },
1392   {
1393     EL_TIMEGATE_SWITCH_ACTIVE,
1394     EL_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1402     EL_DC_TIMEGATE_SWITCH,
1403     0,
1404     InitTimegateWheel,
1405     RunTimegateWheel,
1406     NULL
1407   },
1408   {
1409     EL_EMC_MAGIC_BALL_ACTIVE,
1410     EL_EMC_MAGIC_BALL_ACTIVE,
1411     0,
1412     InitMagicBallDelay,
1413     NULL,
1414     ActivateMagicBall
1415   },
1416   {
1417     EL_EMC_SPRING_BUMPER_ACTIVE,
1418     EL_EMC_SPRING_BUMPER,
1419     8,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_SHRINKING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL
1431   },
1432   {
1433     EL_DIAGONAL_GROWING,
1434     EL_UNDEFINED,
1435     0,
1436     NULL,
1437     NULL,
1438     NULL,
1439   },
1440
1441   {
1442     EL_UNDEFINED,
1443     EL_UNDEFINED,
1444     -1,
1445     NULL,
1446     NULL,
1447     NULL
1448   }
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int push_delay_fixed, push_delay_random;
1455 }
1456 push_delay_list[] =
1457 {
1458   { EL_SPRING,                  0, 0 },
1459   { EL_BALLOON,                 0, 0 },
1460
1461   { EL_SOKOBAN_OBJECT,          2, 0 },
1462   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1463   { EL_SATELLITE,               2, 0 },
1464   { EL_SP_DISK_YELLOW,          2, 0 },
1465
1466   { EL_UNDEFINED,               0, 0 },
1467 };
1468
1469 struct
1470 {
1471   int element;
1472   int move_stepsize;
1473 }
1474 move_stepsize_list[] =
1475 {
1476   { EL_AMOEBA_DROP,             2 },
1477   { EL_AMOEBA_DROPPING,         2 },
1478   { EL_QUICKSAND_FILLING,       1 },
1479   { EL_QUICKSAND_EMPTYING,      1 },
1480   { EL_QUICKSAND_FAST_FILLING,  2 },
1481   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1482   { EL_MAGIC_WALL_FILLING,      2 },
1483   { EL_MAGIC_WALL_EMPTYING,     2 },
1484   { EL_BD_MAGIC_WALL_FILLING,   2 },
1485   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1486   { EL_DC_MAGIC_WALL_FILLING,   2 },
1487   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1488
1489   { EL_UNDEFINED,               0 },
1490 };
1491
1492 struct
1493 {
1494   int element;
1495   int count;
1496 }
1497 collect_count_list[] =
1498 {
1499   { EL_EMERALD,                 1 },
1500   { EL_BD_DIAMOND,              1 },
1501   { EL_EMERALD_YELLOW,          1 },
1502   { EL_EMERALD_RED,             1 },
1503   { EL_EMERALD_PURPLE,          1 },
1504   { EL_DIAMOND,                 3 },
1505   { EL_SP_INFOTRON,             1 },
1506   { EL_PEARL,                   5 },
1507   { EL_CRYSTAL,                 8 },
1508
1509   { EL_UNDEFINED,               0 },
1510 };
1511
1512 struct
1513 {
1514   int element;
1515   int direction;
1516 }
1517 access_direction_list[] =
1518 {
1519   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1521   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1522   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1525   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1526   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1527   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1528   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1529   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1530
1531   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1532   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1533   { EL_SP_PORT_UP,                                                   MV_DOWN },
1534   { EL_SP_PORT_DOWN,                                         MV_UP           },
1535   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1536   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1537   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1538   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1539   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1540   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1542   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1543   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1544   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1545   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1546   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1547   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1548   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1549   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1550
1551   { EL_UNDEFINED,                       MV_NONE                              }
1552 };
1553
1554 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1555
1556 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1557 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1558 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1559                                  IS_JUST_CHANGING(x, y))
1560
1561 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1562
1563 // static variables for playfield scan mode (scanning forward or backward)
1564 static int playfield_scan_start_x = 0;
1565 static int playfield_scan_start_y = 0;
1566 static int playfield_scan_delta_x = 1;
1567 static int playfield_scan_delta_y = 1;
1568
1569 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1570                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1571                                      (y) += playfield_scan_delta_y)     \
1572                                 for ((x) = playfield_scan_start_x;      \
1573                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1574                                      (x) += playfield_scan_delta_x)
1575
1576 #ifdef DEBUG
1577 void DEBUG_SetMaximumDynamite(void)
1578 {
1579   int i;
1580
1581   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1582     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1583       local_player->inventory_element[local_player->inventory_size++] =
1584         EL_DYNAMITE;
1585 }
1586 #endif
1587
1588 static void InitPlayfieldScanModeVars(void)
1589 {
1590   if (game.use_reverse_scan_direction)
1591   {
1592     playfield_scan_start_x = lev_fieldx - 1;
1593     playfield_scan_start_y = lev_fieldy - 1;
1594
1595     playfield_scan_delta_x = -1;
1596     playfield_scan_delta_y = -1;
1597   }
1598   else
1599   {
1600     playfield_scan_start_x = 0;
1601     playfield_scan_start_y = 0;
1602
1603     playfield_scan_delta_x = 1;
1604     playfield_scan_delta_y = 1;
1605   }
1606 }
1607
1608 static void InitPlayfieldScanMode(int mode)
1609 {
1610   game.use_reverse_scan_direction =
1611     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1612
1613   InitPlayfieldScanModeVars();
1614 }
1615
1616 static int get_move_delay_from_stepsize(int move_stepsize)
1617 {
1618   move_stepsize =
1619     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1620
1621   // make sure that stepsize value is always a power of 2
1622   move_stepsize = (1 << log_2(move_stepsize));
1623
1624   return TILEX / move_stepsize;
1625 }
1626
1627 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1628                                boolean init_game)
1629 {
1630   int player_nr = player->index_nr;
1631   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1632   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1633
1634   // do no immediately change move delay -- the player might just be moving
1635   player->move_delay_value_next = move_delay;
1636
1637   // information if player can move must be set separately
1638   player->cannot_move = cannot_move;
1639
1640   if (init_game)
1641   {
1642     player->move_delay       = game.initial_move_delay[player_nr];
1643     player->move_delay_value = game.initial_move_delay_value[player_nr];
1644
1645     player->move_delay_value_next = -1;
1646
1647     player->move_delay_reset_counter = 0;
1648   }
1649 }
1650
1651 void GetPlayerConfig(void)
1652 {
1653   GameFrameDelay = setup.game_frame_delay;
1654
1655   if (!audio.sound_available)
1656     setup.sound_simple = FALSE;
1657
1658   if (!audio.loops_available)
1659     setup.sound_loops = FALSE;
1660
1661   if (!audio.music_available)
1662     setup.sound_music = FALSE;
1663
1664   if (!video.fullscreen_available)
1665     setup.fullscreen = FALSE;
1666
1667   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1668
1669   SetAudioMode(setup.sound);
1670 }
1671
1672 int GetElementFromGroupElement(int element)
1673 {
1674   if (IS_GROUP_ELEMENT(element))
1675   {
1676     struct ElementGroupInfo *group = element_info[element].group;
1677     int last_anim_random_frame = gfx.anim_random_frame;
1678     int element_pos;
1679
1680     if (group->choice_mode == ANIM_RANDOM)
1681       gfx.anim_random_frame = RND(group->num_elements_resolved);
1682
1683     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1684                                     group->choice_mode, 0,
1685                                     group->choice_pos);
1686
1687     if (group->choice_mode == ANIM_RANDOM)
1688       gfx.anim_random_frame = last_anim_random_frame;
1689
1690     group->choice_pos++;
1691
1692     element = group->element_resolved[element_pos];
1693   }
1694
1695   return element;
1696 }
1697
1698 static void IncrementSokobanFieldsNeeded(void)
1699 {
1700   if (level.sb_fields_needed)
1701     game.sokoban_fields_still_needed++;
1702 }
1703
1704 static void IncrementSokobanObjectsNeeded(void)
1705 {
1706   if (level.sb_objects_needed)
1707     game.sokoban_objects_still_needed++;
1708 }
1709
1710 static void DecrementSokobanFieldsNeeded(void)
1711 {
1712   if (game.sokoban_fields_still_needed > 0)
1713     game.sokoban_fields_still_needed--;
1714 }
1715
1716 static void DecrementSokobanObjectsNeeded(void)
1717 {
1718   if (game.sokoban_objects_still_needed > 0)
1719     game.sokoban_objects_still_needed--;
1720 }
1721
1722 static void InitPlayerField(int x, int y, int element, boolean init_game)
1723 {
1724   if (element == EL_SP_MURPHY)
1725   {
1726     if (init_game)
1727     {
1728       if (stored_player[0].present)
1729       {
1730         Tile[x][y] = EL_SP_MURPHY_CLONE;
1731
1732         return;
1733       }
1734       else
1735       {
1736         stored_player[0].initial_element = element;
1737         stored_player[0].use_murphy = TRUE;
1738
1739         if (!level.use_artwork_element[0])
1740           stored_player[0].artwork_element = EL_SP_MURPHY;
1741       }
1742
1743       Tile[x][y] = EL_PLAYER_1;
1744     }
1745   }
1746
1747   if (init_game)
1748   {
1749     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1750     int jx = player->jx, jy = player->jy;
1751
1752     player->present = TRUE;
1753
1754     player->block_last_field = (element == EL_SP_MURPHY ?
1755                                 level.sp_block_last_field :
1756                                 level.block_last_field);
1757
1758     // ---------- initialize player's last field block delay ------------------
1759
1760     // always start with reliable default value (no adjustment needed)
1761     player->block_delay_adjustment = 0;
1762
1763     // special case 1: in Supaplex, Murphy blocks last field one more frame
1764     if (player->block_last_field && element == EL_SP_MURPHY)
1765       player->block_delay_adjustment = 1;
1766
1767     // special case 2: in game engines before 3.1.1, blocking was different
1768     if (game.use_block_last_field_bug)
1769       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1770
1771     if (!network.enabled || player->connected_network)
1772     {
1773       player->active = TRUE;
1774
1775       // remove potentially duplicate players
1776       if (StorePlayer[jx][jy] == Tile[x][y])
1777         StorePlayer[jx][jy] = 0;
1778
1779       StorePlayer[x][y] = Tile[x][y];
1780
1781 #if DEBUG_INIT_PLAYER
1782       Debug("game:init:player", "- player element %d activated",
1783             player->element_nr);
1784       Debug("game:init:player", "  (local player is %d and currently %s)",
1785             local_player->element_nr,
1786             local_player->active ? "active" : "not active");
1787     }
1788 #endif
1789
1790     Tile[x][y] = EL_EMPTY;
1791
1792     player->jx = player->last_jx = x;
1793     player->jy = player->last_jy = y;
1794   }
1795
1796   // always check if player was just killed and should be reanimated
1797   {
1798     int player_nr = GET_PLAYER_NR(element);
1799     struct PlayerInfo *player = &stored_player[player_nr];
1800
1801     if (player->active && player->killed)
1802       player->reanimated = TRUE; // if player was just killed, reanimate him
1803   }
1804 }
1805
1806 static void InitField(int x, int y, boolean init_game)
1807 {
1808   int element = Tile[x][y];
1809
1810   switch (element)
1811   {
1812     case EL_SP_MURPHY:
1813     case EL_PLAYER_1:
1814     case EL_PLAYER_2:
1815     case EL_PLAYER_3:
1816     case EL_PLAYER_4:
1817       InitPlayerField(x, y, element, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_PLAYER:
1821       element = Tile[x][y] = EL_PLAYER_1;
1822       InitField(x, y, init_game);
1823
1824       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1825       InitField(x, y, init_game);
1826       break;
1827
1828     case EL_SOKOBAN_FIELD_EMPTY:
1829       IncrementSokobanFieldsNeeded();
1830       break;
1831
1832     case EL_SOKOBAN_OBJECT:
1833       IncrementSokobanObjectsNeeded();
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1838         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1840         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1844         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888     case EL_SPRING_LEFT:
1889     case EL_SPRING_RIGHT:
1890       InitMovDir(x, y);
1891       break;
1892
1893     case EL_AMOEBA_FULL:
1894     case EL_BD_AMOEBA:
1895       InitAmoebaNr(x, y);
1896       break;
1897
1898     case EL_AMOEBA_DROP:
1899       if (y == lev_fieldy - 1)
1900       {
1901         Tile[x][y] = EL_AMOEBA_GROWING;
1902         Store[x][y] = EL_AMOEBA_WET;
1903       }
1904       break;
1905
1906     case EL_DYNAMITE_ACTIVE:
1907     case EL_SP_DISK_RED_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1910     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1912       MovDelay[x][y] = 96;
1913       break;
1914
1915     case EL_EM_DYNAMITE_ACTIVE:
1916       MovDelay[x][y] = 32;
1917       break;
1918
1919     case EL_LAMP:
1920       game.lights_still_needed++;
1921       break;
1922
1923     case EL_PENGUIN:
1924       game.friends_still_needed++;
1925       break;
1926
1927     case EL_PIG:
1928     case EL_DRAGON:
1929       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930       break;
1931
1932     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1933     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1934     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1935     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944       if (init_game)
1945       {
1946         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1947         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1948         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1949
1950         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1951         {
1952           game.belt_dir[belt_nr] = belt_dir;
1953           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1954         }
1955         else    // more than one switch -- set it like the first switch
1956         {
1957           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1958         }
1959       }
1960       break;
1961
1962     case EL_LIGHT_SWITCH_ACTIVE:
1963       if (init_game)
1964         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1965       break;
1966
1967     case EL_INVISIBLE_STEELWALL:
1968     case EL_INVISIBLE_WALL:
1969     case EL_INVISIBLE_SAND:
1970       if (game.light_time_left > 0 ||
1971           game.lenses_time_left > 0)
1972         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1973       break;
1974
1975     case EL_EMC_MAGIC_BALL:
1976       if (game.ball_active)
1977         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1978       break;
1979
1980     case EL_EMC_MAGIC_BALL_SWITCH:
1981       if (game.ball_active)
1982         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1983       break;
1984
1985     case EL_TRIGGER_PLAYER:
1986     case EL_TRIGGER_ELEMENT:
1987     case EL_TRIGGER_CE_VALUE:
1988     case EL_TRIGGER_CE_SCORE:
1989     case EL_SELF:
1990     case EL_ANY_ELEMENT:
1991     case EL_CURRENT_CE_VALUE:
1992     case EL_CURRENT_CE_SCORE:
1993     case EL_PREV_CE_1:
1994     case EL_PREV_CE_2:
1995     case EL_PREV_CE_3:
1996     case EL_PREV_CE_4:
1997     case EL_PREV_CE_5:
1998     case EL_PREV_CE_6:
1999     case EL_PREV_CE_7:
2000     case EL_PREV_CE_8:
2001     case EL_NEXT_CE_1:
2002     case EL_NEXT_CE_2:
2003     case EL_NEXT_CE_3:
2004     case EL_NEXT_CE_4:
2005     case EL_NEXT_CE_5:
2006     case EL_NEXT_CE_6:
2007     case EL_NEXT_CE_7:
2008     case EL_NEXT_CE_8:
2009       // reference elements should not be used on the playfield
2010       Tile[x][y] = EL_EMPTY;
2011       break;
2012
2013     default:
2014       if (IS_CUSTOM_ELEMENT(element))
2015       {
2016         if (CAN_MOVE(element))
2017           InitMovDir(x, y);
2018
2019         if (!element_info[element].use_last_ce_value || init_game)
2020           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2021       }
2022       else if (IS_GROUP_ELEMENT(element))
2023       {
2024         Tile[x][y] = GetElementFromGroupElement(element);
2025
2026         InitField(x, y, init_game);
2027       }
2028
2029       break;
2030   }
2031
2032   if (!init_game)
2033     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2034 }
2035
2036 static void InitField_WithBug1(int x, int y, boolean init_game)
2037 {
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(Tile[x][y]))
2043     InitMovDir(x, y);
2044 }
2045
2046 static void InitField_WithBug2(int x, int y, boolean init_game)
2047 {
2048   int old_element = Tile[x][y];
2049
2050   InitField(x, y, init_game);
2051
2052   // not needed to call InitMovDir() -- already done by InitField()!
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(old_element) &&
2055       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2056     InitMovDir(x, y);
2057
2058   /* this case is in fact a combination of not less than three bugs:
2059      first, it calls InitMovDir() for elements that can move, although this is
2060      already done by InitField(); then, it checks the element that was at this
2061      field _before_ the call to InitField() (which can change it); lastly, it
2062      was not called for "mole with direction" elements, which were treated as
2063      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2064   */
2065 }
2066
2067 static int get_key_element_from_nr(int key_nr)
2068 {
2069   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2070                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2071                           EL_EM_KEY_1 : EL_KEY_1);
2072
2073   return key_base_element + key_nr;
2074 }
2075
2076 static int get_next_dropped_element(struct PlayerInfo *player)
2077 {
2078   return (player->inventory_size > 0 ?
2079           player->inventory_element[player->inventory_size - 1] :
2080           player->inventory_infinite_element != EL_UNDEFINED ?
2081           player->inventory_infinite_element :
2082           player->dynabombs_left > 0 ?
2083           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2084           EL_UNDEFINED);
2085 }
2086
2087 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2088 {
2089   // pos >= 0: get element from bottom of the stack;
2090   // pos <  0: get element from top of the stack
2091
2092   if (pos < 0)
2093   {
2094     int min_inventory_size = -pos;
2095     int inventory_pos = player->inventory_size - min_inventory_size;
2096     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2097
2098     return (player->inventory_size >= min_inventory_size ?
2099             player->inventory_element[inventory_pos] :
2100             player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             EL_UNDEFINED);
2105   }
2106   else
2107   {
2108     int min_dynabombs_left = pos + 1;
2109     int min_inventory_size = pos + 1 - player->dynabombs_left;
2110     int inventory_pos = pos - player->dynabombs_left;
2111
2112     return (player->inventory_infinite_element != EL_UNDEFINED ?
2113             player->inventory_infinite_element :
2114             player->dynabombs_left >= min_dynabombs_left ?
2115             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2116             player->inventory_size >= min_inventory_size ?
2117             player->inventory_element[inventory_pos] :
2118             EL_UNDEFINED);
2119   }
2120 }
2121
2122 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2123 {
2124   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2125   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2126   int compare_result;
2127
2128   if (gpo1->sort_priority != gpo2->sort_priority)
2129     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2130   else
2131     compare_result = gpo1->nr - gpo2->nr;
2132
2133   return compare_result;
2134 }
2135
2136 int getPlayerInventorySize(int player_nr)
2137 {
2138   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2139     return game_em.ply[player_nr]->dynamite;
2140   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2141     return game_sp.red_disk_count;
2142   else
2143     return stored_player[player_nr].inventory_size;
2144 }
2145
2146 static void InitGameControlValues(void)
2147 {
2148   int i;
2149
2150   for (i = 0; game_panel_controls[i].nr != -1; i++)
2151   {
2152     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2153     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2154     struct TextPosInfo *pos = gpc->pos;
2155     int nr = gpc->nr;
2156     int type = gpc->type;
2157
2158     if (nr != i)
2159     {
2160       Error("'game_panel_controls' structure corrupted at %d", i);
2161
2162       Fail("this should not happen -- please debug");
2163     }
2164
2165     // force update of game controls after initialization
2166     gpc->value = gpc->last_value = -1;
2167     gpc->frame = gpc->last_frame = -1;
2168     gpc->gfx_frame = -1;
2169
2170     // determine panel value width for later calculation of alignment
2171     if (type == TYPE_INTEGER || type == TYPE_STRING)
2172     {
2173       pos->width = pos->size * getFontWidth(pos->font);
2174       pos->height = getFontHeight(pos->font);
2175     }
2176     else if (type == TYPE_ELEMENT)
2177     {
2178       pos->width = pos->size;
2179       pos->height = pos->size;
2180     }
2181
2182     // fill structure for game panel draw order
2183     gpo->nr = gpc->nr;
2184     gpo->sort_priority = pos->sort_priority;
2185   }
2186
2187   // sort game panel controls according to sort_priority and control number
2188   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2189         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2190 }
2191
2192 static void UpdatePlayfieldElementCount(void)
2193 {
2194   boolean use_element_count = FALSE;
2195   int i, j, x, y;
2196
2197   // first check if it is needed at all to calculate playfield element count
2198   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2199     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2200       use_element_count = TRUE;
2201
2202   if (!use_element_count)
2203     return;
2204
2205   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2206     element_info[i].element_count = 0;
2207
2208   SCAN_PLAYFIELD(x, y)
2209   {
2210     element_info[Tile[x][y]].element_count++;
2211   }
2212
2213   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2214     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2215       if (IS_IN_GROUP(j, i))
2216         element_info[EL_GROUP_START + i].element_count +=
2217           element_info[j].element_count;
2218 }
2219
2220 static void UpdateGameControlValues(void)
2221 {
2222   int i, k;
2223   int time = (game.LevelSolved ?
2224               game.LevelSolved_CountingTime :
2225               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226               game_em.lev->time :
2227               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2228               game_sp.time_played :
2229               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2230               game_mm.energy_left :
2231               game.no_time_limit ? TimePlayed : TimeLeft);
2232   int score = (game.LevelSolved ?
2233                game.LevelSolved_CountingScore :
2234                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2235                game_em.lev->score :
2236                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2237                game_sp.score :
2238                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2239                game_mm.score :
2240                game.score);
2241   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2242               game_em.lev->gems_needed :
2243               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2244               game_sp.infotrons_still_needed :
2245               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2246               game_mm.kettles_still_needed :
2247               game.gems_still_needed);
2248   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2249                      game_em.lev->gems_needed > 0 :
2250                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2251                      game_sp.infotrons_still_needed > 0 :
2252                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2253                      game_mm.kettles_still_needed > 0 ||
2254                      game_mm.lights_still_needed > 0 :
2255                      game.gems_still_needed > 0 ||
2256                      game.sokoban_fields_still_needed > 0 ||
2257                      game.sokoban_objects_still_needed > 0 ||
2258                      game.lights_still_needed > 0);
2259   int health = (game.LevelSolved ?
2260                 game.LevelSolved_CountingHealth :
2261                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2262                 MM_HEALTH(game_mm.laser_overload_value) :
2263                 game.health);
2264   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2265
2266   UpdatePlayfieldElementCount();
2267
2268   // update game panel control values
2269
2270   // used instead of "level_nr" (for network games)
2271   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2272   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2273
2274   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2275   for (i = 0; i < MAX_NUM_KEYS; i++)
2276     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2277   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2278   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2279
2280   if (game.centered_player_nr == -1)
2281   {
2282     for (i = 0; i < MAX_PLAYERS; i++)
2283     {
2284       // only one player in Supaplex game engine
2285       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2286         break;
2287
2288       for (k = 0; k < MAX_NUM_KEYS; k++)
2289       {
2290         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291         {
2292           if (game_em.ply[i]->keys & (1 << k))
2293             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294               get_key_element_from_nr(k);
2295         }
2296         else if (stored_player[i].key[k])
2297           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298             get_key_element_from_nr(k);
2299       }
2300
2301       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302         getPlayerInventorySize(i);
2303
2304       if (stored_player[i].num_white_keys > 0)
2305         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2306           EL_DC_KEY_WHITE;
2307
2308       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2309         stored_player[i].num_white_keys;
2310     }
2311   }
2312   else
2313   {
2314     int player_nr = game.centered_player_nr;
2315
2316     for (k = 0; k < MAX_NUM_KEYS; k++)
2317     {
2318       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2319       {
2320         if (game_em.ply[player_nr]->keys & (1 << k))
2321           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2322             get_key_element_from_nr(k);
2323       }
2324       else if (stored_player[player_nr].key[k])
2325         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2326           get_key_element_from_nr(k);
2327     }
2328
2329     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330       getPlayerInventorySize(player_nr);
2331
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338
2339   // re-arrange keys on game panel, if needed or if defined by style settings
2340   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2341   {
2342     int nr = GAME_PANEL_KEY_1 + i;
2343     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2344     struct TextPosInfo *pos = gpc->pos;
2345
2346     // skip check if key is not in the player's inventory
2347     if (gpc->value == EL_EMPTY)
2348       continue;
2349
2350     // check if keys should be arranged on panel from left to right
2351     if (pos->style == STYLE_LEFTMOST_POSITION)
2352     {
2353       // check previous key positions (left from current key)
2354       for (k = 0; k < i; k++)
2355       {
2356         int nr_new = GAME_PANEL_KEY_1 + k;
2357
2358         if (game_panel_controls[nr_new].value == EL_EMPTY)
2359         {
2360           game_panel_controls[nr_new].value = gpc->value;
2361           gpc->value = EL_EMPTY;
2362
2363           break;
2364         }
2365       }
2366     }
2367
2368     // check if "undefined" keys can be placed at some other position
2369     if (pos->x == -1 && pos->y == -1)
2370     {
2371       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2372
2373       // 1st try: display key at the same position as normal or EM keys
2374       if (game_panel_controls[nr_new].value == EL_EMPTY)
2375       {
2376         game_panel_controls[nr_new].value = gpc->value;
2377       }
2378       else
2379       {
2380         // 2nd try: display key at the next free position in the key panel
2381         for (k = 0; k < STD_NUM_KEYS; k++)
2382         {
2383           nr_new = GAME_PANEL_KEY_1 + k;
2384
2385           if (game_panel_controls[nr_new].value == EL_EMPTY)
2386           {
2387             game_panel_controls[nr_new].value = gpc->value;
2388
2389             break;
2390           }
2391         }
2392       }
2393     }
2394   }
2395
2396   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2397   {
2398     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2399       get_inventory_element_from_pos(local_player, i);
2400     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2401       get_inventory_element_from_pos(local_player, -i - 1);
2402   }
2403
2404   game_panel_controls[GAME_PANEL_SCORE].value = score;
2405   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2406
2407   game_panel_controls[GAME_PANEL_TIME].value = time;
2408
2409   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2410   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2411   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2412
2413   if (level.time == 0)
2414     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2415   else
2416     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2417
2418   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2419   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2420
2421   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2422
2423   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2424     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2425      EL_EMPTY);
2426   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2427     local_player->shield_normal_time_left;
2428   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2429     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2430      EL_EMPTY);
2431   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2432     local_player->shield_deadly_time_left;
2433
2434   game_panel_controls[GAME_PANEL_EXIT].value =
2435     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2436
2437   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2438     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2439   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2440     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2441      EL_EMC_MAGIC_BALL_SWITCH);
2442
2443   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2444     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2445   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2446     game.light_time_left;
2447
2448   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2449     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2450   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2451     game.timegate_time_left;
2452
2453   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2454     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2455
2456   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2457     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2458   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2459     game.lenses_time_left;
2460
2461   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2462     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2463   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2464     game.magnify_time_left;
2465
2466   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2467     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2468      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2469      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2470      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2471      EL_BALLOON_SWITCH_NONE);
2472
2473   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2474     local_player->dynabomb_count;
2475   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2476     local_player->dynabomb_size;
2477   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2478     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2479
2480   game_panel_controls[GAME_PANEL_PENGUINS].value =
2481     game.friends_still_needed;
2482
2483   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2484     game.sokoban_objects_still_needed;
2485   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2486     game.sokoban_fields_still_needed;
2487
2488   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2489     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2490
2491   for (i = 0; i < NUM_BELTS; i++)
2492   {
2493     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2494       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2495        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2496     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2497       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2498   }
2499
2500   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2501     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2502   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2503     game.magic_wall_time_left;
2504
2505   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2506     local_player->gravity;
2507
2508   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2509     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2510
2511   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2512     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2513       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2514        game.panel.element[i].id : EL_UNDEFINED);
2515
2516   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2517     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2518       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2519        element_info[game.panel.element_count[i].id].element_count : 0);
2520
2521   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2522     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2523       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2524        element_info[game.panel.ce_score[i].id].collect_score : 0);
2525
2526   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2527     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2528       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2529        element_info[game.panel.ce_score_element[i].id].collect_score :
2530        EL_UNDEFINED);
2531
2532   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2533   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2534   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2535
2536   // update game panel control frames
2537
2538   for (i = 0; game_panel_controls[i].nr != -1; i++)
2539   {
2540     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2541
2542     if (gpc->type == TYPE_ELEMENT)
2543     {
2544       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2545       {
2546         int last_anim_random_frame = gfx.anim_random_frame;
2547         int element = gpc->value;
2548         int graphic = el2panelimg(element);
2549         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2550                                sync_random_frame : INIT_GFX_RANDOM());
2551
2552         if (gpc->value != gpc->last_value)
2553         {
2554           gpc->gfx_frame = 0;
2555           gpc->gfx_random = init_gfx_random;
2556         }
2557         else
2558         {
2559           gpc->gfx_frame++;
2560
2561           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2562               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2563             gpc->gfx_random = init_gfx_random;
2564         }
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = gpc->gfx_random;
2568
2569         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2570           gpc->gfx_frame = element_info[element].collect_score;
2571
2572         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2573
2574         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2575           gfx.anim_random_frame = last_anim_random_frame;
2576       }
2577     }
2578     else if (gpc->type == TYPE_GRAPHIC)
2579     {
2580       if (gpc->graphic != IMG_UNDEFINED)
2581       {
2582         int last_anim_random_frame = gfx.anim_random_frame;
2583         int graphic = gpc->graphic;
2584         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2585                                sync_random_frame : INIT_GFX_RANDOM());
2586
2587         if (gpc->value != gpc->last_value)
2588         {
2589           gpc->gfx_frame = 0;
2590           gpc->gfx_random = init_gfx_random;
2591         }
2592         else
2593         {
2594           gpc->gfx_frame++;
2595
2596           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2597               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2598             gpc->gfx_random = init_gfx_random;
2599         }
2600
2601         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2602           gfx.anim_random_frame = gpc->gfx_random;
2603
2604         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2605
2606         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2607           gfx.anim_random_frame = last_anim_random_frame;
2608       }
2609     }
2610   }
2611 }
2612
2613 static void DisplayGameControlValues(void)
2614 {
2615   boolean redraw_panel = FALSE;
2616   int i;
2617
2618   for (i = 0; game_panel_controls[i].nr != -1; i++)
2619   {
2620     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2621
2622     if (PANEL_DEACTIVATED(gpc->pos))
2623       continue;
2624
2625     if (gpc->value == gpc->last_value &&
2626         gpc->frame == gpc->last_frame)
2627       continue;
2628
2629     redraw_panel = TRUE;
2630   }
2631
2632   if (!redraw_panel)
2633     return;
2634
2635   // copy default game door content to main double buffer
2636
2637   // !!! CHECK AGAIN !!!
2638   SetPanelBackground();
2639   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2640   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2641
2642   // redraw game control buttons
2643   RedrawGameButtons();
2644
2645   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2646
2647   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2648   {
2649     int nr = game_panel_order[i].nr;
2650     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2651     struct TextPosInfo *pos = gpc->pos;
2652     int type = gpc->type;
2653     int value = gpc->value;
2654     int frame = gpc->frame;
2655     int size = pos->size;
2656     int font = pos->font;
2657     boolean draw_masked = pos->draw_masked;
2658     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2659
2660     if (PANEL_DEACTIVATED(pos))
2661       continue;
2662
2663     if (pos->class == get_hash_from_key("extra_panel_items") &&
2664         !setup.prefer_extra_panel_items)
2665       continue;
2666
2667     gpc->last_value = value;
2668     gpc->last_frame = frame;
2669
2670     if (type == TYPE_INTEGER)
2671     {
2672       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2673           nr == GAME_PANEL_TIME)
2674       {
2675         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2676
2677         if (use_dynamic_size)           // use dynamic number of digits
2678         {
2679           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2680           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2681           int size2 = size1 + 1;
2682           int font1 = pos->font;
2683           int font2 = pos->font_alt;
2684
2685           size = (value < value_change ? size1 : size2);
2686           font = (value < value_change ? font1 : font2);
2687         }
2688       }
2689
2690       // correct text size if "digits" is zero or less
2691       if (size <= 0)
2692         size = strlen(int2str(value, size));
2693
2694       // dynamically correct text alignment
2695       pos->width = size * getFontWidth(font);
2696
2697       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2698                   int2str(value, size), font, mask_mode);
2699     }
2700     else if (type == TYPE_ELEMENT)
2701     {
2702       int element, graphic;
2703       Bitmap *src_bitmap;
2704       int src_x, src_y;
2705       int width, height;
2706       int dst_x = PANEL_XPOS(pos);
2707       int dst_y = PANEL_YPOS(pos);
2708
2709       if (value != EL_UNDEFINED && value != EL_EMPTY)
2710       {
2711         element = value;
2712         graphic = el2panelimg(value);
2713
2714 #if 0
2715         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2716               element, EL_NAME(element), size);
2717 #endif
2718
2719         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2720           size = TILESIZE;
2721
2722         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2723                               &src_x, &src_y);
2724
2725         width  = graphic_info[graphic].width  * size / TILESIZE;
2726         height = graphic_info[graphic].height * size / TILESIZE;
2727
2728         if (draw_masked)
2729           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2730                            dst_x, dst_y);
2731         else
2732           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2733                      dst_x, dst_y);
2734       }
2735     }
2736     else if (type == TYPE_GRAPHIC)
2737     {
2738       int graphic        = gpc->graphic;
2739       int graphic_active = gpc->graphic_active;
2740       Bitmap *src_bitmap;
2741       int src_x, src_y;
2742       int width, height;
2743       int dst_x = PANEL_XPOS(pos);
2744       int dst_y = PANEL_YPOS(pos);
2745       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2746                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2747
2748       if (graphic != IMG_UNDEFINED && !skip)
2749       {
2750         if (pos->style == STYLE_REVERSE)
2751           value = 100 - value;
2752
2753         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2754
2755         if (pos->direction & MV_HORIZONTAL)
2756         {
2757           width  = graphic_info[graphic_active].width * value / 100;
2758           height = graphic_info[graphic_active].height;
2759
2760           if (pos->direction == MV_LEFT)
2761           {
2762             src_x += graphic_info[graphic_active].width - width;
2763             dst_x += graphic_info[graphic_active].width - width;
2764           }
2765         }
2766         else
2767         {
2768           width  = graphic_info[graphic_active].width;
2769           height = graphic_info[graphic_active].height * value / 100;
2770
2771           if (pos->direction == MV_UP)
2772           {
2773             src_y += graphic_info[graphic_active].height - height;
2774             dst_y += graphic_info[graphic_active].height - height;
2775           }
2776         }
2777
2778         if (draw_masked)
2779           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2780                            dst_x, dst_y);
2781         else
2782           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2783                      dst_x, dst_y);
2784
2785         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2786
2787         if (pos->direction & MV_HORIZONTAL)
2788         {
2789           if (pos->direction == MV_RIGHT)
2790           {
2791             src_x += width;
2792             dst_x += width;
2793           }
2794           else
2795           {
2796             dst_x = PANEL_XPOS(pos);
2797           }
2798
2799           width = graphic_info[graphic].width - width;
2800         }
2801         else
2802         {
2803           if (pos->direction == MV_DOWN)
2804           {
2805             src_y += height;
2806             dst_y += height;
2807           }
2808           else
2809           {
2810             dst_y = PANEL_YPOS(pos);
2811           }
2812
2813           height = graphic_info[graphic].height - height;
2814         }
2815
2816         if (draw_masked)
2817           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2818                            dst_x, dst_y);
2819         else
2820           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2821                      dst_x, dst_y);
2822       }
2823     }
2824     else if (type == TYPE_STRING)
2825     {
2826       boolean active = (value != 0);
2827       char *state_normal = "off";
2828       char *state_active = "on";
2829       char *state = (active ? state_active : state_normal);
2830       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2831                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2832                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2833                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2834
2835       if (nr == GAME_PANEL_GRAVITY_STATE)
2836       {
2837         int font1 = pos->font;          // (used for normal state)
2838         int font2 = pos->font_alt;      // (used for active state)
2839
2840         font = (active ? font2 : font1);
2841       }
2842
2843       if (s != NULL)
2844       {
2845         char *s_cut;
2846
2847         if (size <= 0)
2848         {
2849           // don't truncate output if "chars" is zero or less
2850           size = strlen(s);
2851
2852           // dynamically correct text alignment
2853           pos->width = size * getFontWidth(font);
2854         }
2855
2856         s_cut = getStringCopyN(s, size);
2857
2858         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2859                     s_cut, font, mask_mode);
2860
2861         free(s_cut);
2862       }
2863     }
2864
2865     redraw_mask |= REDRAW_DOOR_1;
2866   }
2867
2868   SetGameStatus(GAME_MODE_PLAYING);
2869 }
2870
2871 void UpdateAndDisplayGameControlValues(void)
2872 {
2873   if (tape.deactivate_display)
2874     return;
2875
2876   UpdateGameControlValues();
2877   DisplayGameControlValues();
2878 }
2879
2880 void UpdateGameDoorValues(void)
2881 {
2882   UpdateGameControlValues();
2883 }
2884
2885 void DrawGameDoorValues(void)
2886 {
2887   DisplayGameControlValues();
2888 }
2889
2890
2891 // ============================================================================
2892 // InitGameEngine()
2893 // ----------------------------------------------------------------------------
2894 // initialize game engine due to level / tape version number
2895 // ============================================================================
2896
2897 static void InitGameEngine(void)
2898 {
2899   int i, j, k, l, x, y;
2900
2901   // set game engine from tape file when re-playing, else from level file
2902   game.engine_version = (tape.playing ? tape.engine_version :
2903                          level.game_version);
2904
2905   // set single or multi-player game mode (needed for re-playing tapes)
2906   game.team_mode = setup.team_mode;
2907
2908   if (tape.playing)
2909   {
2910     int num_players = 0;
2911
2912     for (i = 0; i < MAX_PLAYERS; i++)
2913       if (tape.player_participates[i])
2914         num_players++;
2915
2916     // multi-player tapes contain input data for more than one player
2917     game.team_mode = (num_players > 1);
2918   }
2919
2920 #if 0
2921   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2922         level.game_version);
2923   Debug("game:init:level", "          tape.file_version   == %06d",
2924         tape.file_version);
2925   Debug("game:init:level", "          tape.game_version   == %06d",
2926         tape.game_version);
2927   Debug("game:init:level", "          tape.engine_version == %06d",
2928         tape.engine_version);
2929   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2930         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2931 #endif
2932
2933   // --------------------------------------------------------------------------
2934   // set flags for bugs and changes according to active game engine version
2935   // --------------------------------------------------------------------------
2936
2937   /*
2938     Summary of bugfix:
2939     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2940
2941     Bug was introduced in version:
2942     2.0.1
2943
2944     Bug was fixed in version:
2945     4.2.0.0
2946
2947     Description:
2948     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2949     but the property "can fall" was missing, which caused some levels to be
2950     unsolvable. This was fixed in version 4.2.0.0.
2951
2952     Affected levels/tapes:
2953     An example for a tape that was fixed by this bugfix is tape 029 from the
2954     level set "rnd_sam_bateman".
2955     The wrong behaviour will still be used for all levels or tapes that were
2956     created/recorded with it. An example for this is tape 023 from the level
2957     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2958   */
2959
2960   boolean use_amoeba_dropping_cannot_fall_bug =
2961     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2962       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2963      (tape.playing &&
2964       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2965       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2966
2967   /*
2968     Summary of bugfix/change:
2969     Fixed move speed of elements entering or leaving magic wall.
2970
2971     Fixed/changed in version:
2972     2.0.1
2973
2974     Description:
2975     Before 2.0.1, move speed of elements entering or leaving magic wall was
2976     twice as fast as it is now.
2977     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2978
2979     Affected levels/tapes:
2980     The first condition is generally needed for all levels/tapes before version
2981     2.0.1, which might use the old behaviour before it was changed; known tapes
2982     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2983     The second condition is an exception from the above case and is needed for
2984     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2985     above, but before it was known that this change would break tapes like the
2986     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2987     although the engine version while recording maybe was before 2.0.1. There
2988     are a lot of tapes that are affected by this exception, like tape 006 from
2989     the level set "rnd_conor_mancone".
2990   */
2991
2992   boolean use_old_move_stepsize_for_magic_wall =
2993     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2994      !(tape.playing &&
2995        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2996        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2997
2998   /*
2999     Summary of bugfix/change:
3000     Fixed handling for custom elements that change when pushed by the player.
3001
3002     Fixed/changed in version:
3003     3.1.0
3004
3005     Description:
3006     Before 3.1.0, custom elements that "change when pushing" changed directly
3007     after the player started pushing them (until then handled in "DigField()").
3008     Since 3.1.0, these custom elements are not changed until the "pushing"
3009     move of the element is finished (now handled in "ContinueMoving()").
3010
3011     Affected levels/tapes:
3012     The first condition is generally needed for all levels/tapes before version
3013     3.1.0, which might use the old behaviour before it was changed; known tapes
3014     that are affected are some tapes from the level set "Walpurgis Gardens" by
3015     Jamie Cullen.
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3018     above (including some development versions of 3.1.0), but before it was
3019     known that this change would break tapes like the above and was fixed in
3020     3.1.1, so that the changed behaviour was active although the engine version
3021     while recording maybe was before 3.1.0. There is at least one tape that is
3022     affected by this exception, which is the tape for the one-level set "Bug
3023     Machine" by Juergen Bonhagen.
3024   */
3025
3026   game.use_change_when_pushing_bug =
3027     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3028      !(tape.playing &&
3029        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3030        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3031
3032   /*
3033     Summary of bugfix/change:
3034     Fixed handling for blocking the field the player leaves when moving.
3035
3036     Fixed/changed in version:
3037     3.1.1
3038
3039     Description:
3040     Before 3.1.1, when "block last field when moving" was enabled, the field
3041     the player is leaving when moving was blocked for the time of the move,
3042     and was directly unblocked afterwards. This resulted in the last field
3043     being blocked for exactly one less than the number of frames of one player
3044     move. Additionally, even when blocking was disabled, the last field was
3045     blocked for exactly one frame.
3046     Since 3.1.1, due to changes in player movement handling, the last field
3047     is not blocked at all when blocking is disabled. When blocking is enabled,
3048     the last field is blocked for exactly the number of frames of one player
3049     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3050     last field is blocked for exactly one more than the number of frames of
3051     one player move.
3052
3053     Affected levels/tapes:
3054     (!!! yet to be determined -- probably many !!!)
3055   */
3056
3057   game.use_block_last_field_bug =
3058     (game.engine_version < VERSION_IDENT(3,1,1,0));
3059
3060   /* various special flags and settings for native Emerald Mine game engine */
3061
3062   game_em.use_single_button =
3063     (game.engine_version > VERSION_IDENT(4,0,0,2));
3064
3065   game_em.use_snap_key_bug =
3066     (game.engine_version < VERSION_IDENT(4,0,1,0));
3067
3068   game_em.use_random_bug =
3069     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3070
3071   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3072
3073   game_em.use_old_explosions            = use_old_em_engine;
3074   game_em.use_old_android               = use_old_em_engine;
3075   game_em.use_old_push_elements         = use_old_em_engine;
3076   game_em.use_old_push_into_acid        = use_old_em_engine;
3077
3078   game_em.use_wrap_around               = !use_old_em_engine;
3079
3080   // --------------------------------------------------------------------------
3081
3082   // set maximal allowed number of custom element changes per game frame
3083   game.max_num_changes_per_frame = 1;
3084
3085   // default scan direction: scan playfield from top/left to bottom/right
3086   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3087
3088   // dynamically adjust element properties according to game engine version
3089   InitElementPropertiesEngine(game.engine_version);
3090
3091   // ---------- initialize special element properties -------------------------
3092
3093   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3094   if (use_amoeba_dropping_cannot_fall_bug)
3095     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3096
3097   // ---------- initialize player's initial move delay ------------------------
3098
3099   // dynamically adjust player properties according to level information
3100   for (i = 0; i < MAX_PLAYERS; i++)
3101     game.initial_move_delay_value[i] =
3102       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3103
3104   // dynamically adjust player properties according to game engine version
3105   for (i = 0; i < MAX_PLAYERS; i++)
3106     game.initial_move_delay[i] =
3107       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3108        game.initial_move_delay_value[i] : 0);
3109
3110   // ---------- initialize player's initial push delay ------------------------
3111
3112   // dynamically adjust player properties according to game engine version
3113   game.initial_push_delay_value =
3114     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3115
3116   // ---------- initialize changing elements ----------------------------------
3117
3118   // initialize changing elements information
3119   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3120   {
3121     struct ElementInfo *ei = &element_info[i];
3122
3123     // this pointer might have been changed in the level editor
3124     ei->change = &ei->change_page[0];
3125
3126     if (!IS_CUSTOM_ELEMENT(i))
3127     {
3128       ei->change->target_element = EL_EMPTY_SPACE;
3129       ei->change->delay_fixed = 0;
3130       ei->change->delay_random = 0;
3131       ei->change->delay_frames = 1;
3132     }
3133
3134     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3135     {
3136       ei->has_change_event[j] = FALSE;
3137
3138       ei->event_page_nr[j] = 0;
3139       ei->event_page[j] = &ei->change_page[0];
3140     }
3141   }
3142
3143   // add changing elements from pre-defined list
3144   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3145   {
3146     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3147     struct ElementInfo *ei = &element_info[ch_delay->element];
3148
3149     ei->change->target_element       = ch_delay->target_element;
3150     ei->change->delay_fixed          = ch_delay->change_delay;
3151
3152     ei->change->pre_change_function  = ch_delay->pre_change_function;
3153     ei->change->change_function      = ch_delay->change_function;
3154     ei->change->post_change_function = ch_delay->post_change_function;
3155
3156     ei->change->can_change = TRUE;
3157     ei->change->can_change_or_has_action = TRUE;
3158
3159     ei->has_change_event[CE_DELAY] = TRUE;
3160
3161     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3162     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3163   }
3164
3165   // ---------- initialize internal run-time variables ------------------------
3166
3167   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3168   {
3169     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3170
3171     for (j = 0; j < ei->num_change_pages; j++)
3172     {
3173       ei->change_page[j].can_change_or_has_action =
3174         (ei->change_page[j].can_change |
3175          ei->change_page[j].has_action);
3176     }
3177   }
3178
3179   // add change events from custom element configuration
3180   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3181   {
3182     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3183
3184     for (j = 0; j < ei->num_change_pages; j++)
3185     {
3186       if (!ei->change_page[j].can_change_or_has_action)
3187         continue;
3188
3189       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3190       {
3191         // only add event page for the first page found with this event
3192         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3193         {
3194           ei->has_change_event[k] = TRUE;
3195
3196           ei->event_page_nr[k] = j;
3197           ei->event_page[k] = &ei->change_page[j];
3198         }
3199       }
3200     }
3201   }
3202
3203   // ---------- initialize reference elements in change conditions ------------
3204
3205   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3206   {
3207     int element = EL_CUSTOM_START + i;
3208     struct ElementInfo *ei = &element_info[element];
3209
3210     for (j = 0; j < ei->num_change_pages; j++)
3211     {
3212       int trigger_element = ei->change_page[j].initial_trigger_element;
3213
3214       if (trigger_element >= EL_PREV_CE_8 &&
3215           trigger_element <= EL_NEXT_CE_8)
3216         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3217
3218       ei->change_page[j].trigger_element = trigger_element;
3219     }
3220   }
3221
3222   // ---------- initialize run-time trigger player and element ----------------
3223
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3231       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3232       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3233       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3234       ei->change_page[j].actual_trigger_ce_value = 0;
3235       ei->change_page[j].actual_trigger_ce_score = 0;
3236     }
3237   }
3238
3239   // ---------- initialize trigger events -------------------------------------
3240
3241   // initialize trigger events information
3242   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3243     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3244       trigger_events[i][j] = FALSE;
3245
3246   // add trigger events from element change event properties
3247   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3248   {
3249     struct ElementInfo *ei = &element_info[i];
3250
3251     for (j = 0; j < ei->num_change_pages; j++)
3252     {
3253       if (!ei->change_page[j].can_change_or_has_action)
3254         continue;
3255
3256       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3257       {
3258         int trigger_element = ei->change_page[j].trigger_element;
3259
3260         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3261         {
3262           if (ei->change_page[j].has_event[k])
3263           {
3264             if (IS_GROUP_ELEMENT(trigger_element))
3265             {
3266               struct ElementGroupInfo *group =
3267                 element_info[trigger_element].group;
3268
3269               for (l = 0; l < group->num_elements_resolved; l++)
3270                 trigger_events[group->element_resolved[l]][k] = TRUE;
3271             }
3272             else if (trigger_element == EL_ANY_ELEMENT)
3273               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3274                 trigger_events[l][k] = TRUE;
3275             else
3276               trigger_events[trigger_element][k] = TRUE;
3277           }
3278         }
3279       }
3280     }
3281   }
3282
3283   // ---------- initialize push delay -----------------------------------------
3284
3285   // initialize push delay values to default
3286   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3287   {
3288     if (!IS_CUSTOM_ELEMENT(i))
3289     {
3290       // set default push delay values (corrected since version 3.0.7-1)
3291       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3292       {
3293         element_info[i].push_delay_fixed = 2;
3294         element_info[i].push_delay_random = 8;
3295       }
3296       else
3297       {
3298         element_info[i].push_delay_fixed = 8;
3299         element_info[i].push_delay_random = 8;
3300       }
3301     }
3302   }
3303
3304   // set push delay value for certain elements from pre-defined list
3305   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3306   {
3307     int e = push_delay_list[i].element;
3308
3309     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3310     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3311   }
3312
3313   // set push delay value for Supaplex elements for newer engine versions
3314   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3315   {
3316     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3317     {
3318       if (IS_SP_ELEMENT(i))
3319       {
3320         // set SP push delay to just enough to push under a falling zonk
3321         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3322
3323         element_info[i].push_delay_fixed  = delay;
3324         element_info[i].push_delay_random = 0;
3325       }
3326     }
3327   }
3328
3329   // ---------- initialize move stepsize --------------------------------------
3330
3331   // initialize move stepsize values to default
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333     if (!IS_CUSTOM_ELEMENT(i))
3334       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3335
3336   // set move stepsize value for certain elements from pre-defined list
3337   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3338   {
3339     int e = move_stepsize_list[i].element;
3340
3341     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3342
3343     // set move stepsize value for certain elements for older engine versions
3344     if (use_old_move_stepsize_for_magic_wall)
3345     {
3346       if (e == EL_MAGIC_WALL_FILLING ||
3347           e == EL_MAGIC_WALL_EMPTYING ||
3348           e == EL_BD_MAGIC_WALL_FILLING ||
3349           e == EL_BD_MAGIC_WALL_EMPTYING)
3350         element_info[e].move_stepsize *= 2;
3351     }
3352   }
3353
3354   // ---------- initialize collect score --------------------------------------
3355
3356   // initialize collect score values for custom elements from initial value
3357   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3358     if (IS_CUSTOM_ELEMENT(i))
3359       element_info[i].collect_score = element_info[i].collect_score_initial;
3360
3361   // ---------- initialize collect count --------------------------------------
3362
3363   // initialize collect count values for non-custom elements
3364   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     if (!IS_CUSTOM_ELEMENT(i))
3366       element_info[i].collect_count_initial = 0;
3367
3368   // add collect count values for all elements from pre-defined list
3369   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3370     element_info[collect_count_list[i].element].collect_count_initial =
3371       collect_count_list[i].count;
3372
3373   // ---------- initialize access direction -----------------------------------
3374
3375   // initialize access direction values to default (access from every side)
3376   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3377     if (!IS_CUSTOM_ELEMENT(i))
3378       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3379
3380   // set access direction value for certain elements from pre-defined list
3381   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3382     element_info[access_direction_list[i].element].access_direction =
3383       access_direction_list[i].direction;
3384
3385   // ---------- initialize explosion content ----------------------------------
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387   {
3388     if (IS_CUSTOM_ELEMENT(i))
3389       continue;
3390
3391     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3392     {
3393       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3394
3395       element_info[i].content.e[x][y] =
3396         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3397          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3398          i == EL_PLAYER_3 ? EL_EMERALD :
3399          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3400          i == EL_MOLE ? EL_EMERALD_RED :
3401          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3402          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3403          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3404          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3405          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3406          i == EL_WALL_EMERALD ? EL_EMERALD :
3407          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3408          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3409          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3410          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3411          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3412          i == EL_WALL_PEARL ? EL_PEARL :
3413          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3414          EL_EMPTY);
3415     }
3416   }
3417
3418   // ---------- initialize recursion detection --------------------------------
3419   recursion_loop_depth = 0;
3420   recursion_loop_detected = FALSE;
3421   recursion_loop_element = EL_UNDEFINED;
3422
3423   // ---------- initialize graphics engine ------------------------------------
3424   game.scroll_delay_value =
3425     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3426      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3427      !setup.forced_scroll_delay           ? 0 :
3428      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3429   game.scroll_delay_value =
3430     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3431
3432   // ---------- initialize game engine snapshots ------------------------------
3433   for (i = 0; i < MAX_PLAYERS; i++)
3434     game.snapshot.last_action[i] = 0;
3435   game.snapshot.changed_action = FALSE;
3436   game.snapshot.collected_item = FALSE;
3437   game.snapshot.mode =
3438     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3439      SNAPSHOT_MODE_EVERY_STEP :
3440      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3441      SNAPSHOT_MODE_EVERY_MOVE :
3442      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3443      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3444   game.snapshot.save_snapshot = FALSE;
3445
3446   // ---------- initialize level time for Supaplex engine ---------------------
3447   // Supaplex levels with time limit currently unsupported -- should be added
3448   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3449     level.time = 0;
3450
3451   // ---------- initialize flags for handling game actions --------------------
3452
3453   // set flags for game actions to default values
3454   game.use_key_actions = TRUE;
3455   game.use_mouse_actions = FALSE;
3456
3457   // when using Mirror Magic game engine, handle mouse events only
3458   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3459   {
3460     game.use_key_actions = FALSE;
3461     game.use_mouse_actions = TRUE;
3462   }
3463
3464   // check for custom elements with mouse click events
3465   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3466   {
3467     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3468     {
3469       int element = EL_CUSTOM_START + i;
3470
3471       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3472           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3473           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3474           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3475         game.use_mouse_actions = TRUE;
3476     }
3477   }
3478 }
3479
3480 static int get_num_special_action(int element, int action_first,
3481                                   int action_last)
3482 {
3483   int num_special_action = 0;
3484   int i, j;
3485
3486   for (i = action_first; i <= action_last; i++)
3487   {
3488     boolean found = FALSE;
3489
3490     for (j = 0; j < NUM_DIRECTIONS; j++)
3491       if (el_act_dir2img(element, i, j) !=
3492           el_act_dir2img(element, ACTION_DEFAULT, j))
3493         found = TRUE;
3494
3495     if (found)
3496       num_special_action++;
3497     else
3498       break;
3499   }
3500
3501   return num_special_action;
3502 }
3503
3504
3505 // ============================================================================
3506 // InitGame()
3507 // ----------------------------------------------------------------------------
3508 // initialize and start new game
3509 // ============================================================================
3510
3511 #if DEBUG_INIT_PLAYER
3512 static void DebugPrintPlayerStatus(char *message)
3513 {
3514   int i;
3515
3516   if (!options.debug)
3517     return;
3518
3519   Debug("game:init:player", "%s:", message);
3520
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     Debug("game:init:player",
3526           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3527           i + 1,
3528           player->present,
3529           player->connected,
3530           player->connected_locally,
3531           player->connected_network,
3532           player->active,
3533           (local_player == player ? " (local player)" : ""));
3534   }
3535 }
3536 #endif
3537
3538 void InitGame(void)
3539 {
3540   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3541   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3542   int fade_mask = REDRAW_FIELD;
3543
3544   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3545   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3546   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3547   int initial_move_dir = MV_DOWN;
3548   int i, j, x, y;
3549
3550   // required here to update video display before fading (FIX THIS)
3551   DrawMaskedBorder(REDRAW_DOOR_2);
3552
3553   if (!game.restart_level)
3554     CloseDoor(DOOR_CLOSE_1);
3555
3556   SetGameStatus(GAME_MODE_PLAYING);
3557
3558   if (level_editor_test_game)
3559     FadeSkipNextFadeOut();
3560   else
3561     FadeSetEnterScreen();
3562
3563   if (CheckFadeAll())
3564     fade_mask = REDRAW_ALL;
3565
3566   FadeLevelSoundsAndMusic();
3567
3568   ExpireSoundLoops(TRUE);
3569
3570   FadeOut(fade_mask);
3571
3572   if (level_editor_test_game)
3573     FadeSkipNextFadeIn();
3574
3575   // needed if different viewport properties defined for playing
3576   ChangeViewportPropertiesIfNeeded();
3577
3578   ClearField();
3579
3580   DrawCompleteVideoDisplay();
3581
3582   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3583
3584   InitGameEngine();
3585   InitGameControlValues();
3586
3587   if (tape.recording)
3588   {
3589     // initialize tape actions from game when recording tape
3590     tape.use_key_actions   = game.use_key_actions;
3591     tape.use_mouse_actions = game.use_mouse_actions;
3592
3593     // initialize visible playfield size when recording tape (for team mode)
3594     tape.scr_fieldx = SCR_FIELDX;
3595     tape.scr_fieldy = SCR_FIELDY;
3596   }
3597
3598   // don't play tapes over network
3599   network_playing = (network.enabled && !tape.playing);
3600
3601   for (i = 0; i < MAX_PLAYERS; i++)
3602   {
3603     struct PlayerInfo *player = &stored_player[i];
3604
3605     player->index_nr = i;
3606     player->index_bit = (1 << i);
3607     player->element_nr = EL_PLAYER_1 + i;
3608
3609     player->present = FALSE;
3610     player->active = FALSE;
3611     player->mapped = FALSE;
3612
3613     player->killed = FALSE;
3614     player->reanimated = FALSE;
3615     player->buried = FALSE;
3616
3617     player->action = 0;
3618     player->effective_action = 0;
3619     player->programmed_action = 0;
3620     player->snap_action = 0;
3621
3622     player->mouse_action.lx = 0;
3623     player->mouse_action.ly = 0;
3624     player->mouse_action.button = 0;
3625     player->mouse_action.button_hint = 0;
3626
3627     player->effective_mouse_action.lx = 0;
3628     player->effective_mouse_action.ly = 0;
3629     player->effective_mouse_action.button = 0;
3630     player->effective_mouse_action.button_hint = 0;
3631
3632     for (j = 0; j < MAX_NUM_KEYS; j++)
3633       player->key[j] = FALSE;
3634
3635     player->num_white_keys = 0;
3636
3637     player->dynabomb_count = 0;
3638     player->dynabomb_size = 1;
3639     player->dynabombs_left = 0;
3640     player->dynabomb_xl = FALSE;
3641
3642     player->MovDir = initial_move_dir;
3643     player->MovPos = 0;
3644     player->GfxPos = 0;
3645     player->GfxDir = initial_move_dir;
3646     player->GfxAction = ACTION_DEFAULT;
3647     player->Frame = 0;
3648     player->StepFrame = 0;
3649
3650     player->initial_element = player->element_nr;
3651     player->artwork_element =
3652       (level.use_artwork_element[i] ? level.artwork_element[i] :
3653        player->element_nr);
3654     player->use_murphy = FALSE;
3655
3656     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3657     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3658
3659     player->gravity = level.initial_player_gravity[i];
3660
3661     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3662
3663     player->actual_frame_counter = 0;
3664
3665     player->step_counter = 0;
3666
3667     player->last_move_dir = initial_move_dir;
3668
3669     player->is_active = FALSE;
3670
3671     player->is_waiting = FALSE;
3672     player->is_moving = FALSE;
3673     player->is_auto_moving = FALSE;
3674     player->is_digging = FALSE;
3675     player->is_snapping = FALSE;
3676     player->is_collecting = FALSE;
3677     player->is_pushing = FALSE;
3678     player->is_switching = FALSE;
3679     player->is_dropping = FALSE;
3680     player->is_dropping_pressed = FALSE;
3681
3682     player->is_bored = FALSE;
3683     player->is_sleeping = FALSE;
3684
3685     player->was_waiting = TRUE;
3686     player->was_moving = FALSE;
3687     player->was_snapping = FALSE;
3688     player->was_dropping = FALSE;
3689
3690     player->force_dropping = FALSE;
3691
3692     player->frame_counter_bored = -1;
3693     player->frame_counter_sleeping = -1;
3694
3695     player->anim_delay_counter = 0;
3696     player->post_delay_counter = 0;
3697
3698     player->dir_waiting = initial_move_dir;
3699     player->action_waiting = ACTION_DEFAULT;
3700     player->last_action_waiting = ACTION_DEFAULT;
3701     player->special_action_bored = ACTION_DEFAULT;
3702     player->special_action_sleeping = ACTION_DEFAULT;
3703
3704     player->switch_x = -1;
3705     player->switch_y = -1;
3706
3707     player->drop_x = -1;
3708     player->drop_y = -1;
3709
3710     player->show_envelope = 0;
3711
3712     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3713
3714     player->push_delay       = -1;      // initialized when pushing starts
3715     player->push_delay_value = game.initial_push_delay_value;
3716
3717     player->drop_delay = 0;
3718     player->drop_pressed_delay = 0;
3719
3720     player->last_jx = -1;
3721     player->last_jy = -1;
3722     player->jx = -1;
3723     player->jy = -1;
3724
3725     player->shield_normal_time_left = 0;
3726     player->shield_deadly_time_left = 0;
3727
3728     player->last_removed_element = EL_UNDEFINED;
3729
3730     player->inventory_infinite_element = EL_UNDEFINED;
3731     player->inventory_size = 0;
3732
3733     if (level.use_initial_inventory[i])
3734     {
3735       for (j = 0; j < level.initial_inventory_size[i]; j++)
3736       {
3737         int element = level.initial_inventory_content[i][j];
3738         int collect_count = element_info[element].collect_count_initial;
3739         int k;
3740
3741         if (!IS_CUSTOM_ELEMENT(element))
3742           collect_count = 1;
3743
3744         if (collect_count == 0)
3745           player->inventory_infinite_element = element;
3746         else
3747           for (k = 0; k < collect_count; k++)
3748             if (player->inventory_size < MAX_INVENTORY_SIZE)
3749               player->inventory_element[player->inventory_size++] = element;
3750       }
3751     }
3752
3753     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3754     SnapField(player, 0, 0);
3755
3756     map_player_action[i] = i;
3757   }
3758
3759   network_player_action_received = FALSE;
3760
3761   // initial null action
3762   if (network_playing)
3763     SendToServer_MovePlayer(MV_NONE);
3764
3765   FrameCounter = 0;
3766   TimeFrames = 0;
3767   TimePlayed = 0;
3768   TimeLeft = level.time;
3769   TapeTime = 0;
3770
3771   ScreenMovDir = MV_NONE;
3772   ScreenMovPos = 0;
3773   ScreenGfxPos = 0;
3774
3775   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3776
3777   game.robot_wheel_x = -1;
3778   game.robot_wheel_y = -1;
3779
3780   game.exit_x = -1;
3781   game.exit_y = -1;
3782
3783   game.all_players_gone = FALSE;
3784
3785   game.LevelSolved = FALSE;
3786   game.GameOver = FALSE;
3787
3788   game.GamePlayed = !tape.playing;
3789
3790   game.LevelSolved_GameWon = FALSE;
3791   game.LevelSolved_GameEnd = FALSE;
3792   game.LevelSolved_SaveTape = FALSE;
3793   game.LevelSolved_SaveScore = FALSE;
3794
3795   game.LevelSolved_CountingTime = 0;
3796   game.LevelSolved_CountingScore = 0;
3797   game.LevelSolved_CountingHealth = 0;
3798
3799   game.panel.active = TRUE;
3800
3801   game.no_time_limit = (level.time == 0);
3802
3803   game.yamyam_content_nr = 0;
3804   game.robot_wheel_active = FALSE;
3805   game.magic_wall_active = FALSE;
3806   game.magic_wall_time_left = 0;
3807   game.light_time_left = 0;
3808   game.timegate_time_left = 0;
3809   game.switchgate_pos = 0;
3810   game.wind_direction = level.wind_direction_initial;
3811
3812   game.time_final = 0;
3813   game.score_time_final = 0;
3814
3815   game.score = 0;
3816   game.score_final = 0;
3817
3818   game.health = MAX_HEALTH;
3819   game.health_final = MAX_HEALTH;
3820
3821   game.gems_still_needed = level.gems_needed;
3822   game.sokoban_fields_still_needed = 0;
3823   game.sokoban_objects_still_needed = 0;
3824   game.lights_still_needed = 0;
3825   game.players_still_needed = 0;
3826   game.friends_still_needed = 0;
3827
3828   game.lenses_time_left = 0;
3829   game.magnify_time_left = 0;
3830
3831   game.ball_active = level.ball_active_initial;
3832   game.ball_content_nr = 0;
3833
3834   game.explosions_delayed = TRUE;
3835
3836   game.envelope_active = FALSE;
3837
3838   for (i = 0; i < NUM_BELTS; i++)
3839   {
3840     game.belt_dir[i] = MV_NONE;
3841     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3842   }
3843
3844   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3845     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3846
3847 #if DEBUG_INIT_PLAYER
3848   DebugPrintPlayerStatus("Player status at level initialization");
3849 #endif
3850
3851   SCAN_PLAYFIELD(x, y)
3852   {
3853     Tile[x][y] = Last[x][y] = level.field[x][y];
3854     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3855     ChangeDelay[x][y] = 0;
3856     ChangePage[x][y] = -1;
3857     CustomValue[x][y] = 0;              // initialized in InitField()
3858     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3859     AmoebaNr[x][y] = 0;
3860     WasJustMoving[x][y] = 0;
3861     WasJustFalling[x][y] = 0;
3862     CheckCollision[x][y] = 0;
3863     CheckImpact[x][y] = 0;
3864     Stop[x][y] = FALSE;
3865     Pushed[x][y] = FALSE;
3866
3867     ChangeCount[x][y] = 0;
3868     ChangeEvent[x][y] = -1;
3869
3870     ExplodePhase[x][y] = 0;
3871     ExplodeDelay[x][y] = 0;
3872     ExplodeField[x][y] = EX_TYPE_NONE;
3873
3874     RunnerVisit[x][y] = 0;
3875     PlayerVisit[x][y] = 0;
3876
3877     GfxFrame[x][y] = 0;
3878     GfxRandom[x][y] = INIT_GFX_RANDOM();
3879     GfxElement[x][y] = EL_UNDEFINED;
3880     GfxAction[x][y] = ACTION_DEFAULT;
3881     GfxDir[x][y] = MV_NONE;
3882     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3883   }
3884
3885   SCAN_PLAYFIELD(x, y)
3886   {
3887     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3888       emulate_bd = FALSE;
3889     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3890       emulate_sb = 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_sb ? EMU_SOKOBAN :
3916                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3917
3918   // initialize type of slippery elements
3919   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3920   {
3921     if (!IS_CUSTOM_ELEMENT(i))
3922     {
3923       // default: elements slip down either to the left or right randomly
3924       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3925
3926       // SP style elements prefer to slip down on the left side
3927       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3928         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3929
3930       // BD style elements prefer to slip down on the left side
3931       if (game.emulation == EMU_BOULDERDASH)
3932         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3933     }
3934   }
3935
3936   // initialize explosion and ignition delay
3937   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3938   {
3939     if (!IS_CUSTOM_ELEMENT(i))
3940     {
3941       int num_phase = 8;
3942       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3943                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3944                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3945       int last_phase = (num_phase + 1) * delay;
3946       int half_phase = (num_phase / 2) * delay;
3947
3948       element_info[i].explosion_delay = last_phase - 1;
3949       element_info[i].ignition_delay = half_phase;
3950
3951       if (i == EL_BLACK_ORB)
3952         element_info[i].ignition_delay = 1;
3953     }
3954   }
3955
3956   // correct non-moving belts to start moving left
3957   for (i = 0; i < NUM_BELTS; i++)
3958     if (game.belt_dir[i] == MV_NONE)
3959       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3960
3961 #if USE_NEW_PLAYER_ASSIGNMENTS
3962   // use preferred player also in local single-player mode
3963   if (!network.enabled && !game.team_mode)
3964   {
3965     int new_index_nr = setup.network_player_nr;
3966
3967     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3968     {
3969       for (i = 0; i < MAX_PLAYERS; i++)
3970         stored_player[i].connected_locally = FALSE;
3971
3972       stored_player[new_index_nr].connected_locally = TRUE;
3973     }
3974   }
3975
3976   for (i = 0; i < MAX_PLAYERS; i++)
3977   {
3978     stored_player[i].connected = FALSE;
3979
3980     // in network game mode, the local player might not be the first player
3981     if (stored_player[i].connected_locally)
3982       local_player = &stored_player[i];
3983   }
3984
3985   if (!network.enabled)
3986     local_player->connected = TRUE;
3987
3988   if (tape.playing)
3989   {
3990     for (i = 0; i < MAX_PLAYERS; i++)
3991       stored_player[i].connected = tape.player_participates[i];
3992   }
3993   else if (network.enabled)
3994   {
3995     // add team mode players connected over the network (needed for correct
3996     // assignment of player figures from level to locally playing players)
3997
3998     for (i = 0; i < MAX_PLAYERS; i++)
3999       if (stored_player[i].connected_network)
4000         stored_player[i].connected = TRUE;
4001   }
4002   else if (game.team_mode)
4003   {
4004     // try to guess locally connected team mode players (needed for correct
4005     // assignment of player figures from level to locally playing players)
4006
4007     for (i = 0; i < MAX_PLAYERS; i++)
4008       if (setup.input[i].use_joystick ||
4009           setup.input[i].key.left != KSYM_UNDEFINED)
4010         stored_player[i].connected = TRUE;
4011   }
4012
4013 #if DEBUG_INIT_PLAYER
4014   DebugPrintPlayerStatus("Player status after level initialization");
4015 #endif
4016
4017 #if DEBUG_INIT_PLAYER
4018   Debug("game:init:player", "Reassigning players ...");
4019 #endif
4020
4021   // check if any connected player was not found in playfield
4022   for (i = 0; i < MAX_PLAYERS; i++)
4023   {
4024     struct PlayerInfo *player = &stored_player[i];
4025
4026     if (player->connected && !player->present)
4027     {
4028       struct PlayerInfo *field_player = NULL;
4029
4030 #if DEBUG_INIT_PLAYER
4031       Debug("game:init:player",
4032             "- looking for field player for player %d ...", i + 1);
4033 #endif
4034
4035       // assign first free player found that is present in the playfield
4036
4037       // first try: look for unmapped playfield player that is not connected
4038       for (j = 0; j < MAX_PLAYERS; j++)
4039         if (field_player == NULL &&
4040             stored_player[j].present &&
4041             !stored_player[j].mapped &&
4042             !stored_player[j].connected)
4043           field_player = &stored_player[j];
4044
4045       // second try: look for *any* unmapped playfield player
4046       for (j = 0; j < MAX_PLAYERS; j++)
4047         if (field_player == NULL &&
4048             stored_player[j].present &&
4049             !stored_player[j].mapped)
4050           field_player = &stored_player[j];
4051
4052       if (field_player != NULL)
4053       {
4054         int jx = field_player->jx, jy = field_player->jy;
4055
4056 #if DEBUG_INIT_PLAYER
4057         Debug("game:init:player", "- found player %d",
4058               field_player->index_nr + 1);
4059 #endif
4060
4061         player->present = FALSE;
4062         player->active = FALSE;
4063
4064         field_player->present = TRUE;
4065         field_player->active = TRUE;
4066
4067         /*
4068         player->initial_element = field_player->initial_element;
4069         player->artwork_element = field_player->artwork_element;
4070
4071         player->block_last_field       = field_player->block_last_field;
4072         player->block_delay_adjustment = field_player->block_delay_adjustment;
4073         */
4074
4075         StorePlayer[jx][jy] = field_player->element_nr;
4076
4077         field_player->jx = field_player->last_jx = jx;
4078         field_player->jy = field_player->last_jy = jy;
4079
4080         if (local_player == player)
4081           local_player = field_player;
4082
4083         map_player_action[field_player->index_nr] = i;
4084
4085         field_player->mapped = TRUE;
4086
4087 #if DEBUG_INIT_PLAYER
4088         Debug("game:init:player", "- map_player_action[%d] == %d",
4089               field_player->index_nr + 1, i + 1);
4090 #endif
4091       }
4092     }
4093
4094     if (player->connected && player->present)
4095       player->mapped = TRUE;
4096   }
4097
4098 #if DEBUG_INIT_PLAYER
4099   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4100 #endif
4101
4102 #else
4103
4104   // check if any connected player was not found in playfield
4105   for (i = 0; i < MAX_PLAYERS; i++)
4106   {
4107     struct PlayerInfo *player = &stored_player[i];
4108
4109     if (player->connected && !player->present)
4110     {
4111       for (j = 0; j < MAX_PLAYERS; j++)
4112       {
4113         struct PlayerInfo *field_player = &stored_player[j];
4114         int jx = field_player->jx, jy = field_player->jy;
4115
4116         // assign first free player found that is present in the playfield
4117         if (field_player->present && !field_player->connected)
4118         {
4119           player->present = TRUE;
4120           player->active = TRUE;
4121
4122           field_player->present = FALSE;
4123           field_player->active = FALSE;
4124
4125           player->initial_element = field_player->initial_element;
4126           player->artwork_element = field_player->artwork_element;
4127
4128           player->block_last_field       = field_player->block_last_field;
4129           player->block_delay_adjustment = field_player->block_delay_adjustment;
4130
4131           StorePlayer[jx][jy] = player->element_nr;
4132
4133           player->jx = player->last_jx = jx;
4134           player->jy = player->last_jy = jy;
4135
4136           break;
4137         }
4138       }
4139     }
4140   }
4141 #endif
4142
4143 #if 0
4144   Debug("game:init:player", "local_player->present == %d",
4145         local_player->present);
4146 #endif
4147
4148   // set focus to local player for network games, else to all players
4149   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4150   game.centered_player_nr_next = game.centered_player_nr;
4151   game.set_centered_player = FALSE;
4152   game.set_centered_player_wrap = FALSE;
4153
4154   if (network_playing && tape.recording)
4155   {
4156     // store client dependent player focus when recording network games
4157     tape.centered_player_nr_next = game.centered_player_nr_next;
4158     tape.set_centered_player = TRUE;
4159   }
4160
4161   if (tape.playing)
4162   {
4163     // when playing a tape, eliminate all players who do not participate
4164
4165 #if USE_NEW_PLAYER_ASSIGNMENTS
4166
4167     if (!game.team_mode)
4168     {
4169       for (i = 0; i < MAX_PLAYERS; i++)
4170       {
4171         if (stored_player[i].active &&
4172             !tape.player_participates[map_player_action[i]])
4173         {
4174           struct PlayerInfo *player = &stored_player[i];
4175           int jx = player->jx, jy = player->jy;
4176
4177 #if DEBUG_INIT_PLAYER
4178           Debug("game:init:player", "Removing player %d at (%d, %d)",
4179                 i + 1, jx, jy);
4180 #endif
4181
4182           player->active = FALSE;
4183           StorePlayer[jx][jy] = 0;
4184           Tile[jx][jy] = EL_EMPTY;
4185         }
4186       }
4187     }
4188
4189 #else
4190
4191     for (i = 0; i < MAX_PLAYERS; i++)
4192     {
4193       if (stored_player[i].active &&
4194           !tape.player_participates[i])
4195       {
4196         struct PlayerInfo *player = &stored_player[i];
4197         int jx = player->jx, jy = player->jy;
4198
4199         player->active = FALSE;
4200         StorePlayer[jx][jy] = 0;
4201         Tile[jx][jy] = EL_EMPTY;
4202       }
4203     }
4204 #endif
4205   }
4206   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4207   {
4208     // when in single player mode, eliminate all but the local player
4209
4210     for (i = 0; i < MAX_PLAYERS; i++)
4211     {
4212       struct PlayerInfo *player = &stored_player[i];
4213
4214       if (player->active && player != local_player)
4215       {
4216         int jx = player->jx, jy = player->jy;
4217
4218         player->active = FALSE;
4219         player->present = FALSE;
4220
4221         StorePlayer[jx][jy] = 0;
4222         Tile[jx][jy] = EL_EMPTY;
4223       }
4224     }
4225   }
4226
4227   for (i = 0; i < MAX_PLAYERS; i++)
4228     if (stored_player[i].active)
4229       game.players_still_needed++;
4230
4231   if (level.solved_by_one_player)
4232     game.players_still_needed = 1;
4233
4234   // when recording the game, store which players take part in the game
4235   if (tape.recording)
4236   {
4237 #if USE_NEW_PLAYER_ASSIGNMENTS
4238     for (i = 0; i < MAX_PLAYERS; i++)
4239       if (stored_player[i].connected)
4240         tape.player_participates[i] = TRUE;
4241 #else
4242     for (i = 0; i < MAX_PLAYERS; i++)
4243       if (stored_player[i].active)
4244         tape.player_participates[i] = TRUE;
4245 #endif
4246   }
4247
4248 #if DEBUG_INIT_PLAYER
4249   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4250 #endif
4251
4252   if (BorderElement == EL_EMPTY)
4253   {
4254     SBX_Left = 0;
4255     SBX_Right = lev_fieldx - SCR_FIELDX;
4256     SBY_Upper = 0;
4257     SBY_Lower = lev_fieldy - SCR_FIELDY;
4258   }
4259   else
4260   {
4261     SBX_Left = -1;
4262     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4263     SBY_Upper = -1;
4264     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4265   }
4266
4267   if (full_lev_fieldx <= SCR_FIELDX)
4268     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4269   if (full_lev_fieldy <= SCR_FIELDY)
4270     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4271
4272   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4273     SBX_Left--;
4274   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4275     SBY_Upper--;
4276
4277   // if local player not found, look for custom element that might create
4278   // the player (make some assumptions about the right custom element)
4279   if (!local_player->present)
4280   {
4281     int start_x = 0, start_y = 0;
4282     int found_rating = 0;
4283     int found_element = EL_UNDEFINED;
4284     int player_nr = local_player->index_nr;
4285
4286     SCAN_PLAYFIELD(x, y)
4287     {
4288       int element = Tile[x][y];
4289       int content;
4290       int xx, yy;
4291       boolean is_player;
4292
4293       if (level.use_start_element[player_nr] &&
4294           level.start_element[player_nr] == element &&
4295           found_rating < 4)
4296       {
4297         start_x = x;
4298         start_y = y;
4299
4300         found_rating = 4;
4301         found_element = element;
4302       }
4303
4304       if (!IS_CUSTOM_ELEMENT(element))
4305         continue;
4306
4307       if (CAN_CHANGE(element))
4308       {
4309         for (i = 0; i < element_info[element].num_change_pages; i++)
4310         {
4311           // check for player created from custom element as single target
4312           content = element_info[element].change_page[i].target_element;
4313           is_player = ELEM_IS_PLAYER(content);
4314
4315           if (is_player && (found_rating < 3 ||
4316                             (found_rating == 3 && element < found_element)))
4317           {
4318             start_x = x;
4319             start_y = y;
4320
4321             found_rating = 3;
4322             found_element = element;
4323           }
4324         }
4325       }
4326
4327       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4328       {
4329         // check for player created from custom element as explosion content
4330         content = element_info[element].content.e[xx][yy];
4331         is_player = ELEM_IS_PLAYER(content);
4332
4333         if (is_player && (found_rating < 2 ||
4334                           (found_rating == 2 && element < found_element)))
4335         {
4336           start_x = x + xx - 1;
4337           start_y = y + yy - 1;
4338
4339           found_rating = 2;
4340           found_element = element;
4341         }
4342
4343         if (!CAN_CHANGE(element))
4344           continue;
4345
4346         for (i = 0; i < element_info[element].num_change_pages; i++)
4347         {
4348           // check for player created from custom element as extended target
4349           content =
4350             element_info[element].change_page[i].target_content.e[xx][yy];
4351
4352           is_player = ELEM_IS_PLAYER(content);
4353
4354           if (is_player && (found_rating < 1 ||
4355                             (found_rating == 1 && element < found_element)))
4356           {
4357             start_x = x + xx - 1;
4358             start_y = y + yy - 1;
4359
4360             found_rating = 1;
4361             found_element = element;
4362           }
4363         }
4364       }
4365     }
4366
4367     scroll_x = SCROLL_POSITION_X(start_x);
4368     scroll_y = SCROLL_POSITION_Y(start_y);
4369   }
4370   else
4371   {
4372     scroll_x = SCROLL_POSITION_X(local_player->jx);
4373     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4374   }
4375
4376   // !!! FIX THIS (START) !!!
4377   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4378   {
4379     InitGameEngine_EM();
4380   }
4381   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4382   {
4383     InitGameEngine_SP();
4384   }
4385   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4386   {
4387     InitGameEngine_MM();
4388   }
4389   else
4390   {
4391     DrawLevel(REDRAW_FIELD);
4392     DrawAllPlayers();
4393
4394     // after drawing the level, correct some elements
4395     if (game.timegate_time_left == 0)
4396       CloseAllOpenTimegates();
4397   }
4398
4399   // blit playfield from scroll buffer to normal back buffer for fading in
4400   BlitScreenToBitmap(backbuffer);
4401   // !!! FIX THIS (END) !!!
4402
4403   DrawMaskedBorder(fade_mask);
4404
4405   FadeIn(fade_mask);
4406
4407 #if 1
4408   // full screen redraw is required at this point in the following cases:
4409   // - special editor door undrawn when game was started from level editor
4410   // - drawing area (playfield) was changed and has to be removed completely
4411   redraw_mask = REDRAW_ALL;
4412   BackToFront();
4413 #endif
4414
4415   if (!game.restart_level)
4416   {
4417     // copy default game door content to main double buffer
4418
4419     // !!! CHECK AGAIN !!!
4420     SetPanelBackground();
4421     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4422     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4423   }
4424
4425   SetPanelBackground();
4426   SetDrawBackgroundMask(REDRAW_DOOR_1);
4427
4428   UpdateAndDisplayGameControlValues();
4429
4430   if (!game.restart_level)
4431   {
4432     UnmapGameButtons();
4433     UnmapTapeButtons();
4434
4435     FreeGameButtons();
4436     CreateGameButtons();
4437
4438     MapGameButtons();
4439     MapTapeButtons();
4440
4441     // copy actual game door content to door double buffer for OpenDoor()
4442     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4443
4444     OpenDoor(DOOR_OPEN_ALL);
4445
4446     KeyboardAutoRepeatOffUnlessAutoplay();
4447
4448 #if DEBUG_INIT_PLAYER
4449     DebugPrintPlayerStatus("Player status (final)");
4450 #endif
4451   }
4452
4453   UnmapAllGadgets();
4454
4455   MapGameButtons();
4456   MapTapeButtons();
4457
4458   if (!game.restart_level && !tape.playing)
4459   {
4460     LevelStats_incPlayed(level_nr);
4461
4462     SaveLevelSetup_SeriesInfo();
4463   }
4464
4465   game.restart_level = FALSE;
4466   game.restart_game_message = NULL;
4467
4468   game.request_active = FALSE;
4469   game.request_active_or_moving = FALSE;
4470
4471   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4472     InitGameActions_MM();
4473
4474   SaveEngineSnapshotToListInitial();
4475
4476   if (!game.restart_level)
4477   {
4478     PlaySound(SND_GAME_STARTING);
4479
4480     if (setup.sound_music)
4481       PlayLevelMusic();
4482   }
4483 }
4484
4485 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4486                         int actual_player_x, int actual_player_y)
4487 {
4488   // this is used for non-R'n'D game engines to update certain engine values
4489
4490   // needed to determine if sounds are played within the visible screen area
4491   scroll_x = actual_scroll_x;
4492   scroll_y = actual_scroll_y;
4493
4494   // needed to get player position for "follow finger" playing input method
4495   local_player->jx = actual_player_x;
4496   local_player->jy = actual_player_y;
4497 }
4498
4499 void InitMovDir(int x, int y)
4500 {
4501   int i, element = Tile[x][y];
4502   static int xy[4][2] =
4503   {
4504     {  0, +1 },
4505     { +1,  0 },
4506     {  0, -1 },
4507     { -1,  0 }
4508   };
4509   static int direction[3][4] =
4510   {
4511     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4512     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4513     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4514   };
4515
4516   switch (element)
4517   {
4518     case EL_BUG_RIGHT:
4519     case EL_BUG_UP:
4520     case EL_BUG_LEFT:
4521     case EL_BUG_DOWN:
4522       Tile[x][y] = EL_BUG;
4523       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4524       break;
4525
4526     case EL_SPACESHIP_RIGHT:
4527     case EL_SPACESHIP_UP:
4528     case EL_SPACESHIP_LEFT:
4529     case EL_SPACESHIP_DOWN:
4530       Tile[x][y] = EL_SPACESHIP;
4531       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4532       break;
4533
4534     case EL_BD_BUTTERFLY_RIGHT:
4535     case EL_BD_BUTTERFLY_UP:
4536     case EL_BD_BUTTERFLY_LEFT:
4537     case EL_BD_BUTTERFLY_DOWN:
4538       Tile[x][y] = EL_BD_BUTTERFLY;
4539       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4540       break;
4541
4542     case EL_BD_FIREFLY_RIGHT:
4543     case EL_BD_FIREFLY_UP:
4544     case EL_BD_FIREFLY_LEFT:
4545     case EL_BD_FIREFLY_DOWN:
4546       Tile[x][y] = EL_BD_FIREFLY;
4547       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4548       break;
4549
4550     case EL_PACMAN_RIGHT:
4551     case EL_PACMAN_UP:
4552     case EL_PACMAN_LEFT:
4553     case EL_PACMAN_DOWN:
4554       Tile[x][y] = EL_PACMAN;
4555       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4556       break;
4557
4558     case EL_YAMYAM_LEFT:
4559     case EL_YAMYAM_RIGHT:
4560     case EL_YAMYAM_UP:
4561     case EL_YAMYAM_DOWN:
4562       Tile[x][y] = EL_YAMYAM;
4563       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4564       break;
4565
4566     case EL_SP_SNIKSNAK:
4567       MovDir[x][y] = MV_UP;
4568       break;
4569
4570     case EL_SP_ELECTRON:
4571       MovDir[x][y] = MV_LEFT;
4572       break;
4573
4574     case EL_MOLE_LEFT:
4575     case EL_MOLE_RIGHT:
4576     case EL_MOLE_UP:
4577     case EL_MOLE_DOWN:
4578       Tile[x][y] = EL_MOLE;
4579       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4580       break;
4581
4582     case EL_SPRING_LEFT:
4583     case EL_SPRING_RIGHT:
4584       Tile[x][y] = EL_SPRING;
4585       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4586       break;
4587
4588     default:
4589       if (IS_CUSTOM_ELEMENT(element))
4590       {
4591         struct ElementInfo *ei = &element_info[element];
4592         int move_direction_initial = ei->move_direction_initial;
4593         int move_pattern = ei->move_pattern;
4594
4595         if (move_direction_initial == MV_START_PREVIOUS)
4596         {
4597           if (MovDir[x][y] != MV_NONE)
4598             return;
4599
4600           move_direction_initial = MV_START_AUTOMATIC;
4601         }
4602
4603         if (move_direction_initial == MV_START_RANDOM)
4604           MovDir[x][y] = 1 << RND(4);
4605         else if (move_direction_initial & MV_ANY_DIRECTION)
4606           MovDir[x][y] = move_direction_initial;
4607         else if (move_pattern == MV_ALL_DIRECTIONS ||
4608                  move_pattern == MV_TURNING_LEFT ||
4609                  move_pattern == MV_TURNING_RIGHT ||
4610                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4611                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4612                  move_pattern == MV_TURNING_RANDOM)
4613           MovDir[x][y] = 1 << RND(4);
4614         else if (move_pattern == MV_HORIZONTAL)
4615           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4616         else if (move_pattern == MV_VERTICAL)
4617           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4618         else if (move_pattern & MV_ANY_DIRECTION)
4619           MovDir[x][y] = element_info[element].move_pattern;
4620         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4621                  move_pattern == MV_ALONG_RIGHT_SIDE)
4622         {
4623           // use random direction as default start direction
4624           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4625             MovDir[x][y] = 1 << RND(4);
4626
4627           for (i = 0; i < NUM_DIRECTIONS; i++)
4628           {
4629             int x1 = x + xy[i][0];
4630             int y1 = y + xy[i][1];
4631
4632             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4633             {
4634               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4635                 MovDir[x][y] = direction[0][i];
4636               else
4637                 MovDir[x][y] = direction[1][i];
4638
4639               break;
4640             }
4641           }
4642         }                
4643       }
4644       else
4645       {
4646         MovDir[x][y] = 1 << RND(4);
4647
4648         if (element != EL_BUG &&
4649             element != EL_SPACESHIP &&
4650             element != EL_BD_BUTTERFLY &&
4651             element != EL_BD_FIREFLY)
4652           break;
4653
4654         for (i = 0; i < NUM_DIRECTIONS; i++)
4655         {
4656           int x1 = x + xy[i][0];
4657           int y1 = y + xy[i][1];
4658
4659           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4660           {
4661             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4662             {
4663               MovDir[x][y] = direction[0][i];
4664               break;
4665             }
4666             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4667                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4668             {
4669               MovDir[x][y] = direction[1][i];
4670               break;
4671             }
4672           }
4673         }
4674       }
4675       break;
4676   }
4677
4678   GfxDir[x][y] = MovDir[x][y];
4679 }
4680
4681 void InitAmoebaNr(int x, int y)
4682 {
4683   int i;
4684   int group_nr = AmoebaNeighbourNr(x, y);
4685
4686   if (group_nr == 0)
4687   {
4688     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4689     {
4690       if (AmoebaCnt[i] == 0)
4691       {
4692         group_nr = i;
4693         break;
4694       }
4695     }
4696   }
4697
4698   AmoebaNr[x][y] = group_nr;
4699   AmoebaCnt[group_nr]++;
4700   AmoebaCnt2[group_nr]++;
4701 }
4702
4703 static void LevelSolved(void)
4704 {
4705   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4706       game.players_still_needed > 0)
4707     return;
4708
4709   game.LevelSolved = TRUE;
4710   game.GameOver = TRUE;
4711 }
4712
4713 void GameWon(void)
4714 {
4715   static int time_count_steps;
4716   static int time, time_final;
4717   static float score, score_final; // needed for time score < 10 for 10 seconds
4718   static int health, health_final;
4719   static int game_over_delay_1 = 0;
4720   static int game_over_delay_2 = 0;
4721   static int game_over_delay_3 = 0;
4722   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4723   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4724
4725   if (!game.LevelSolved_GameWon)
4726   {
4727     int i;
4728
4729     // do not start end game actions before the player stops moving (to exit)
4730     if (local_player->active && local_player->MovPos)
4731       return;
4732
4733     game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4734     game.score_time_final = (level.use_step_counter ? TimePlayed :
4735                              TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4736
4737     game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4738                         game_em.lev->score :
4739                         level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4740                         game_mm.score :
4741                         game.score);
4742
4743     game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4744                          MM_HEALTH(game_mm.laser_overload_value) :
4745                          game.health);
4746
4747     game.LevelSolved_CountingTime = game.time_final;
4748     game.LevelSolved_CountingScore = game.score_final;
4749     game.LevelSolved_CountingHealth = game.health_final;
4750
4751     game.LevelSolved_GameWon = TRUE;
4752     game.LevelSolved_SaveTape = tape.recording;
4753     game.LevelSolved_SaveScore = !tape.playing;
4754
4755     if (!tape.playing)
4756     {
4757       LevelStats_incSolved(level_nr);
4758
4759       SaveLevelSetup_SeriesInfo();
4760     }
4761
4762     if (tape.auto_play)         // tape might already be stopped here
4763       tape.auto_play_level_solved = TRUE;
4764
4765     TapeStop();
4766
4767     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4768     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4769     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4770
4771     time = time_final = game.time_final;
4772     score = score_final = game.score_final;
4773     health = health_final = game.health_final;
4774
4775     if (time_score > 0)
4776     {
4777       int time_final_max = 999;
4778       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4779       int time_frames = 0;
4780       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4781       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4782
4783       if (TimeLeft > 0)
4784       {
4785         time_final = 0;
4786         time_frames = time_frames_left;
4787       }
4788       else if (game.no_time_limit && TimePlayed < time_final_max)
4789       {
4790         time_final = time_final_max;
4791         time_frames = time_frames_final_max - time_frames_played;
4792       }
4793
4794       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4795
4796       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4797
4798       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4799       {
4800         health_final = 0;
4801         score_final += health * time_score;
4802       }
4803
4804       game.score_final = score_final;
4805       game.health_final = health_final;
4806     }
4807
4808     if (level_editor_test_game || !setup.count_score_after_game)
4809     {
4810       time = time_final;
4811       score = score_final;
4812
4813       game.LevelSolved_CountingTime = time;
4814       game.LevelSolved_CountingScore = score;
4815
4816       game_panel_controls[GAME_PANEL_TIME].value = time;
4817       game_panel_controls[GAME_PANEL_SCORE].value = score;
4818
4819       DisplayGameControlValues();
4820     }
4821
4822     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4823     {
4824       // check if last player has left the level
4825       if (game.exit_x >= 0 &&
4826           game.exit_y >= 0)
4827       {
4828         int x = game.exit_x;
4829         int y = game.exit_y;
4830         int element = Tile[x][y];
4831
4832         // close exit door after last player
4833         if ((game.all_players_gone &&
4834              (element == EL_EXIT_OPEN ||
4835               element == EL_SP_EXIT_OPEN ||
4836               element == EL_STEEL_EXIT_OPEN)) ||
4837             element == EL_EM_EXIT_OPEN ||
4838             element == EL_EM_STEEL_EXIT_OPEN)
4839         {
4840
4841           Tile[x][y] =
4842             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4843              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4844              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4845              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4846              EL_EM_STEEL_EXIT_CLOSING);
4847
4848           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4849         }
4850
4851         // player disappears
4852         DrawLevelField(x, y);
4853       }
4854
4855       for (i = 0; i < MAX_PLAYERS; i++)
4856       {
4857         struct PlayerInfo *player = &stored_player[i];
4858
4859         if (player->present)
4860         {
4861           RemovePlayer(player);
4862
4863           // player disappears
4864           DrawLevelField(player->jx, player->jy);
4865         }
4866       }
4867     }
4868
4869     PlaySound(SND_GAME_WINNING);
4870   }
4871
4872   if (setup.count_score_after_game)
4873   {
4874     if (time != time_final)
4875     {
4876       if (game_over_delay_1 > 0)
4877       {
4878         game_over_delay_1--;
4879
4880         return;
4881       }
4882
4883       int time_to_go = ABS(time_final - time);
4884       int time_count_dir = (time < time_final ? +1 : -1);
4885
4886       if (time_to_go < time_count_steps)
4887         time_count_steps = 1;
4888
4889       time  += time_count_steps * time_count_dir;
4890       score += time_count_steps * time_score;
4891
4892       // set final score to correct rounding differences after counting score
4893       if (time == time_final)
4894         score = score_final;
4895
4896       game.LevelSolved_CountingTime = time;
4897       game.LevelSolved_CountingScore = score;
4898
4899       game_panel_controls[GAME_PANEL_TIME].value = time;
4900       game_panel_controls[GAME_PANEL_SCORE].value = score;
4901
4902       DisplayGameControlValues();
4903
4904       if (time == time_final)
4905         StopSound(SND_GAME_LEVELTIME_BONUS);
4906       else if (setup.sound_loops)
4907         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4908       else
4909         PlaySound(SND_GAME_LEVELTIME_BONUS);
4910
4911       return;
4912     }
4913
4914     if (health != health_final)
4915     {
4916       if (game_over_delay_2 > 0)
4917       {
4918         game_over_delay_2--;
4919
4920         return;
4921       }
4922
4923       int health_count_dir = (health < health_final ? +1 : -1);
4924
4925       health += health_count_dir;
4926       score  += time_score;
4927
4928       game.LevelSolved_CountingHealth = health;
4929       game.LevelSolved_CountingScore = score;
4930
4931       game_panel_controls[GAME_PANEL_HEALTH].value = health;
4932       game_panel_controls[GAME_PANEL_SCORE].value = score;
4933
4934       DisplayGameControlValues();
4935
4936       if (health == health_final)
4937         StopSound(SND_GAME_LEVELTIME_BONUS);
4938       else if (setup.sound_loops)
4939         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4940       else
4941         PlaySound(SND_GAME_LEVELTIME_BONUS);
4942
4943       return;
4944     }
4945   }
4946
4947   game.panel.active = FALSE;
4948
4949   if (game_over_delay_3 > 0)
4950   {
4951     game_over_delay_3--;
4952
4953     return;
4954   }
4955
4956   GameEnd();
4957 }
4958
4959 void GameEnd(void)
4960 {
4961   // used instead of "level_nr" (needed for network games)
4962   int last_level_nr = levelset.level_nr;
4963
4964   game.LevelSolved_GameEnd = TRUE;
4965
4966   if (game.LevelSolved_SaveTape)
4967   {
4968     // make sure that request dialog to save tape does not open door again
4969     if (!global.use_envelope_request)
4970       CloseDoor(DOOR_CLOSE_1);
4971
4972     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4973
4974     // set unique basename for score tape (also saved in high score table)
4975     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
4976   }
4977
4978   // if no tape is to be saved, close both doors simultaneously
4979   CloseDoor(DOOR_CLOSE_ALL);
4980
4981   if (level_editor_test_game)
4982   {
4983     SetGameStatus(GAME_MODE_MAIN);
4984
4985     DrawMainMenu();
4986
4987     return;
4988   }
4989
4990   if (!game.LevelSolved_SaveScore)
4991   {
4992     SetGameStatus(GAME_MODE_MAIN);
4993
4994     DrawMainMenu();
4995
4996     return;
4997   }
4998
4999   if (level_nr == leveldir_current->handicap_level)
5000   {
5001     leveldir_current->handicap_level++;
5002
5003     SaveLevelSetup_SeriesInfo();
5004   }
5005
5006   // save score and score tape before potentially erasing tape below
5007   NewHighScore(last_level_nr);
5008
5009   if (setup.increment_levels &&
5010       level_nr < leveldir_current->last_level &&
5011       !network_playing)
5012   {
5013     level_nr++;         // advance to next level
5014     TapeErase();        // start with empty tape
5015
5016     if (setup.auto_play_next_level)
5017     {
5018       LoadLevel(level_nr);
5019
5020       SaveLevelSetup_SeriesInfo();
5021     }
5022   }
5023
5024   if (scores.last_added >= 0 && setup.show_scores_after_game)
5025   {
5026     SetGameStatus(GAME_MODE_SCORES);
5027
5028     DrawHallOfFame(last_level_nr);
5029   }
5030   else if (setup.auto_play_next_level && setup.increment_levels &&
5031            last_level_nr < leveldir_current->last_level &&
5032            !network_playing)
5033   {
5034     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5035   }
5036   else
5037   {
5038     SetGameStatus(GAME_MODE_MAIN);
5039
5040     DrawMainMenu();
5041   }
5042 }
5043
5044 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5045                          boolean one_score_entry_per_name)
5046 {
5047   int i;
5048
5049   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5050     return -1;
5051
5052   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5053   {
5054     struct ScoreEntry *entry = &list->entry[i];
5055     boolean score_is_better = (new_entry->score >  entry->score);
5056     boolean score_is_equal  = (new_entry->score == entry->score);
5057     boolean time_is_better  = (new_entry->time  <  entry->time);
5058     boolean time_is_equal   = (new_entry->time  == entry->time);
5059     boolean better_by_score = (score_is_better ||
5060                                (score_is_equal && time_is_better));
5061     boolean better_by_time  = (time_is_better ||
5062                                (time_is_equal && score_is_better));
5063     boolean is_better = (level.rate_time_over_score ? better_by_time :
5064                          better_by_score);
5065     boolean entry_is_empty = (entry->score == 0 &&
5066                               entry->time == 0);
5067
5068     // prevent adding server score entries if also existing in local score file
5069     // (special case: historic score entries have an empty tape basename entry)
5070     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5071         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5072       return -1;
5073
5074     if (is_better || entry_is_empty)
5075     {
5076       // player has made it to the hall of fame
5077
5078       if (i < MAX_SCORE_ENTRIES - 1)
5079       {
5080         int m = MAX_SCORE_ENTRIES - 1;
5081         int l;
5082
5083         if (one_score_entry_per_name)
5084         {
5085           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5086             if (strEqual(list->entry[l].name, new_entry->name))
5087               m = l;
5088
5089           if (m == i)   // player's new highscore overwrites his old one
5090             goto put_into_list;
5091         }
5092
5093         for (l = m; l > i; l--)
5094           list->entry[l] = list->entry[l - 1];
5095       }
5096
5097       put_into_list:
5098
5099       *entry = *new_entry;
5100
5101       return i;
5102     }
5103     else if (one_score_entry_per_name &&
5104              strEqual(entry->name, new_entry->name))
5105     {
5106       // player already in high score list with better score or time
5107
5108       return -1;
5109     }
5110   }
5111
5112   return -1;
5113 }
5114
5115 void NewHighScore(int level_nr)
5116 {
5117   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5118   boolean one_per_name = !program.many_scores_per_name;
5119
5120   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5121   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5122
5123   new_entry.score = game.score_final;
5124   new_entry.time = game.score_time_final;
5125
5126   LoadScore(level_nr);
5127
5128   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5129
5130   if (scores.last_added >= 0)
5131   {
5132     SaveScore(level_nr);
5133
5134     if (game.LevelSolved_SaveTape)
5135     {
5136       SaveScoreTape(level_nr);
5137       SaveServerScore(level_nr);
5138     }
5139
5140     // store last added local score entry (before merging server scores)
5141     scores.last_added_local = scores.last_added;
5142   }
5143 }
5144
5145 void MergeServerScore(void)
5146 {
5147   boolean one_per_name = !program.many_scores_per_name;
5148   int i;
5149
5150   for (i = 0; i < server_scores.num_entries; i++)
5151   {
5152     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5153
5154     if (pos >= 0 && pos <= scores.last_added)
5155       scores.last_added++;
5156   }
5157
5158   if (scores.last_added >= MAX_SCORE_ENTRIES)
5159     scores.last_added = -1;
5160 }
5161
5162 static int getElementMoveStepsizeExt(int x, int y, int direction)
5163 {
5164   int element = Tile[x][y];
5165   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5166   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5167   int horiz_move = (dx != 0);
5168   int sign = (horiz_move ? dx : dy);
5169   int step = sign * element_info[element].move_stepsize;
5170
5171   // special values for move stepsize for spring and things on conveyor belt
5172   if (horiz_move)
5173   {
5174     if (CAN_FALL(element) &&
5175         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5176       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5177     else if (element == EL_SPRING)
5178       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5179   }
5180
5181   return step;
5182 }
5183
5184 static int getElementMoveStepsize(int x, int y)
5185 {
5186   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5187 }
5188
5189 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5190 {
5191   if (player->GfxAction != action || player->GfxDir != dir)
5192   {
5193     player->GfxAction = action;
5194     player->GfxDir = dir;
5195     player->Frame = 0;
5196     player->StepFrame = 0;
5197   }
5198 }
5199
5200 static void ResetGfxFrame(int x, int y)
5201 {
5202   // profiling showed that "autotest" spends 10~20% of its time in this function
5203   if (DrawingDeactivatedField())
5204     return;
5205
5206   int element = Tile[x][y];
5207   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5208
5209   if (graphic_info[graphic].anim_global_sync)
5210     GfxFrame[x][y] = FrameCounter;
5211   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5212     GfxFrame[x][y] = CustomValue[x][y];
5213   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5214     GfxFrame[x][y] = element_info[element].collect_score;
5215   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5216     GfxFrame[x][y] = ChangeDelay[x][y];
5217 }
5218
5219 static void ResetGfxAnimation(int x, int y)
5220 {
5221   GfxAction[x][y] = ACTION_DEFAULT;
5222   GfxDir[x][y] = MovDir[x][y];
5223   GfxFrame[x][y] = 0;
5224
5225   ResetGfxFrame(x, y);
5226 }
5227
5228 static void ResetRandomAnimationValue(int x, int y)
5229 {
5230   GfxRandom[x][y] = INIT_GFX_RANDOM();
5231 }
5232
5233 static void InitMovingField(int x, int y, int direction)
5234 {
5235   int element = Tile[x][y];
5236   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5237   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5238   int newx = x + dx;
5239   int newy = y + dy;
5240   boolean is_moving_before, is_moving_after;
5241
5242   // check if element was/is moving or being moved before/after mode change
5243   is_moving_before = (WasJustMoving[x][y] != 0);
5244   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5245
5246   // reset animation only for moving elements which change direction of moving
5247   // or which just started or stopped moving
5248   // (else CEs with property "can move" / "not moving" are reset each frame)
5249   if (is_moving_before != is_moving_after ||
5250       direction != MovDir[x][y])
5251     ResetGfxAnimation(x, y);
5252
5253   MovDir[x][y] = direction;
5254   GfxDir[x][y] = direction;
5255
5256   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5257                      direction == MV_DOWN && CAN_FALL(element) ?
5258                      ACTION_FALLING : ACTION_MOVING);
5259
5260   // this is needed for CEs with property "can move" / "not moving"
5261
5262   if (is_moving_after)
5263   {
5264     if (Tile[newx][newy] == EL_EMPTY)
5265       Tile[newx][newy] = EL_BLOCKED;
5266
5267     MovDir[newx][newy] = MovDir[x][y];
5268
5269     CustomValue[newx][newy] = CustomValue[x][y];
5270
5271     GfxFrame[newx][newy] = GfxFrame[x][y];
5272     GfxRandom[newx][newy] = GfxRandom[x][y];
5273     GfxAction[newx][newy] = GfxAction[x][y];
5274     GfxDir[newx][newy] = GfxDir[x][y];
5275   }
5276 }
5277
5278 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5279 {
5280   int direction = MovDir[x][y];
5281   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5282   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5283
5284   *goes_to_x = newx;
5285   *goes_to_y = newy;
5286 }
5287
5288 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5289 {
5290   int oldx = x, oldy = y;
5291   int direction = MovDir[x][y];
5292
5293   if (direction == MV_LEFT)
5294     oldx++;
5295   else if (direction == MV_RIGHT)
5296     oldx--;
5297   else if (direction == MV_UP)
5298     oldy++;
5299   else if (direction == MV_DOWN)
5300     oldy--;
5301
5302   *comes_from_x = oldx;
5303   *comes_from_y = oldy;
5304 }
5305
5306 static int MovingOrBlocked2Element(int x, int y)
5307 {
5308   int element = Tile[x][y];
5309
5310   if (element == EL_BLOCKED)
5311   {
5312     int oldx, oldy;
5313
5314     Blocked2Moving(x, y, &oldx, &oldy);
5315     return Tile[oldx][oldy];
5316   }
5317   else
5318     return element;
5319 }
5320
5321 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5322 {
5323   // like MovingOrBlocked2Element(), but if element is moving
5324   // and (x,y) is the field the moving element is just leaving,
5325   // return EL_BLOCKED instead of the element value
5326   int element = Tile[x][y];
5327
5328   if (IS_MOVING(x, y))
5329   {
5330     if (element == EL_BLOCKED)
5331     {
5332       int oldx, oldy;
5333
5334       Blocked2Moving(x, y, &oldx, &oldy);
5335       return Tile[oldx][oldy];
5336     }
5337     else
5338       return EL_BLOCKED;
5339   }
5340   else
5341     return element;
5342 }
5343
5344 static void RemoveField(int x, int y)
5345 {
5346   Tile[x][y] = EL_EMPTY;
5347
5348   MovPos[x][y] = 0;
5349   MovDir[x][y] = 0;
5350   MovDelay[x][y] = 0;
5351
5352   CustomValue[x][y] = 0;
5353
5354   AmoebaNr[x][y] = 0;
5355   ChangeDelay[x][y] = 0;
5356   ChangePage[x][y] = -1;
5357   Pushed[x][y] = FALSE;
5358
5359   GfxElement[x][y] = EL_UNDEFINED;
5360   GfxAction[x][y] = ACTION_DEFAULT;
5361   GfxDir[x][y] = MV_NONE;
5362 }
5363
5364 static void RemoveMovingField(int x, int y)
5365 {
5366   int oldx = x, oldy = y, newx = x, newy = y;
5367   int element = Tile[x][y];
5368   int next_element = EL_UNDEFINED;
5369
5370   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5371     return;
5372
5373   if (IS_MOVING(x, y))
5374   {
5375     Moving2Blocked(x, y, &newx, &newy);
5376
5377     if (Tile[newx][newy] != EL_BLOCKED)
5378     {
5379       // element is moving, but target field is not free (blocked), but
5380       // already occupied by something different (example: acid pool);
5381       // in this case, only remove the moving field, but not the target
5382
5383       RemoveField(oldx, oldy);
5384
5385       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5386
5387       TEST_DrawLevelField(oldx, oldy);
5388
5389       return;
5390     }
5391   }
5392   else if (element == EL_BLOCKED)
5393   {
5394     Blocked2Moving(x, y, &oldx, &oldy);
5395     if (!IS_MOVING(oldx, oldy))
5396       return;
5397   }
5398
5399   if (element == EL_BLOCKED &&
5400       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5401        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5402        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5403        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5404        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5405        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5406     next_element = get_next_element(Tile[oldx][oldy]);
5407
5408   RemoveField(oldx, oldy);
5409   RemoveField(newx, newy);
5410
5411   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5412
5413   if (next_element != EL_UNDEFINED)
5414     Tile[oldx][oldy] = next_element;
5415
5416   TEST_DrawLevelField(oldx, oldy);
5417   TEST_DrawLevelField(newx, newy);
5418 }
5419
5420 void DrawDynamite(int x, int y)
5421 {
5422   int sx = SCREENX(x), sy = SCREENY(y);
5423   int graphic = el2img(Tile[x][y]);
5424   int frame;
5425
5426   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5427     return;
5428
5429   if (IS_WALKABLE_INSIDE(Back[x][y]))
5430     return;
5431
5432   if (Back[x][y])
5433     DrawLevelElement(x, y, Back[x][y]);
5434   else if (Store[x][y])
5435     DrawLevelElement(x, y, Store[x][y]);
5436   else if (game.use_masked_elements)
5437     DrawLevelElement(x, y, EL_EMPTY);
5438
5439   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5440
5441   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5442     DrawGraphicThruMask(sx, sy, graphic, frame);
5443   else
5444     DrawGraphic(sx, sy, graphic, frame);
5445 }
5446
5447 static void CheckDynamite(int x, int y)
5448 {
5449   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5450   {
5451     MovDelay[x][y]--;
5452
5453     if (MovDelay[x][y] != 0)
5454     {
5455       DrawDynamite(x, y);
5456       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5457
5458       return;
5459     }
5460   }
5461
5462   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5463
5464   Bang(x, y);
5465 }
5466
5467 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5468 {
5469   boolean num_checked_players = 0;
5470   int i;
5471
5472   for (i = 0; i < MAX_PLAYERS; i++)
5473   {
5474     if (stored_player[i].active)
5475     {
5476       int sx = stored_player[i].jx;
5477       int sy = stored_player[i].jy;
5478
5479       if (num_checked_players == 0)
5480       {
5481         *sx1 = *sx2 = sx;
5482         *sy1 = *sy2 = sy;
5483       }
5484       else
5485       {
5486         *sx1 = MIN(*sx1, sx);
5487         *sy1 = MIN(*sy1, sy);
5488         *sx2 = MAX(*sx2, sx);
5489         *sy2 = MAX(*sy2, sy);
5490       }
5491
5492       num_checked_players++;
5493     }
5494   }
5495 }
5496
5497 static boolean checkIfAllPlayersFitToScreen_RND(void)
5498 {
5499   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5500
5501   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5502
5503   return (sx2 - sx1 < SCR_FIELDX &&
5504           sy2 - sy1 < SCR_FIELDY);
5505 }
5506
5507 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5508 {
5509   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5510
5511   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5512
5513   *sx = (sx1 + sx2) / 2;
5514   *sy = (sy1 + sy2) / 2;
5515 }
5516
5517 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5518                                boolean center_screen, boolean quick_relocation)
5519 {
5520   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5521   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5522   boolean no_delay = (tape.warp_forward);
5523   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5524   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5525   int new_scroll_x, new_scroll_y;
5526
5527   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5528   {
5529     // case 1: quick relocation inside visible screen (without scrolling)
5530
5531     RedrawPlayfield();
5532
5533     return;
5534   }
5535
5536   if (!level.shifted_relocation || center_screen)
5537   {
5538     // relocation _with_ centering of screen
5539
5540     new_scroll_x = SCROLL_POSITION_X(x);
5541     new_scroll_y = SCROLL_POSITION_Y(y);
5542   }
5543   else
5544   {
5545     // relocation _without_ centering of screen
5546
5547     int center_scroll_x = SCROLL_POSITION_X(old_x);
5548     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5549     int offset_x = x + (scroll_x - center_scroll_x);
5550     int offset_y = y + (scroll_y - center_scroll_y);
5551
5552     // for new screen position, apply previous offset to center position
5553     new_scroll_x = SCROLL_POSITION_X(offset_x);
5554     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5555   }
5556
5557   if (quick_relocation)
5558   {
5559     // case 2: quick relocation (redraw without visible scrolling)
5560
5561     scroll_x = new_scroll_x;
5562     scroll_y = new_scroll_y;
5563
5564     RedrawPlayfield();
5565
5566     return;
5567   }
5568
5569   // case 3: visible relocation (with scrolling to new position)
5570
5571   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5572
5573   SetVideoFrameDelay(wait_delay_value);
5574
5575   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5576   {
5577     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5578     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5579
5580     if (dx == 0 && dy == 0)             // no scrolling needed at all
5581       break;
5582
5583     scroll_x -= dx;
5584     scroll_y -= dy;
5585
5586     // set values for horizontal/vertical screen scrolling (half tile size)
5587     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5588     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5589     int pos_x = dx * TILEX / 2;
5590     int pos_y = dy * TILEY / 2;
5591     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5592     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5593
5594     ScrollLevel(dx, dy);
5595     DrawAllPlayers();
5596
5597     // scroll in two steps of half tile size to make things smoother
5598     BlitScreenToBitmapExt_RND(window, fx, fy);
5599
5600     // scroll second step to align at full tile size
5601     BlitScreenToBitmap(window);
5602   }
5603
5604   DrawAllPlayers();
5605   BackToFront();
5606
5607   SetVideoFrameDelay(frame_delay_value_old);
5608 }
5609
5610 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5611 {
5612   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5613   int player_nr = GET_PLAYER_NR(el_player);
5614   struct PlayerInfo *player = &stored_player[player_nr];
5615   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5616   boolean no_delay = (tape.warp_forward);
5617   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5618   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5619   int old_jx = player->jx;
5620   int old_jy = player->jy;
5621   int old_element = Tile[old_jx][old_jy];
5622   int element = Tile[jx][jy];
5623   boolean player_relocated = (old_jx != jx || old_jy != jy);
5624
5625   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5626   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5627   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5628   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5629   int leave_side_horiz = move_dir_horiz;
5630   int leave_side_vert  = move_dir_vert;
5631   int enter_side = enter_side_horiz | enter_side_vert;
5632   int leave_side = leave_side_horiz | leave_side_vert;
5633
5634   if (player->buried)           // do not reanimate dead player
5635     return;
5636
5637   if (!player_relocated)        // no need to relocate the player
5638     return;
5639
5640   if (IS_PLAYER(jx, jy))        // player already placed at new position
5641   {
5642     RemoveField(jx, jy);        // temporarily remove newly placed player
5643     DrawLevelField(jx, jy);
5644   }
5645
5646   if (player->present)
5647   {
5648     while (player->MovPos)
5649     {
5650       ScrollPlayer(player, SCROLL_GO_ON);
5651       ScrollScreen(NULL, SCROLL_GO_ON);
5652
5653       AdvanceFrameAndPlayerCounters(player->index_nr);
5654
5655       DrawPlayer(player);
5656
5657       BackToFront_WithFrameDelay(wait_delay_value);
5658     }
5659
5660     DrawPlayer(player);         // needed here only to cleanup last field
5661     DrawLevelField(player->jx, player->jy);     // remove player graphic
5662
5663     player->is_moving = FALSE;
5664   }
5665
5666   if (IS_CUSTOM_ELEMENT(old_element))
5667     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5668                                CE_LEFT_BY_PLAYER,
5669                                player->index_bit, leave_side);
5670
5671   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5672                                       CE_PLAYER_LEAVES_X,
5673                                       player->index_bit, leave_side);
5674
5675   Tile[jx][jy] = el_player;
5676   InitPlayerField(jx, jy, el_player, TRUE);
5677
5678   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5679      possible that the relocation target field did not contain a player element,
5680      but a walkable element, to which the new player was relocated -- in this
5681      case, restore that (already initialized!) element on the player field */
5682   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5683   {
5684     Tile[jx][jy] = element;     // restore previously existing element
5685   }
5686
5687   // only visually relocate centered player
5688   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5689                      FALSE, level.instant_relocation);
5690
5691   TestIfPlayerTouchesBadThing(jx, jy);
5692   TestIfPlayerTouchesCustomElement(jx, jy);
5693
5694   if (IS_CUSTOM_ELEMENT(element))
5695     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5696                                player->index_bit, enter_side);
5697
5698   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5699                                       player->index_bit, enter_side);
5700
5701   if (player->is_switching)
5702   {
5703     /* ensure that relocation while still switching an element does not cause
5704        a new element to be treated as also switched directly after relocation
5705        (this is important for teleporter switches that teleport the player to
5706        a place where another teleporter switch is in the same direction, which
5707        would then incorrectly be treated as immediately switched before the
5708        direction key that caused the switch was released) */
5709
5710     player->switch_x += jx - old_jx;
5711     player->switch_y += jy - old_jy;
5712   }
5713 }
5714
5715 static void Explode(int ex, int ey, int phase, int mode)
5716 {
5717   int x, y;
5718   int last_phase;
5719   int border_element;
5720
5721   // !!! eliminate this variable !!!
5722   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5723
5724   if (game.explosions_delayed)
5725   {
5726     ExplodeField[ex][ey] = mode;
5727     return;
5728   }
5729
5730   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5731   {
5732     int center_element = Tile[ex][ey];
5733     int artwork_element, explosion_element;     // set these values later
5734
5735     // remove things displayed in background while burning dynamite
5736     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5737       Back[ex][ey] = 0;
5738
5739     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5740     {
5741       // put moving element to center field (and let it explode there)
5742       center_element = MovingOrBlocked2Element(ex, ey);
5743       RemoveMovingField(ex, ey);
5744       Tile[ex][ey] = center_element;
5745     }
5746
5747     // now "center_element" is finally determined -- set related values now
5748     artwork_element = center_element;           // for custom player artwork
5749     explosion_element = center_element;         // for custom player artwork
5750
5751     if (IS_PLAYER(ex, ey))
5752     {
5753       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5754
5755       artwork_element = stored_player[player_nr].artwork_element;
5756
5757       if (level.use_explosion_element[player_nr])
5758       {
5759         explosion_element = level.explosion_element[player_nr];
5760         artwork_element = explosion_element;
5761       }
5762     }
5763
5764     if (mode == EX_TYPE_NORMAL ||
5765         mode == EX_TYPE_CENTER ||
5766         mode == EX_TYPE_CROSS)
5767       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5768
5769     last_phase = element_info[explosion_element].explosion_delay + 1;
5770
5771     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5772     {
5773       int xx = x - ex + 1;
5774       int yy = y - ey + 1;
5775       int element;
5776
5777       if (!IN_LEV_FIELD(x, y) ||
5778           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5779           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5780         continue;
5781
5782       element = Tile[x][y];
5783
5784       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5785       {
5786         element = MovingOrBlocked2Element(x, y);
5787
5788         if (!IS_EXPLOSION_PROOF(element))
5789           RemoveMovingField(x, y);
5790       }
5791
5792       // indestructible elements can only explode in center (but not flames)
5793       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5794                                            mode == EX_TYPE_BORDER)) ||
5795           element == EL_FLAMES)
5796         continue;
5797
5798       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5799          behaviour, for example when touching a yamyam that explodes to rocks
5800          with active deadly shield, a rock is created under the player !!! */
5801       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5802 #if 0
5803       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5804           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5805            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5806 #else
5807       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5808 #endif
5809       {
5810         if (IS_ACTIVE_BOMB(element))
5811         {
5812           // re-activate things under the bomb like gate or penguin
5813           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5814           Back[x][y] = 0;
5815         }
5816
5817         continue;
5818       }
5819
5820       // save walkable background elements while explosion on same tile
5821       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5822           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5823         Back[x][y] = element;
5824
5825       // ignite explodable elements reached by other explosion
5826       if (element == EL_EXPLOSION)
5827         element = Store2[x][y];
5828
5829       if (AmoebaNr[x][y] &&
5830           (element == EL_AMOEBA_FULL ||
5831            element == EL_BD_AMOEBA ||
5832            element == EL_AMOEBA_GROWING))
5833       {
5834         AmoebaCnt[AmoebaNr[x][y]]--;
5835         AmoebaCnt2[AmoebaNr[x][y]]--;
5836       }
5837
5838       RemoveField(x, y);
5839
5840       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5841       {
5842         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5843
5844         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5845
5846         if (PLAYERINFO(ex, ey)->use_murphy)
5847           Store[x][y] = EL_EMPTY;
5848       }
5849
5850       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5851       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5852       else if (ELEM_IS_PLAYER(center_element))
5853         Store[x][y] = EL_EMPTY;
5854       else if (center_element == EL_YAMYAM)
5855         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5856       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5857         Store[x][y] = element_info[center_element].content.e[xx][yy];
5858 #if 1
5859       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5860       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5861       // otherwise) -- FIX THIS !!!
5862       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5863         Store[x][y] = element_info[element].content.e[1][1];
5864 #else
5865       else if (!CAN_EXPLODE(element))
5866         Store[x][y] = element_info[element].content.e[1][1];
5867 #endif
5868       else
5869         Store[x][y] = EL_EMPTY;
5870
5871       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5872           center_element == EL_AMOEBA_TO_DIAMOND)
5873         Store2[x][y] = element;
5874
5875       Tile[x][y] = EL_EXPLOSION;
5876       GfxElement[x][y] = artwork_element;
5877
5878       ExplodePhase[x][y] = 1;
5879       ExplodeDelay[x][y] = last_phase;
5880
5881       Stop[x][y] = TRUE;
5882     }
5883
5884     if (center_element == EL_YAMYAM)
5885       game.yamyam_content_nr =
5886         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5887
5888     return;
5889   }
5890
5891   if (Stop[ex][ey])
5892     return;
5893
5894   x = ex;
5895   y = ey;
5896
5897   if (phase == 1)
5898     GfxFrame[x][y] = 0;         // restart explosion animation
5899
5900   last_phase = ExplodeDelay[x][y];
5901
5902   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5903
5904   // this can happen if the player leaves an explosion just in time
5905   if (GfxElement[x][y] == EL_UNDEFINED)
5906     GfxElement[x][y] = EL_EMPTY;
5907
5908   border_element = Store2[x][y];
5909   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5910     border_element = StorePlayer[x][y];
5911
5912   if (phase == element_info[border_element].ignition_delay ||
5913       phase == last_phase)
5914   {
5915     boolean border_explosion = FALSE;
5916
5917     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5918         !PLAYER_EXPLOSION_PROTECTED(x, y))
5919     {
5920       KillPlayerUnlessExplosionProtected(x, y);
5921       border_explosion = TRUE;
5922     }
5923     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5924     {
5925       Tile[x][y] = Store2[x][y];
5926       Store2[x][y] = 0;
5927       Bang(x, y);
5928       border_explosion = TRUE;
5929     }
5930     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5931     {
5932       AmoebaToDiamond(x, y);
5933       Store2[x][y] = 0;
5934       border_explosion = TRUE;
5935     }
5936
5937     // if an element just explodes due to another explosion (chain-reaction),
5938     // do not immediately end the new explosion when it was the last frame of
5939     // the explosion (as it would be done in the following "if"-statement!)
5940     if (border_explosion && phase == last_phase)
5941       return;
5942   }
5943
5944   if (phase == last_phase)
5945   {
5946     int element;
5947
5948     element = Tile[x][y] = Store[x][y];
5949     Store[x][y] = Store2[x][y] = 0;
5950     GfxElement[x][y] = EL_UNDEFINED;
5951
5952     // player can escape from explosions and might therefore be still alive
5953     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5954         element <= EL_PLAYER_IS_EXPLODING_4)
5955     {
5956       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5957       int explosion_element = EL_PLAYER_1 + player_nr;
5958       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5959       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5960
5961       if (level.use_explosion_element[player_nr])
5962         explosion_element = level.explosion_element[player_nr];
5963
5964       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5965                     element_info[explosion_element].content.e[xx][yy]);
5966     }
5967
5968     // restore probably existing indestructible background element
5969     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5970       element = Tile[x][y] = Back[x][y];
5971     Back[x][y] = 0;
5972
5973     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5974     GfxDir[x][y] = MV_NONE;
5975     ChangeDelay[x][y] = 0;
5976     ChangePage[x][y] = -1;
5977
5978     CustomValue[x][y] = 0;
5979
5980     InitField_WithBug2(x, y, FALSE);
5981
5982     TEST_DrawLevelField(x, y);
5983
5984     TestIfElementTouchesCustomElement(x, y);
5985
5986     if (GFX_CRUMBLED(element))
5987       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5988
5989     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5990       StorePlayer[x][y] = 0;
5991
5992     if (ELEM_IS_PLAYER(element))
5993       RelocatePlayer(x, y, element);
5994   }
5995   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5996   {
5997     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5998     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5999
6000     if (phase == delay)
6001       TEST_DrawLevelFieldCrumbled(x, y);
6002
6003     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6004     {
6005       DrawLevelElement(x, y, Back[x][y]);
6006       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6007     }
6008     else if (IS_WALKABLE_UNDER(Back[x][y]))
6009     {
6010       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6011       DrawLevelElementThruMask(x, y, Back[x][y]);
6012     }
6013     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6014       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6015   }
6016 }
6017
6018 static void DynaExplode(int ex, int ey)
6019 {
6020   int i, j;
6021   int dynabomb_element = Tile[ex][ey];
6022   int dynabomb_size = 1;
6023   boolean dynabomb_xl = FALSE;
6024   struct PlayerInfo *player;
6025   static int xy[4][2] =
6026   {
6027     { 0, -1 },
6028     { -1, 0 },
6029     { +1, 0 },
6030     { 0, +1 }
6031   };
6032
6033   if (IS_ACTIVE_BOMB(dynabomb_element))
6034   {
6035     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6036     dynabomb_size = player->dynabomb_size;
6037     dynabomb_xl = player->dynabomb_xl;
6038     player->dynabombs_left++;
6039   }
6040
6041   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6042
6043   for (i = 0; i < NUM_DIRECTIONS; i++)
6044   {
6045     for (j = 1; j <= dynabomb_size; j++)
6046     {
6047       int x = ex + j * xy[i][0];
6048       int y = ey + j * xy[i][1];
6049       int element;
6050
6051       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6052         break;
6053
6054       element = Tile[x][y];
6055
6056       // do not restart explosions of fields with active bombs
6057       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6058         continue;
6059
6060       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6061
6062       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6063           !IS_DIGGABLE(element) && !dynabomb_xl)
6064         break;
6065     }
6066   }
6067 }
6068
6069 void Bang(int x, int y)
6070 {
6071   int element = MovingOrBlocked2Element(x, y);
6072   int explosion_type = EX_TYPE_NORMAL;
6073
6074   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6075   {
6076     struct PlayerInfo *player = PLAYERINFO(x, y);
6077
6078     element = Tile[x][y] = player->initial_element;
6079
6080     if (level.use_explosion_element[player->index_nr])
6081     {
6082       int explosion_element = level.explosion_element[player->index_nr];
6083
6084       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6085         explosion_type = EX_TYPE_CROSS;
6086       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6087         explosion_type = EX_TYPE_CENTER;
6088     }
6089   }
6090
6091   switch (element)
6092   {
6093     case EL_BUG:
6094     case EL_SPACESHIP:
6095     case EL_BD_BUTTERFLY:
6096     case EL_BD_FIREFLY:
6097     case EL_YAMYAM:
6098     case EL_DARK_YAMYAM:
6099     case EL_ROBOT:
6100     case EL_PACMAN:
6101     case EL_MOLE:
6102       RaiseScoreElement(element);
6103       break;
6104
6105     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6106     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6107     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6108     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6109     case EL_DYNABOMB_INCREASE_NUMBER:
6110     case EL_DYNABOMB_INCREASE_SIZE:
6111     case EL_DYNABOMB_INCREASE_POWER:
6112       explosion_type = EX_TYPE_DYNA;
6113       break;
6114
6115     case EL_DC_LANDMINE:
6116       explosion_type = EX_TYPE_CENTER;
6117       break;
6118
6119     case EL_PENGUIN:
6120     case EL_LAMP:
6121     case EL_LAMP_ACTIVE:
6122     case EL_AMOEBA_TO_DIAMOND:
6123       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6124         explosion_type = EX_TYPE_CENTER;
6125       break;
6126
6127     default:
6128       if (element_info[element].explosion_type == EXPLODES_CROSS)
6129         explosion_type = EX_TYPE_CROSS;
6130       else if (element_info[element].explosion_type == EXPLODES_1X1)
6131         explosion_type = EX_TYPE_CENTER;
6132       break;
6133   }
6134
6135   if (explosion_type == EX_TYPE_DYNA)
6136     DynaExplode(x, y);
6137   else
6138     Explode(x, y, EX_PHASE_START, explosion_type);
6139
6140   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6141 }
6142
6143 static void SplashAcid(int x, int y)
6144 {
6145   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6146       (!IN_LEV_FIELD(x - 1, y - 2) ||
6147        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6148     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6149
6150   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6151       (!IN_LEV_FIELD(x + 1, y - 2) ||
6152        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6153     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6154
6155   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6156 }
6157
6158 static void InitBeltMovement(void)
6159 {
6160   static int belt_base_element[4] =
6161   {
6162     EL_CONVEYOR_BELT_1_LEFT,
6163     EL_CONVEYOR_BELT_2_LEFT,
6164     EL_CONVEYOR_BELT_3_LEFT,
6165     EL_CONVEYOR_BELT_4_LEFT
6166   };
6167   static int belt_base_active_element[4] =
6168   {
6169     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6170     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6171     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6172     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6173   };
6174
6175   int x, y, i, j;
6176
6177   // set frame order for belt animation graphic according to belt direction
6178   for (i = 0; i < NUM_BELTS; i++)
6179   {
6180     int belt_nr = i;
6181
6182     for (j = 0; j < NUM_BELT_PARTS; j++)
6183     {
6184       int element = belt_base_active_element[belt_nr] + j;
6185       int graphic_1 = el2img(element);
6186       int graphic_2 = el2panelimg(element);
6187
6188       if (game.belt_dir[i] == MV_LEFT)
6189       {
6190         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6191         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6192       }
6193       else
6194       {
6195         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6196         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6197       }
6198     }
6199   }
6200
6201   SCAN_PLAYFIELD(x, y)
6202   {
6203     int element = Tile[x][y];
6204
6205     for (i = 0; i < NUM_BELTS; i++)
6206     {
6207       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6208       {
6209         int e_belt_nr = getBeltNrFromBeltElement(element);
6210         int belt_nr = i;
6211
6212         if (e_belt_nr == belt_nr)
6213         {
6214           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6215
6216           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6217         }
6218       }
6219     }
6220   }
6221 }
6222
6223 static void ToggleBeltSwitch(int x, int y)
6224 {
6225   static int belt_base_element[4] =
6226   {
6227     EL_CONVEYOR_BELT_1_LEFT,
6228     EL_CONVEYOR_BELT_2_LEFT,
6229     EL_CONVEYOR_BELT_3_LEFT,
6230     EL_CONVEYOR_BELT_4_LEFT
6231   };
6232   static int belt_base_active_element[4] =
6233   {
6234     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6235     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6236     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6237     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6238   };
6239   static int belt_base_switch_element[4] =
6240   {
6241     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6242     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6243     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6244     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6245   };
6246   static int belt_move_dir[4] =
6247   {
6248     MV_LEFT,
6249     MV_NONE,
6250     MV_RIGHT,
6251     MV_NONE,
6252   };
6253
6254   int element = Tile[x][y];
6255   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6256   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6257   int belt_dir = belt_move_dir[belt_dir_nr];
6258   int xx, yy, i;
6259
6260   if (!IS_BELT_SWITCH(element))
6261     return;
6262
6263   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6264   game.belt_dir[belt_nr] = belt_dir;
6265
6266   if (belt_dir_nr == 3)
6267     belt_dir_nr = 1;
6268
6269   // set frame order for belt animation graphic according to belt direction
6270   for (i = 0; i < NUM_BELT_PARTS; i++)
6271   {
6272     int element = belt_base_active_element[belt_nr] + i;
6273     int graphic_1 = el2img(element);
6274     int graphic_2 = el2panelimg(element);
6275
6276     if (belt_dir == MV_LEFT)
6277     {
6278       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6279       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6280     }
6281     else
6282     {
6283       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6284       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6285     }
6286   }
6287
6288   SCAN_PLAYFIELD(xx, yy)
6289   {
6290     int element = Tile[xx][yy];
6291
6292     if (IS_BELT_SWITCH(element))
6293     {
6294       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6295
6296       if (e_belt_nr == belt_nr)
6297       {
6298         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6299         TEST_DrawLevelField(xx, yy);
6300       }
6301     }
6302     else if (IS_BELT(element) && belt_dir != MV_NONE)
6303     {
6304       int e_belt_nr = getBeltNrFromBeltElement(element);
6305
6306       if (e_belt_nr == belt_nr)
6307       {
6308         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6309
6310         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6311         TEST_DrawLevelField(xx, yy);
6312       }
6313     }
6314     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6315     {
6316       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6317
6318       if (e_belt_nr == belt_nr)
6319       {
6320         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6321
6322         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6323         TEST_DrawLevelField(xx, yy);
6324       }
6325     }
6326   }
6327 }
6328
6329 static void ToggleSwitchgateSwitch(int x, int y)
6330 {
6331   int xx, yy;
6332
6333   game.switchgate_pos = !game.switchgate_pos;
6334
6335   SCAN_PLAYFIELD(xx, yy)
6336   {
6337     int element = Tile[xx][yy];
6338
6339     if (element == EL_SWITCHGATE_SWITCH_UP)
6340     {
6341       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6342       TEST_DrawLevelField(xx, yy);
6343     }
6344     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6345     {
6346       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6347       TEST_DrawLevelField(xx, yy);
6348     }
6349     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6350     {
6351       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6352       TEST_DrawLevelField(xx, yy);
6353     }
6354     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6355     {
6356       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6357       TEST_DrawLevelField(xx, yy);
6358     }
6359     else if (element == EL_SWITCHGATE_OPEN ||
6360              element == EL_SWITCHGATE_OPENING)
6361     {
6362       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6363
6364       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6365     }
6366     else if (element == EL_SWITCHGATE_CLOSED ||
6367              element == EL_SWITCHGATE_CLOSING)
6368     {
6369       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6370
6371       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6372     }
6373   }
6374 }
6375
6376 static int getInvisibleActiveFromInvisibleElement(int element)
6377 {
6378   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6379           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6380           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6381           element);
6382 }
6383
6384 static int getInvisibleFromInvisibleActiveElement(int element)
6385 {
6386   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6387           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6388           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6389           element);
6390 }
6391
6392 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6393 {
6394   int x, y;
6395
6396   SCAN_PLAYFIELD(x, y)
6397   {
6398     int element = Tile[x][y];
6399
6400     if (element == EL_LIGHT_SWITCH &&
6401         game.light_time_left > 0)
6402     {
6403       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6404       TEST_DrawLevelField(x, y);
6405     }
6406     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6407              game.light_time_left == 0)
6408     {
6409       Tile[x][y] = EL_LIGHT_SWITCH;
6410       TEST_DrawLevelField(x, y);
6411     }
6412     else if (element == EL_EMC_DRIPPER &&
6413              game.light_time_left > 0)
6414     {
6415       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6416       TEST_DrawLevelField(x, y);
6417     }
6418     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6419              game.light_time_left == 0)
6420     {
6421       Tile[x][y] = EL_EMC_DRIPPER;
6422       TEST_DrawLevelField(x, y);
6423     }
6424     else if (element == EL_INVISIBLE_STEELWALL ||
6425              element == EL_INVISIBLE_WALL ||
6426              element == EL_INVISIBLE_SAND)
6427     {
6428       if (game.light_time_left > 0)
6429         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6430
6431       TEST_DrawLevelField(x, y);
6432
6433       // uncrumble neighbour fields, if needed
6434       if (element == EL_INVISIBLE_SAND)
6435         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6436     }
6437     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6438              element == EL_INVISIBLE_WALL_ACTIVE ||
6439              element == EL_INVISIBLE_SAND_ACTIVE)
6440     {
6441       if (game.light_time_left == 0)
6442         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6443
6444       TEST_DrawLevelField(x, y);
6445
6446       // re-crumble neighbour fields, if needed
6447       if (element == EL_INVISIBLE_SAND)
6448         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6449     }
6450   }
6451 }
6452
6453 static void RedrawAllInvisibleElementsForLenses(void)
6454 {
6455   int x, y;
6456
6457   SCAN_PLAYFIELD(x, y)
6458   {
6459     int element = Tile[x][y];
6460
6461     if (element == EL_EMC_DRIPPER &&
6462         game.lenses_time_left > 0)
6463     {
6464       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6465       TEST_DrawLevelField(x, y);
6466     }
6467     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6468              game.lenses_time_left == 0)
6469     {
6470       Tile[x][y] = EL_EMC_DRIPPER;
6471       TEST_DrawLevelField(x, y);
6472     }
6473     else if (element == EL_INVISIBLE_STEELWALL ||
6474              element == EL_INVISIBLE_WALL ||
6475              element == EL_INVISIBLE_SAND)
6476     {
6477       if (game.lenses_time_left > 0)
6478         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6479
6480       TEST_DrawLevelField(x, y);
6481
6482       // uncrumble neighbour fields, if needed
6483       if (element == EL_INVISIBLE_SAND)
6484         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6485     }
6486     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6487              element == EL_INVISIBLE_WALL_ACTIVE ||
6488              element == EL_INVISIBLE_SAND_ACTIVE)
6489     {
6490       if (game.lenses_time_left == 0)
6491         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6492
6493       TEST_DrawLevelField(x, y);
6494
6495       // re-crumble neighbour fields, if needed
6496       if (element == EL_INVISIBLE_SAND)
6497         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6498     }
6499   }
6500 }
6501
6502 static void RedrawAllInvisibleElementsForMagnifier(void)
6503 {
6504   int x, y;
6505
6506   SCAN_PLAYFIELD(x, y)
6507   {
6508     int element = Tile[x][y];
6509
6510     if (element == EL_EMC_FAKE_GRASS &&
6511         game.magnify_time_left > 0)
6512     {
6513       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6514       TEST_DrawLevelField(x, y);
6515     }
6516     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6517              game.magnify_time_left == 0)
6518     {
6519       Tile[x][y] = EL_EMC_FAKE_GRASS;
6520       TEST_DrawLevelField(x, y);
6521     }
6522     else if (IS_GATE_GRAY(element) &&
6523              game.magnify_time_left > 0)
6524     {
6525       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6526                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6527                     IS_EM_GATE_GRAY(element) ?
6528                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6529                     IS_EMC_GATE_GRAY(element) ?
6530                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6531                     IS_DC_GATE_GRAY(element) ?
6532                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6533                     element);
6534       TEST_DrawLevelField(x, y);
6535     }
6536     else if (IS_GATE_GRAY_ACTIVE(element) &&
6537              game.magnify_time_left == 0)
6538     {
6539       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6540                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6541                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6542                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6543                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6544                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6545                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6546                     EL_DC_GATE_WHITE_GRAY :
6547                     element);
6548       TEST_DrawLevelField(x, y);
6549     }
6550   }
6551 }
6552
6553 static void ToggleLightSwitch(int x, int y)
6554 {
6555   int element = Tile[x][y];
6556
6557   game.light_time_left =
6558     (element == EL_LIGHT_SWITCH ?
6559      level.time_light * FRAMES_PER_SECOND : 0);
6560
6561   RedrawAllLightSwitchesAndInvisibleElements();
6562 }
6563
6564 static void ActivateTimegateSwitch(int x, int y)
6565 {
6566   int xx, yy;
6567
6568   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6569
6570   SCAN_PLAYFIELD(xx, yy)
6571   {
6572     int element = Tile[xx][yy];
6573
6574     if (element == EL_TIMEGATE_CLOSED ||
6575         element == EL_TIMEGATE_CLOSING)
6576     {
6577       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6578       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6579     }
6580
6581     /*
6582     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6583     {
6584       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6585       TEST_DrawLevelField(xx, yy);
6586     }
6587     */
6588
6589   }
6590
6591   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6592                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6593 }
6594
6595 static void Impact(int x, int y)
6596 {
6597   boolean last_line = (y == lev_fieldy - 1);
6598   boolean object_hit = FALSE;
6599   boolean impact = (last_line || object_hit);
6600   int element = Tile[x][y];
6601   int smashed = EL_STEELWALL;
6602
6603   if (!last_line)       // check if element below was hit
6604   {
6605     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6606       return;
6607
6608     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6609                                          MovDir[x][y + 1] != MV_DOWN ||
6610                                          MovPos[x][y + 1] <= TILEY / 2));
6611
6612     // do not smash moving elements that left the smashed field in time
6613     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6614         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6615       object_hit = FALSE;
6616
6617 #if USE_QUICKSAND_IMPACT_BUGFIX
6618     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6619     {
6620       RemoveMovingField(x, y + 1);
6621       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6622       Tile[x][y + 2] = EL_ROCK;
6623       TEST_DrawLevelField(x, y + 2);
6624
6625       object_hit = TRUE;
6626     }
6627
6628     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6629     {
6630       RemoveMovingField(x, y + 1);
6631       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6632       Tile[x][y + 2] = EL_ROCK;
6633       TEST_DrawLevelField(x, y + 2);
6634
6635       object_hit = TRUE;
6636     }
6637 #endif
6638
6639     if (object_hit)
6640       smashed = MovingOrBlocked2Element(x, y + 1);
6641
6642     impact = (last_line || object_hit);
6643   }
6644
6645   if (!last_line && smashed == EL_ACID) // element falls into acid
6646   {
6647     SplashAcid(x, y + 1);
6648     return;
6649   }
6650
6651   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6652   // only reset graphic animation if graphic really changes after impact
6653   if (impact &&
6654       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6655   {
6656     ResetGfxAnimation(x, y);
6657     TEST_DrawLevelField(x, y);
6658   }
6659
6660   if (impact && CAN_EXPLODE_IMPACT(element))
6661   {
6662     Bang(x, y);
6663     return;
6664   }
6665   else if (impact && element == EL_PEARL &&
6666            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6667   {
6668     ResetGfxAnimation(x, y);
6669
6670     Tile[x][y] = EL_PEARL_BREAKING;
6671     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6672     return;
6673   }
6674   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6675   {
6676     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6677
6678     return;
6679   }
6680
6681   if (impact && element == EL_AMOEBA_DROP)
6682   {
6683     if (object_hit && IS_PLAYER(x, y + 1))
6684       KillPlayerUnlessEnemyProtected(x, y + 1);
6685     else if (object_hit && smashed == EL_PENGUIN)
6686       Bang(x, y + 1);
6687     else
6688     {
6689       Tile[x][y] = EL_AMOEBA_GROWING;
6690       Store[x][y] = EL_AMOEBA_WET;
6691
6692       ResetRandomAnimationValue(x, y);
6693     }
6694     return;
6695   }
6696
6697   if (object_hit)               // check which object was hit
6698   {
6699     if ((CAN_PASS_MAGIC_WALL(element) && 
6700          (smashed == EL_MAGIC_WALL ||
6701           smashed == EL_BD_MAGIC_WALL)) ||
6702         (CAN_PASS_DC_MAGIC_WALL(element) &&
6703          smashed == EL_DC_MAGIC_WALL))
6704     {
6705       int xx, yy;
6706       int activated_magic_wall =
6707         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6708          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6709          EL_DC_MAGIC_WALL_ACTIVE);
6710
6711       // activate magic wall / mill
6712       SCAN_PLAYFIELD(xx, yy)
6713       {
6714         if (Tile[xx][yy] == smashed)
6715           Tile[xx][yy] = activated_magic_wall;
6716       }
6717
6718       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6719       game.magic_wall_active = TRUE;
6720
6721       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6722                             SND_MAGIC_WALL_ACTIVATING :
6723                             smashed == EL_BD_MAGIC_WALL ?
6724                             SND_BD_MAGIC_WALL_ACTIVATING :
6725                             SND_DC_MAGIC_WALL_ACTIVATING));
6726     }
6727
6728     if (IS_PLAYER(x, y + 1))
6729     {
6730       if (CAN_SMASH_PLAYER(element))
6731       {
6732         KillPlayerUnlessEnemyProtected(x, y + 1);
6733         return;
6734       }
6735     }
6736     else if (smashed == EL_PENGUIN)
6737     {
6738       if (CAN_SMASH_PLAYER(element))
6739       {
6740         Bang(x, y + 1);
6741         return;
6742       }
6743     }
6744     else if (element == EL_BD_DIAMOND)
6745     {
6746       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6747       {
6748         Bang(x, y + 1);
6749         return;
6750       }
6751     }
6752     else if (((element == EL_SP_INFOTRON ||
6753                element == EL_SP_ZONK) &&
6754               (smashed == EL_SP_SNIKSNAK ||
6755                smashed == EL_SP_ELECTRON ||
6756                smashed == EL_SP_DISK_ORANGE)) ||
6757              (element == EL_SP_INFOTRON &&
6758               smashed == EL_SP_DISK_YELLOW))
6759     {
6760       Bang(x, y + 1);
6761       return;
6762     }
6763     else if (CAN_SMASH_EVERYTHING(element))
6764     {
6765       if (IS_CLASSIC_ENEMY(smashed) ||
6766           CAN_EXPLODE_SMASHED(smashed))
6767       {
6768         Bang(x, y + 1);
6769         return;
6770       }
6771       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6772       {
6773         if (smashed == EL_LAMP ||
6774             smashed == EL_LAMP_ACTIVE)
6775         {
6776           Bang(x, y + 1);
6777           return;
6778         }
6779         else if (smashed == EL_NUT)
6780         {
6781           Tile[x][y + 1] = EL_NUT_BREAKING;
6782           PlayLevelSound(x, y, SND_NUT_BREAKING);
6783           RaiseScoreElement(EL_NUT);
6784           return;
6785         }
6786         else if (smashed == EL_PEARL)
6787         {
6788           ResetGfxAnimation(x, y);
6789
6790           Tile[x][y + 1] = EL_PEARL_BREAKING;
6791           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6792           return;
6793         }
6794         else if (smashed == EL_DIAMOND)
6795         {
6796           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6797           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6798           return;
6799         }
6800         else if (IS_BELT_SWITCH(smashed))
6801         {
6802           ToggleBeltSwitch(x, y + 1);
6803         }
6804         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6805                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6806                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6807                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6808         {
6809           ToggleSwitchgateSwitch(x, y + 1);
6810         }
6811         else if (smashed == EL_LIGHT_SWITCH ||
6812                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6813         {
6814           ToggleLightSwitch(x, y + 1);
6815         }
6816         else
6817         {
6818           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6819
6820           CheckElementChangeBySide(x, y + 1, smashed, element,
6821                                    CE_SWITCHED, CH_SIDE_TOP);
6822           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6823                                             CH_SIDE_TOP);
6824         }
6825       }
6826       else
6827       {
6828         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6829       }
6830     }
6831   }
6832
6833   // play sound of magic wall / mill
6834   if (!last_line &&
6835       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6836        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6837        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6838   {
6839     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6840       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6841     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6842       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6843     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6844       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6845
6846     return;
6847   }
6848
6849   // play sound of object that hits the ground
6850   if (last_line || object_hit)
6851     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6852 }
6853
6854 static void TurnRoundExt(int x, int y)
6855 {
6856   static struct
6857   {
6858     int dx, dy;
6859   } move_xy[] =
6860   {
6861     {  0,  0 },
6862     { -1,  0 },
6863     { +1,  0 },
6864     {  0,  0 },
6865     {  0, -1 },
6866     {  0,  0 }, { 0, 0 }, { 0, 0 },
6867     {  0, +1 }
6868   };
6869   static struct
6870   {
6871     int left, right, back;
6872   } turn[] =
6873   {
6874     { 0,        0,              0        },
6875     { MV_DOWN,  MV_UP,          MV_RIGHT },
6876     { MV_UP,    MV_DOWN,        MV_LEFT  },
6877     { 0,        0,              0        },
6878     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6879     { 0,        0,              0        },
6880     { 0,        0,              0        },
6881     { 0,        0,              0        },
6882     { MV_RIGHT, MV_LEFT,        MV_UP    }
6883   };
6884
6885   int element = Tile[x][y];
6886   int move_pattern = element_info[element].move_pattern;
6887
6888   int old_move_dir = MovDir[x][y];
6889   int left_dir  = turn[old_move_dir].left;
6890   int right_dir = turn[old_move_dir].right;
6891   int back_dir  = turn[old_move_dir].back;
6892
6893   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6894   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6895   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6896   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6897
6898   int left_x  = x + left_dx,  left_y  = y + left_dy;
6899   int right_x = x + right_dx, right_y = y + right_dy;
6900   int move_x  = x + move_dx,  move_y  = y + move_dy;
6901
6902   int xx, yy;
6903
6904   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6905   {
6906     TestIfBadThingTouchesOtherBadThing(x, y);
6907
6908     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6909       MovDir[x][y] = right_dir;
6910     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6911       MovDir[x][y] = left_dir;
6912
6913     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6914       MovDelay[x][y] = 9;
6915     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6916       MovDelay[x][y] = 1;
6917   }
6918   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6919   {
6920     TestIfBadThingTouchesOtherBadThing(x, y);
6921
6922     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6923       MovDir[x][y] = left_dir;
6924     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6925       MovDir[x][y] = right_dir;
6926
6927     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6928       MovDelay[x][y] = 9;
6929     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6930       MovDelay[x][y] = 1;
6931   }
6932   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6933   {
6934     TestIfBadThingTouchesOtherBadThing(x, y);
6935
6936     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6937       MovDir[x][y] = left_dir;
6938     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6939       MovDir[x][y] = right_dir;
6940
6941     if (MovDir[x][y] != old_move_dir)
6942       MovDelay[x][y] = 9;
6943   }
6944   else if (element == EL_YAMYAM)
6945   {
6946     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6947     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6948
6949     if (can_turn_left && can_turn_right)
6950       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6951     else if (can_turn_left)
6952       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6953     else if (can_turn_right)
6954       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6955     else
6956       MovDir[x][y] = back_dir;
6957
6958     MovDelay[x][y] = 16 + 16 * RND(3);
6959   }
6960   else if (element == EL_DARK_YAMYAM)
6961   {
6962     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6963                                                          left_x, left_y);
6964     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6965                                                          right_x, right_y);
6966
6967     if (can_turn_left && can_turn_right)
6968       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6969     else if (can_turn_left)
6970       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6971     else if (can_turn_right)
6972       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6973     else
6974       MovDir[x][y] = back_dir;
6975
6976     MovDelay[x][y] = 16 + 16 * RND(3);
6977   }
6978   else if (element == EL_PACMAN)
6979   {
6980     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6981     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6982
6983     if (can_turn_left && can_turn_right)
6984       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6985     else if (can_turn_left)
6986       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6987     else if (can_turn_right)
6988       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6989     else
6990       MovDir[x][y] = back_dir;
6991
6992     MovDelay[x][y] = 6 + RND(40);
6993   }
6994   else if (element == EL_PIG)
6995   {
6996     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6997     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6998     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6999     boolean should_turn_left, should_turn_right, should_move_on;
7000     int rnd_value = 24;
7001     int rnd = RND(rnd_value);
7002
7003     should_turn_left = (can_turn_left &&
7004                         (!can_move_on ||
7005                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7006                                                    y + back_dy + left_dy)));
7007     should_turn_right = (can_turn_right &&
7008                          (!can_move_on ||
7009                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7010                                                     y + back_dy + right_dy)));
7011     should_move_on = (can_move_on &&
7012                       (!can_turn_left ||
7013                        !can_turn_right ||
7014                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7015                                                  y + move_dy + left_dy) ||
7016                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7017                                                  y + move_dy + right_dy)));
7018
7019     if (should_turn_left || should_turn_right || should_move_on)
7020     {
7021       if (should_turn_left && should_turn_right && should_move_on)
7022         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7023                         rnd < 2 * rnd_value / 3 ? right_dir :
7024                         old_move_dir);
7025       else if (should_turn_left && should_turn_right)
7026         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7027       else if (should_turn_left && should_move_on)
7028         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7029       else if (should_turn_right && should_move_on)
7030         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7031       else if (should_turn_left)
7032         MovDir[x][y] = left_dir;
7033       else if (should_turn_right)
7034         MovDir[x][y] = right_dir;
7035       else if (should_move_on)
7036         MovDir[x][y] = old_move_dir;
7037     }
7038     else if (can_move_on && rnd > rnd_value / 8)
7039       MovDir[x][y] = old_move_dir;
7040     else if (can_turn_left && can_turn_right)
7041       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7042     else if (can_turn_left && rnd > rnd_value / 8)
7043       MovDir[x][y] = left_dir;
7044     else if (can_turn_right && rnd > rnd_value/8)
7045       MovDir[x][y] = right_dir;
7046     else
7047       MovDir[x][y] = back_dir;
7048
7049     xx = x + move_xy[MovDir[x][y]].dx;
7050     yy = y + move_xy[MovDir[x][y]].dy;
7051
7052     if (!IN_LEV_FIELD(xx, yy) ||
7053         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7054       MovDir[x][y] = old_move_dir;
7055
7056     MovDelay[x][y] = 0;
7057   }
7058   else if (element == EL_DRAGON)
7059   {
7060     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7061     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7062     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7063     int rnd_value = 24;
7064     int rnd = RND(rnd_value);
7065
7066     if (can_move_on && rnd > rnd_value / 8)
7067       MovDir[x][y] = old_move_dir;
7068     else if (can_turn_left && can_turn_right)
7069       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7070     else if (can_turn_left && rnd > rnd_value / 8)
7071       MovDir[x][y] = left_dir;
7072     else if (can_turn_right && rnd > rnd_value / 8)
7073       MovDir[x][y] = right_dir;
7074     else
7075       MovDir[x][y] = back_dir;
7076
7077     xx = x + move_xy[MovDir[x][y]].dx;
7078     yy = y + move_xy[MovDir[x][y]].dy;
7079
7080     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7081       MovDir[x][y] = old_move_dir;
7082
7083     MovDelay[x][y] = 0;
7084   }
7085   else if (element == EL_MOLE)
7086   {
7087     boolean can_move_on =
7088       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7089                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7090                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7091     if (!can_move_on)
7092     {
7093       boolean can_turn_left =
7094         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7095                               IS_AMOEBOID(Tile[left_x][left_y])));
7096
7097       boolean can_turn_right =
7098         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7099                               IS_AMOEBOID(Tile[right_x][right_y])));
7100
7101       if (can_turn_left && can_turn_right)
7102         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7103       else if (can_turn_left)
7104         MovDir[x][y] = left_dir;
7105       else
7106         MovDir[x][y] = right_dir;
7107     }
7108
7109     if (MovDir[x][y] != old_move_dir)
7110       MovDelay[x][y] = 9;
7111   }
7112   else if (element == EL_BALLOON)
7113   {
7114     MovDir[x][y] = game.wind_direction;
7115     MovDelay[x][y] = 0;
7116   }
7117   else if (element == EL_SPRING)
7118   {
7119     if (MovDir[x][y] & MV_HORIZONTAL)
7120     {
7121       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7122           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7123       {
7124         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7125         ResetGfxAnimation(move_x, move_y);
7126         TEST_DrawLevelField(move_x, move_y);
7127
7128         MovDir[x][y] = back_dir;
7129       }
7130       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7131                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7132         MovDir[x][y] = MV_NONE;
7133     }
7134
7135     MovDelay[x][y] = 0;
7136   }
7137   else if (element == EL_ROBOT ||
7138            element == EL_SATELLITE ||
7139            element == EL_PENGUIN ||
7140            element == EL_EMC_ANDROID)
7141   {
7142     int attr_x = -1, attr_y = -1;
7143
7144     if (game.all_players_gone)
7145     {
7146       attr_x = game.exit_x;
7147       attr_y = game.exit_y;
7148     }
7149     else
7150     {
7151       int i;
7152
7153       for (i = 0; i < MAX_PLAYERS; i++)
7154       {
7155         struct PlayerInfo *player = &stored_player[i];
7156         int jx = player->jx, jy = player->jy;
7157
7158         if (!player->active)
7159           continue;
7160
7161         if (attr_x == -1 ||
7162             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7163         {
7164           attr_x = jx;
7165           attr_y = jy;
7166         }
7167       }
7168     }
7169
7170     if (element == EL_ROBOT &&
7171         game.robot_wheel_x >= 0 &&
7172         game.robot_wheel_y >= 0 &&
7173         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7174          game.engine_version < VERSION_IDENT(3,1,0,0)))
7175     {
7176       attr_x = game.robot_wheel_x;
7177       attr_y = game.robot_wheel_y;
7178     }
7179
7180     if (element == EL_PENGUIN)
7181     {
7182       int i;
7183       static int xy[4][2] =
7184       {
7185         { 0, -1 },
7186         { -1, 0 },
7187         { +1, 0 },
7188         { 0, +1 }
7189       };
7190
7191       for (i = 0; i < NUM_DIRECTIONS; i++)
7192       {
7193         int ex = x + xy[i][0];
7194         int ey = y + xy[i][1];
7195
7196         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7197                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7198                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7199                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7200         {
7201           attr_x = ex;
7202           attr_y = ey;
7203           break;
7204         }
7205       }
7206     }
7207
7208     MovDir[x][y] = MV_NONE;
7209     if (attr_x < x)
7210       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7211     else if (attr_x > x)
7212       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7213     if (attr_y < y)
7214       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7215     else if (attr_y > y)
7216       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7217
7218     if (element == EL_ROBOT)
7219     {
7220       int newx, newy;
7221
7222       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7223         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7224       Moving2Blocked(x, y, &newx, &newy);
7225
7226       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7227         MovDelay[x][y] = 8 + 8 * !RND(3);
7228       else
7229         MovDelay[x][y] = 16;
7230     }
7231     else if (element == EL_PENGUIN)
7232     {
7233       int newx, newy;
7234
7235       MovDelay[x][y] = 1;
7236
7237       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7238       {
7239         boolean first_horiz = RND(2);
7240         int new_move_dir = MovDir[x][y];
7241
7242         MovDir[x][y] =
7243           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7244         Moving2Blocked(x, y, &newx, &newy);
7245
7246         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7247           return;
7248
7249         MovDir[x][y] =
7250           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7251         Moving2Blocked(x, y, &newx, &newy);
7252
7253         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7254           return;
7255
7256         MovDir[x][y] = old_move_dir;
7257         return;
7258       }
7259     }
7260     else if (element == EL_SATELLITE)
7261     {
7262       int newx, newy;
7263
7264       MovDelay[x][y] = 1;
7265
7266       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7267       {
7268         boolean first_horiz = RND(2);
7269         int new_move_dir = MovDir[x][y];
7270
7271         MovDir[x][y] =
7272           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7273         Moving2Blocked(x, y, &newx, &newy);
7274
7275         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7276           return;
7277
7278         MovDir[x][y] =
7279           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7280         Moving2Blocked(x, y, &newx, &newy);
7281
7282         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7283           return;
7284
7285         MovDir[x][y] = old_move_dir;
7286         return;
7287       }
7288     }
7289     else if (element == EL_EMC_ANDROID)
7290     {
7291       static int check_pos[16] =
7292       {
7293         -1,             //  0 => (invalid)
7294         7,              //  1 => MV_LEFT
7295         3,              //  2 => MV_RIGHT
7296         -1,             //  3 => (invalid)
7297         1,              //  4 =>            MV_UP
7298         0,              //  5 => MV_LEFT  | MV_UP
7299         2,              //  6 => MV_RIGHT | MV_UP
7300         -1,             //  7 => (invalid)
7301         5,              //  8 =>            MV_DOWN
7302         6,              //  9 => MV_LEFT  | MV_DOWN
7303         4,              // 10 => MV_RIGHT | MV_DOWN
7304         -1,             // 11 => (invalid)
7305         -1,             // 12 => (invalid)
7306         -1,             // 13 => (invalid)
7307         -1,             // 14 => (invalid)
7308         -1,             // 15 => (invalid)
7309       };
7310       static struct
7311       {
7312         int dx, dy;
7313         int dir;
7314       } check_xy[8] =
7315       {
7316         { -1, -1,       MV_LEFT  | MV_UP   },
7317         {  0, -1,                  MV_UP   },
7318         { +1, -1,       MV_RIGHT | MV_UP   },
7319         { +1,  0,       MV_RIGHT           },
7320         { +1, +1,       MV_RIGHT | MV_DOWN },
7321         {  0, +1,                  MV_DOWN },
7322         { -1, +1,       MV_LEFT  | MV_DOWN },
7323         { -1,  0,       MV_LEFT            },
7324       };
7325       int start_pos, check_order;
7326       boolean can_clone = FALSE;
7327       int i;
7328
7329       // check if there is any free field around current position
7330       for (i = 0; i < 8; i++)
7331       {
7332         int newx = x + check_xy[i].dx;
7333         int newy = y + check_xy[i].dy;
7334
7335         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7336         {
7337           can_clone = TRUE;
7338
7339           break;
7340         }
7341       }
7342
7343       if (can_clone)            // randomly find an element to clone
7344       {
7345         can_clone = FALSE;
7346
7347         start_pos = check_pos[RND(8)];
7348         check_order = (RND(2) ? -1 : +1);
7349
7350         for (i = 0; i < 8; i++)
7351         {
7352           int pos_raw = start_pos + i * check_order;
7353           int pos = (pos_raw + 8) % 8;
7354           int newx = x + check_xy[pos].dx;
7355           int newy = y + check_xy[pos].dy;
7356
7357           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7358           {
7359             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7360             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7361
7362             Store[x][y] = Tile[newx][newy];
7363
7364             can_clone = TRUE;
7365
7366             break;
7367           }
7368         }
7369       }
7370
7371       if (can_clone)            // randomly find a direction to move
7372       {
7373         can_clone = FALSE;
7374
7375         start_pos = check_pos[RND(8)];
7376         check_order = (RND(2) ? -1 : +1);
7377
7378         for (i = 0; i < 8; i++)
7379         {
7380           int pos_raw = start_pos + i * check_order;
7381           int pos = (pos_raw + 8) % 8;
7382           int newx = x + check_xy[pos].dx;
7383           int newy = y + check_xy[pos].dy;
7384           int new_move_dir = check_xy[pos].dir;
7385
7386           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7387           {
7388             MovDir[x][y] = new_move_dir;
7389             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7390
7391             can_clone = TRUE;
7392
7393             break;
7394           }
7395         }
7396       }
7397
7398       if (can_clone)            // cloning and moving successful
7399         return;
7400
7401       // cannot clone -- try to move towards player
7402
7403       start_pos = check_pos[MovDir[x][y] & 0x0f];
7404       check_order = (RND(2) ? -1 : +1);
7405
7406       for (i = 0; i < 3; i++)
7407       {
7408         // first check start_pos, then previous/next or (next/previous) pos
7409         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7410         int pos = (pos_raw + 8) % 8;
7411         int newx = x + check_xy[pos].dx;
7412         int newy = y + check_xy[pos].dy;
7413         int new_move_dir = check_xy[pos].dir;
7414
7415         if (IS_PLAYER(newx, newy))
7416           break;
7417
7418         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7419         {
7420           MovDir[x][y] = new_move_dir;
7421           MovDelay[x][y] = level.android_move_time * 8 + 1;
7422
7423           break;
7424         }
7425       }
7426     }
7427   }
7428   else if (move_pattern == MV_TURNING_LEFT ||
7429            move_pattern == MV_TURNING_RIGHT ||
7430            move_pattern == MV_TURNING_LEFT_RIGHT ||
7431            move_pattern == MV_TURNING_RIGHT_LEFT ||
7432            move_pattern == MV_TURNING_RANDOM ||
7433            move_pattern == MV_ALL_DIRECTIONS)
7434   {
7435     boolean can_turn_left =
7436       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7437     boolean can_turn_right =
7438       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7439
7440     if (element_info[element].move_stepsize == 0)       // "not moving"
7441       return;
7442
7443     if (move_pattern == MV_TURNING_LEFT)
7444       MovDir[x][y] = left_dir;
7445     else if (move_pattern == MV_TURNING_RIGHT)
7446       MovDir[x][y] = right_dir;
7447     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7448       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7449     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7450       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7451     else if (move_pattern == MV_TURNING_RANDOM)
7452       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7453                       can_turn_right && !can_turn_left ? right_dir :
7454                       RND(2) ? left_dir : right_dir);
7455     else if (can_turn_left && can_turn_right)
7456       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7457     else if (can_turn_left)
7458       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7459     else if (can_turn_right)
7460       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7461     else
7462       MovDir[x][y] = back_dir;
7463
7464     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7465   }
7466   else if (move_pattern == MV_HORIZONTAL ||
7467            move_pattern == MV_VERTICAL)
7468   {
7469     if (move_pattern & old_move_dir)
7470       MovDir[x][y] = back_dir;
7471     else if (move_pattern == MV_HORIZONTAL)
7472       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7473     else if (move_pattern == MV_VERTICAL)
7474       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7475
7476     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7477   }
7478   else if (move_pattern & MV_ANY_DIRECTION)
7479   {
7480     MovDir[x][y] = move_pattern;
7481     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7482   }
7483   else if (move_pattern & MV_WIND_DIRECTION)
7484   {
7485     MovDir[x][y] = game.wind_direction;
7486     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7487   }
7488   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7489   {
7490     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7491       MovDir[x][y] = left_dir;
7492     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7493       MovDir[x][y] = right_dir;
7494
7495     if (MovDir[x][y] != old_move_dir)
7496       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7497   }
7498   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7499   {
7500     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7501       MovDir[x][y] = right_dir;
7502     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7503       MovDir[x][y] = left_dir;
7504
7505     if (MovDir[x][y] != old_move_dir)
7506       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7507   }
7508   else if (move_pattern == MV_TOWARDS_PLAYER ||
7509            move_pattern == MV_AWAY_FROM_PLAYER)
7510   {
7511     int attr_x = -1, attr_y = -1;
7512     int newx, newy;
7513     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7514
7515     if (game.all_players_gone)
7516     {
7517       attr_x = game.exit_x;
7518       attr_y = game.exit_y;
7519     }
7520     else
7521     {
7522       int i;
7523
7524       for (i = 0; i < MAX_PLAYERS; i++)
7525       {
7526         struct PlayerInfo *player = &stored_player[i];
7527         int jx = player->jx, jy = player->jy;
7528
7529         if (!player->active)
7530           continue;
7531
7532         if (attr_x == -1 ||
7533             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7534         {
7535           attr_x = jx;
7536           attr_y = jy;
7537         }
7538       }
7539     }
7540
7541     MovDir[x][y] = MV_NONE;
7542     if (attr_x < x)
7543       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7544     else if (attr_x > x)
7545       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7546     if (attr_y < y)
7547       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7548     else if (attr_y > y)
7549       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7550
7551     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7552
7553     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7554     {
7555       boolean first_horiz = RND(2);
7556       int new_move_dir = MovDir[x][y];
7557
7558       if (element_info[element].move_stepsize == 0)     // "not moving"
7559       {
7560         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7561         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7562
7563         return;
7564       }
7565
7566       MovDir[x][y] =
7567         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7568       Moving2Blocked(x, y, &newx, &newy);
7569
7570       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7571         return;
7572
7573       MovDir[x][y] =
7574         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7575       Moving2Blocked(x, y, &newx, &newy);
7576
7577       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7578         return;
7579
7580       MovDir[x][y] = old_move_dir;
7581     }
7582   }
7583   else if (move_pattern == MV_WHEN_PUSHED ||
7584            move_pattern == MV_WHEN_DROPPED)
7585   {
7586     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7587       MovDir[x][y] = MV_NONE;
7588
7589     MovDelay[x][y] = 0;
7590   }
7591   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7592   {
7593     static int test_xy[7][2] =
7594     {
7595       { 0, -1 },
7596       { -1, 0 },
7597       { +1, 0 },
7598       { 0, +1 },
7599       { 0, -1 },
7600       { -1, 0 },
7601       { +1, 0 },
7602     };
7603     static int test_dir[7] =
7604     {
7605       MV_UP,
7606       MV_LEFT,
7607       MV_RIGHT,
7608       MV_DOWN,
7609       MV_UP,
7610       MV_LEFT,
7611       MV_RIGHT,
7612     };
7613     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7614     int move_preference = -1000000;     // start with very low preference
7615     int new_move_dir = MV_NONE;
7616     int start_test = RND(4);
7617     int i;
7618
7619     for (i = 0; i < NUM_DIRECTIONS; i++)
7620     {
7621       int move_dir = test_dir[start_test + i];
7622       int move_dir_preference;
7623
7624       xx = x + test_xy[start_test + i][0];
7625       yy = y + test_xy[start_test + i][1];
7626
7627       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7628           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7629       {
7630         new_move_dir = move_dir;
7631
7632         break;
7633       }
7634
7635       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7636         continue;
7637
7638       move_dir_preference = -1 * RunnerVisit[xx][yy];
7639       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7640         move_dir_preference = PlayerVisit[xx][yy];
7641
7642       if (move_dir_preference > move_preference)
7643       {
7644         // prefer field that has not been visited for the longest time
7645         move_preference = move_dir_preference;
7646         new_move_dir = move_dir;
7647       }
7648       else if (move_dir_preference == move_preference &&
7649                move_dir == old_move_dir)
7650       {
7651         // prefer last direction when all directions are preferred equally
7652         move_preference = move_dir_preference;
7653         new_move_dir = move_dir;
7654       }
7655     }
7656
7657     MovDir[x][y] = new_move_dir;
7658     if (old_move_dir != new_move_dir)
7659       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7660   }
7661 }
7662
7663 static void TurnRound(int x, int y)
7664 {
7665   int direction = MovDir[x][y];
7666
7667   TurnRoundExt(x, y);
7668
7669   GfxDir[x][y] = MovDir[x][y];
7670
7671   if (direction != MovDir[x][y])
7672     GfxFrame[x][y] = 0;
7673
7674   if (MovDelay[x][y])
7675     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7676
7677   ResetGfxFrame(x, y);
7678 }
7679
7680 static boolean JustBeingPushed(int x, int y)
7681 {
7682   int i;
7683
7684   for (i = 0; i < MAX_PLAYERS; i++)
7685   {
7686     struct PlayerInfo *player = &stored_player[i];
7687
7688     if (player->active && player->is_pushing && player->MovPos)
7689     {
7690       int next_jx = player->jx + (player->jx - player->last_jx);
7691       int next_jy = player->jy + (player->jy - player->last_jy);
7692
7693       if (x == next_jx && y == next_jy)
7694         return TRUE;
7695     }
7696   }
7697
7698   return FALSE;
7699 }
7700
7701 static void StartMoving(int x, int y)
7702 {
7703   boolean started_moving = FALSE;       // some elements can fall _and_ move
7704   int element = Tile[x][y];
7705
7706   if (Stop[x][y])
7707     return;
7708
7709   if (MovDelay[x][y] == 0)
7710     GfxAction[x][y] = ACTION_DEFAULT;
7711
7712   if (CAN_FALL(element) && y < lev_fieldy - 1)
7713   {
7714     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7715         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7716       if (JustBeingPushed(x, y))
7717         return;
7718
7719     if (element == EL_QUICKSAND_FULL)
7720     {
7721       if (IS_FREE(x, y + 1))
7722       {
7723         InitMovingField(x, y, MV_DOWN);
7724         started_moving = TRUE;
7725
7726         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7727 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7728         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7729           Store[x][y] = EL_ROCK;
7730 #else
7731         Store[x][y] = EL_ROCK;
7732 #endif
7733
7734         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7735       }
7736       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7737       {
7738         if (!MovDelay[x][y])
7739         {
7740           MovDelay[x][y] = TILEY + 1;
7741
7742           ResetGfxAnimation(x, y);
7743           ResetGfxAnimation(x, y + 1);
7744         }
7745
7746         if (MovDelay[x][y])
7747         {
7748           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7749           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7750
7751           MovDelay[x][y]--;
7752           if (MovDelay[x][y])
7753             return;
7754         }
7755
7756         Tile[x][y] = EL_QUICKSAND_EMPTY;
7757         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7758         Store[x][y + 1] = Store[x][y];
7759         Store[x][y] = 0;
7760
7761         PlayLevelSoundAction(x, y, ACTION_FILLING);
7762       }
7763       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7764       {
7765         if (!MovDelay[x][y])
7766         {
7767           MovDelay[x][y] = TILEY + 1;
7768
7769           ResetGfxAnimation(x, y);
7770           ResetGfxAnimation(x, y + 1);
7771         }
7772
7773         if (MovDelay[x][y])
7774         {
7775           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7776           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7777
7778           MovDelay[x][y]--;
7779           if (MovDelay[x][y])
7780             return;
7781         }
7782
7783         Tile[x][y] = EL_QUICKSAND_EMPTY;
7784         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7785         Store[x][y + 1] = Store[x][y];
7786         Store[x][y] = 0;
7787
7788         PlayLevelSoundAction(x, y, ACTION_FILLING);
7789       }
7790     }
7791     else if (element == EL_QUICKSAND_FAST_FULL)
7792     {
7793       if (IS_FREE(x, y + 1))
7794       {
7795         InitMovingField(x, y, MV_DOWN);
7796         started_moving = TRUE;
7797
7798         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7799 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7800         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7801           Store[x][y] = EL_ROCK;
7802 #else
7803         Store[x][y] = EL_ROCK;
7804 #endif
7805
7806         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7807       }
7808       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7809       {
7810         if (!MovDelay[x][y])
7811         {
7812           MovDelay[x][y] = TILEY + 1;
7813
7814           ResetGfxAnimation(x, y);
7815           ResetGfxAnimation(x, y + 1);
7816         }
7817
7818         if (MovDelay[x][y])
7819         {
7820           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7821           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7822
7823           MovDelay[x][y]--;
7824           if (MovDelay[x][y])
7825             return;
7826         }
7827
7828         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7829         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7830         Store[x][y + 1] = Store[x][y];
7831         Store[x][y] = 0;
7832
7833         PlayLevelSoundAction(x, y, ACTION_FILLING);
7834       }
7835       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7836       {
7837         if (!MovDelay[x][y])
7838         {
7839           MovDelay[x][y] = TILEY + 1;
7840
7841           ResetGfxAnimation(x, y);
7842           ResetGfxAnimation(x, y + 1);
7843         }
7844
7845         if (MovDelay[x][y])
7846         {
7847           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7848           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7849
7850           MovDelay[x][y]--;
7851           if (MovDelay[x][y])
7852             return;
7853         }
7854
7855         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7856         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7857         Store[x][y + 1] = Store[x][y];
7858         Store[x][y] = 0;
7859
7860         PlayLevelSoundAction(x, y, ACTION_FILLING);
7861       }
7862     }
7863     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7864              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7865     {
7866       InitMovingField(x, y, MV_DOWN);
7867       started_moving = TRUE;
7868
7869       Tile[x][y] = EL_QUICKSAND_FILLING;
7870       Store[x][y] = element;
7871
7872       PlayLevelSoundAction(x, y, ACTION_FILLING);
7873     }
7874     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7875              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7876     {
7877       InitMovingField(x, y, MV_DOWN);
7878       started_moving = TRUE;
7879
7880       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7881       Store[x][y] = element;
7882
7883       PlayLevelSoundAction(x, y, ACTION_FILLING);
7884     }
7885     else if (element == EL_MAGIC_WALL_FULL)
7886     {
7887       if (IS_FREE(x, y + 1))
7888       {
7889         InitMovingField(x, y, MV_DOWN);
7890         started_moving = TRUE;
7891
7892         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7893         Store[x][y] = EL_CHANGED(Store[x][y]);
7894       }
7895       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7896       {
7897         if (!MovDelay[x][y])
7898           MovDelay[x][y] = TILEY / 4 + 1;
7899
7900         if (MovDelay[x][y])
7901         {
7902           MovDelay[x][y]--;
7903           if (MovDelay[x][y])
7904             return;
7905         }
7906
7907         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7908         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7909         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7910         Store[x][y] = 0;
7911       }
7912     }
7913     else if (element == EL_BD_MAGIC_WALL_FULL)
7914     {
7915       if (IS_FREE(x, y + 1))
7916       {
7917         InitMovingField(x, y, MV_DOWN);
7918         started_moving = TRUE;
7919
7920         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7921         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7922       }
7923       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7924       {
7925         if (!MovDelay[x][y])
7926           MovDelay[x][y] = TILEY / 4 + 1;
7927
7928         if (MovDelay[x][y])
7929         {
7930           MovDelay[x][y]--;
7931           if (MovDelay[x][y])
7932             return;
7933         }
7934
7935         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7936         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7937         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7938         Store[x][y] = 0;
7939       }
7940     }
7941     else if (element == EL_DC_MAGIC_WALL_FULL)
7942     {
7943       if (IS_FREE(x, y + 1))
7944       {
7945         InitMovingField(x, y, MV_DOWN);
7946         started_moving = TRUE;
7947
7948         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7949         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7950       }
7951       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7952       {
7953         if (!MovDelay[x][y])
7954           MovDelay[x][y] = TILEY / 4 + 1;
7955
7956         if (MovDelay[x][y])
7957         {
7958           MovDelay[x][y]--;
7959           if (MovDelay[x][y])
7960             return;
7961         }
7962
7963         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7964         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7965         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7966         Store[x][y] = 0;
7967       }
7968     }
7969     else if ((CAN_PASS_MAGIC_WALL(element) &&
7970               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7971                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7972              (CAN_PASS_DC_MAGIC_WALL(element) &&
7973               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7974
7975     {
7976       InitMovingField(x, y, MV_DOWN);
7977       started_moving = TRUE;
7978
7979       Tile[x][y] =
7980         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7981          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7982          EL_DC_MAGIC_WALL_FILLING);
7983       Store[x][y] = element;
7984     }
7985     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7986     {
7987       SplashAcid(x, y + 1);
7988
7989       InitMovingField(x, y, MV_DOWN);
7990       started_moving = TRUE;
7991
7992       Store[x][y] = EL_ACID;
7993     }
7994     else if (
7995              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7996               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7997              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7998               CAN_FALL(element) && WasJustFalling[x][y] &&
7999               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8000
8001              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8002               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8003               (Tile[x][y + 1] == EL_BLOCKED)))
8004     {
8005       /* this is needed for a special case not covered by calling "Impact()"
8006          from "ContinueMoving()": if an element moves to a tile directly below
8007          another element which was just falling on that tile (which was empty
8008          in the previous frame), the falling element above would just stop
8009          instead of smashing the element below (in previous version, the above
8010          element was just checked for "moving" instead of "falling", resulting
8011          in incorrect smashes caused by horizontal movement of the above
8012          element; also, the case of the player being the element to smash was
8013          simply not covered here... :-/ ) */
8014
8015       CheckCollision[x][y] = 0;
8016       CheckImpact[x][y] = 0;
8017
8018       Impact(x, y);
8019     }
8020     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8021     {
8022       if (MovDir[x][y] == MV_NONE)
8023       {
8024         InitMovingField(x, y, MV_DOWN);
8025         started_moving = TRUE;
8026       }
8027     }
8028     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8029     {
8030       if (WasJustFalling[x][y]) // prevent animation from being restarted
8031         MovDir[x][y] = MV_DOWN;
8032
8033       InitMovingField(x, y, MV_DOWN);
8034       started_moving = TRUE;
8035     }
8036     else if (element == EL_AMOEBA_DROP)
8037     {
8038       Tile[x][y] = EL_AMOEBA_GROWING;
8039       Store[x][y] = EL_AMOEBA_WET;
8040     }
8041     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8042               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8043              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8044              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8045     {
8046       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8047                                 (IS_FREE(x - 1, y + 1) ||
8048                                  Tile[x - 1][y + 1] == EL_ACID));
8049       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8050                                 (IS_FREE(x + 1, y + 1) ||
8051                                  Tile[x + 1][y + 1] == EL_ACID));
8052       boolean can_fall_any  = (can_fall_left || can_fall_right);
8053       boolean can_fall_both = (can_fall_left && can_fall_right);
8054       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8055
8056       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8057       {
8058         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8059           can_fall_right = FALSE;
8060         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8061           can_fall_left = FALSE;
8062         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8063           can_fall_right = FALSE;
8064         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8065           can_fall_left = FALSE;
8066
8067         can_fall_any  = (can_fall_left || can_fall_right);
8068         can_fall_both = FALSE;
8069       }
8070
8071       if (can_fall_both)
8072       {
8073         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8074           can_fall_right = FALSE;       // slip down on left side
8075         else
8076           can_fall_left = !(can_fall_right = RND(2));
8077
8078         can_fall_both = FALSE;
8079       }
8080
8081       if (can_fall_any)
8082       {
8083         // if not determined otherwise, prefer left side for slipping down
8084         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8085         started_moving = TRUE;
8086       }
8087     }
8088     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8089     {
8090       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8091       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8092       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8093       int belt_dir = game.belt_dir[belt_nr];
8094
8095       if ((belt_dir == MV_LEFT  && left_is_free) ||
8096           (belt_dir == MV_RIGHT && right_is_free))
8097       {
8098         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8099
8100         InitMovingField(x, y, belt_dir);
8101         started_moving = TRUE;
8102
8103         Pushed[x][y] = TRUE;
8104         Pushed[nextx][y] = TRUE;
8105
8106         GfxAction[x][y] = ACTION_DEFAULT;
8107       }
8108       else
8109       {
8110         MovDir[x][y] = 0;       // if element was moving, stop it
8111       }
8112     }
8113   }
8114
8115   // not "else if" because of elements that can fall and move (EL_SPRING)
8116   if (CAN_MOVE(element) && !started_moving)
8117   {
8118     int move_pattern = element_info[element].move_pattern;
8119     int newx, newy;
8120
8121     Moving2Blocked(x, y, &newx, &newy);
8122
8123     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8124       return;
8125
8126     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8127         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8128     {
8129       WasJustMoving[x][y] = 0;
8130       CheckCollision[x][y] = 0;
8131
8132       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8133
8134       if (Tile[x][y] != element)        // element has changed
8135         return;
8136     }
8137
8138     if (!MovDelay[x][y])        // start new movement phase
8139     {
8140       // all objects that can change their move direction after each step
8141       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8142
8143       if (element != EL_YAMYAM &&
8144           element != EL_DARK_YAMYAM &&
8145           element != EL_PACMAN &&
8146           !(move_pattern & MV_ANY_DIRECTION) &&
8147           move_pattern != MV_TURNING_LEFT &&
8148           move_pattern != MV_TURNING_RIGHT &&
8149           move_pattern != MV_TURNING_LEFT_RIGHT &&
8150           move_pattern != MV_TURNING_RIGHT_LEFT &&
8151           move_pattern != MV_TURNING_RANDOM)
8152       {
8153         TurnRound(x, y);
8154
8155         if (MovDelay[x][y] && (element == EL_BUG ||
8156                                element == EL_SPACESHIP ||
8157                                element == EL_SP_SNIKSNAK ||
8158                                element == EL_SP_ELECTRON ||
8159                                element == EL_MOLE))
8160           TEST_DrawLevelField(x, y);
8161       }
8162     }
8163
8164     if (MovDelay[x][y])         // wait some time before next movement
8165     {
8166       MovDelay[x][y]--;
8167
8168       if (element == EL_ROBOT ||
8169           element == EL_YAMYAM ||
8170           element == EL_DARK_YAMYAM)
8171       {
8172         DrawLevelElementAnimationIfNeeded(x, y, element);
8173         PlayLevelSoundAction(x, y, ACTION_WAITING);
8174       }
8175       else if (element == EL_SP_ELECTRON)
8176         DrawLevelElementAnimationIfNeeded(x, y, element);
8177       else if (element == EL_DRAGON)
8178       {
8179         int i;
8180         int dir = MovDir[x][y];
8181         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8182         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8183         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8184                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8185                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8186                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8187         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8188
8189         GfxAction[x][y] = ACTION_ATTACKING;
8190
8191         if (IS_PLAYER(x, y))
8192           DrawPlayerField(x, y);
8193         else
8194           TEST_DrawLevelField(x, y);
8195
8196         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8197
8198         for (i = 1; i <= 3; i++)
8199         {
8200           int xx = x + i * dx;
8201           int yy = y + i * dy;
8202           int sx = SCREENX(xx);
8203           int sy = SCREENY(yy);
8204           int flame_graphic = graphic + (i - 1);
8205
8206           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8207             break;
8208
8209           if (MovDelay[x][y])
8210           {
8211             int flamed = MovingOrBlocked2Element(xx, yy);
8212
8213             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8214               Bang(xx, yy);
8215             else
8216               RemoveMovingField(xx, yy);
8217
8218             ChangeDelay[xx][yy] = 0;
8219
8220             Tile[xx][yy] = EL_FLAMES;
8221
8222             if (IN_SCR_FIELD(sx, sy))
8223             {
8224               TEST_DrawLevelFieldCrumbled(xx, yy);
8225               DrawGraphic(sx, sy, flame_graphic, frame);
8226             }
8227           }
8228           else
8229           {
8230             if (Tile[xx][yy] == EL_FLAMES)
8231               Tile[xx][yy] = EL_EMPTY;
8232             TEST_DrawLevelField(xx, yy);
8233           }
8234         }
8235       }
8236
8237       if (MovDelay[x][y])       // element still has to wait some time
8238       {
8239         PlayLevelSoundAction(x, y, ACTION_WAITING);
8240
8241         return;
8242       }
8243     }
8244
8245     // now make next step
8246
8247     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8248
8249     if (DONT_COLLIDE_WITH(element) &&
8250         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8251         !PLAYER_ENEMY_PROTECTED(newx, newy))
8252     {
8253       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8254
8255       return;
8256     }
8257
8258     else if (CAN_MOVE_INTO_ACID(element) &&
8259              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8260              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8261              (MovDir[x][y] == MV_DOWN ||
8262               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8263     {
8264       SplashAcid(newx, newy);
8265       Store[x][y] = EL_ACID;
8266     }
8267     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8268     {
8269       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8270           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8271           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8272           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8273       {
8274         RemoveField(x, y);
8275         TEST_DrawLevelField(x, y);
8276
8277         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8278         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8279           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8280
8281         game.friends_still_needed--;
8282         if (!game.friends_still_needed &&
8283             !game.GameOver &&
8284             game.all_players_gone)
8285           LevelSolved();
8286
8287         return;
8288       }
8289       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8290       {
8291         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8292           TEST_DrawLevelField(newx, newy);
8293         else
8294           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8295       }
8296       else if (!IS_FREE(newx, newy))
8297       {
8298         GfxAction[x][y] = ACTION_WAITING;
8299
8300         if (IS_PLAYER(x, y))
8301           DrawPlayerField(x, y);
8302         else
8303           TEST_DrawLevelField(x, y);
8304
8305         return;
8306       }
8307     }
8308     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8309     {
8310       if (IS_FOOD_PIG(Tile[newx][newy]))
8311       {
8312         if (IS_MOVING(newx, newy))
8313           RemoveMovingField(newx, newy);
8314         else
8315         {
8316           Tile[newx][newy] = EL_EMPTY;
8317           TEST_DrawLevelField(newx, newy);
8318         }
8319
8320         PlayLevelSound(x, y, SND_PIG_DIGGING);
8321       }
8322       else if (!IS_FREE(newx, newy))
8323       {
8324         if (IS_PLAYER(x, y))
8325           DrawPlayerField(x, y);
8326         else
8327           TEST_DrawLevelField(x, y);
8328
8329         return;
8330       }
8331     }
8332     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8333     {
8334       if (Store[x][y] != EL_EMPTY)
8335       {
8336         boolean can_clone = FALSE;
8337         int xx, yy;
8338
8339         // check if element to clone is still there
8340         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8341         {
8342           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8343           {
8344             can_clone = TRUE;
8345
8346             break;
8347           }
8348         }
8349
8350         // cannot clone or target field not free anymore -- do not clone
8351         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8352           Store[x][y] = EL_EMPTY;
8353       }
8354
8355       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8356       {
8357         if (IS_MV_DIAGONAL(MovDir[x][y]))
8358         {
8359           int diagonal_move_dir = MovDir[x][y];
8360           int stored = Store[x][y];
8361           int change_delay = 8;
8362           int graphic;
8363
8364           // android is moving diagonally
8365
8366           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8367
8368           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8369           GfxElement[x][y] = EL_EMC_ANDROID;
8370           GfxAction[x][y] = ACTION_SHRINKING;
8371           GfxDir[x][y] = diagonal_move_dir;
8372           ChangeDelay[x][y] = change_delay;
8373
8374           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8375                                    GfxDir[x][y]);
8376
8377           DrawLevelGraphicAnimation(x, y, graphic);
8378           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8379
8380           if (Tile[newx][newy] == EL_ACID)
8381           {
8382             SplashAcid(newx, newy);
8383
8384             return;
8385           }
8386
8387           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8388
8389           Store[newx][newy] = EL_EMC_ANDROID;
8390           GfxElement[newx][newy] = EL_EMC_ANDROID;
8391           GfxAction[newx][newy] = ACTION_GROWING;
8392           GfxDir[newx][newy] = diagonal_move_dir;
8393           ChangeDelay[newx][newy] = change_delay;
8394
8395           graphic = el_act_dir2img(GfxElement[newx][newy],
8396                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8397
8398           DrawLevelGraphicAnimation(newx, newy, graphic);
8399           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8400
8401           return;
8402         }
8403         else
8404         {
8405           Tile[newx][newy] = EL_EMPTY;
8406           TEST_DrawLevelField(newx, newy);
8407
8408           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8409         }
8410       }
8411       else if (!IS_FREE(newx, newy))
8412       {
8413         return;
8414       }
8415     }
8416     else if (IS_CUSTOM_ELEMENT(element) &&
8417              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8418     {
8419       if (!DigFieldByCE(newx, newy, element))
8420         return;
8421
8422       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8423       {
8424         RunnerVisit[x][y] = FrameCounter;
8425         PlayerVisit[x][y] /= 8;         // expire player visit path
8426       }
8427     }
8428     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8429     {
8430       if (!IS_FREE(newx, newy))
8431       {
8432         if (IS_PLAYER(x, y))
8433           DrawPlayerField(x, y);
8434         else
8435           TEST_DrawLevelField(x, y);
8436
8437         return;
8438       }
8439       else
8440       {
8441         boolean wanna_flame = !RND(10);
8442         int dx = newx - x, dy = newy - y;
8443         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8444         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8445         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8446                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8447         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8448                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8449
8450         if ((wanna_flame ||
8451              IS_CLASSIC_ENEMY(element1) ||
8452              IS_CLASSIC_ENEMY(element2)) &&
8453             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8454             element1 != EL_FLAMES && element2 != EL_FLAMES)
8455         {
8456           ResetGfxAnimation(x, y);
8457           GfxAction[x][y] = ACTION_ATTACKING;
8458
8459           if (IS_PLAYER(x, y))
8460             DrawPlayerField(x, y);
8461           else
8462             TEST_DrawLevelField(x, y);
8463
8464           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8465
8466           MovDelay[x][y] = 50;
8467
8468           Tile[newx][newy] = EL_FLAMES;
8469           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8470             Tile[newx1][newy1] = EL_FLAMES;
8471           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8472             Tile[newx2][newy2] = EL_FLAMES;
8473
8474           return;
8475         }
8476       }
8477     }
8478     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8479              Tile[newx][newy] == EL_DIAMOND)
8480     {
8481       if (IS_MOVING(newx, newy))
8482         RemoveMovingField(newx, newy);
8483       else
8484       {
8485         Tile[newx][newy] = EL_EMPTY;
8486         TEST_DrawLevelField(newx, newy);
8487       }
8488
8489       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8490     }
8491     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8492              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8493     {
8494       if (AmoebaNr[newx][newy])
8495       {
8496         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8497         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8498             Tile[newx][newy] == EL_BD_AMOEBA)
8499           AmoebaCnt[AmoebaNr[newx][newy]]--;
8500       }
8501
8502       if (IS_MOVING(newx, newy))
8503       {
8504         RemoveMovingField(newx, newy);
8505       }
8506       else
8507       {
8508         Tile[newx][newy] = EL_EMPTY;
8509         TEST_DrawLevelField(newx, newy);
8510       }
8511
8512       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8513     }
8514     else if ((element == EL_PACMAN || element == EL_MOLE)
8515              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8516     {
8517       if (AmoebaNr[newx][newy])
8518       {
8519         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8520         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8521             Tile[newx][newy] == EL_BD_AMOEBA)
8522           AmoebaCnt[AmoebaNr[newx][newy]]--;
8523       }
8524
8525       if (element == EL_MOLE)
8526       {
8527         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8528         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8529
8530         ResetGfxAnimation(x, y);
8531         GfxAction[x][y] = ACTION_DIGGING;
8532         TEST_DrawLevelField(x, y);
8533
8534         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8535
8536         return;                         // wait for shrinking amoeba
8537       }
8538       else      // element == EL_PACMAN
8539       {
8540         Tile[newx][newy] = EL_EMPTY;
8541         TEST_DrawLevelField(newx, newy);
8542         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8543       }
8544     }
8545     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8546              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8547               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8548     {
8549       // wait for shrinking amoeba to completely disappear
8550       return;
8551     }
8552     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8553     {
8554       // object was running against a wall
8555
8556       TurnRound(x, y);
8557
8558       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8559         DrawLevelElementAnimation(x, y, element);
8560
8561       if (DONT_TOUCH(element))
8562         TestIfBadThingTouchesPlayer(x, y);
8563
8564       return;
8565     }
8566
8567     InitMovingField(x, y, MovDir[x][y]);
8568
8569     PlayLevelSoundAction(x, y, ACTION_MOVING);
8570   }
8571
8572   if (MovDir[x][y])
8573     ContinueMoving(x, y);
8574 }
8575
8576 void ContinueMoving(int x, int y)
8577 {
8578   int element = Tile[x][y];
8579   struct ElementInfo *ei = &element_info[element];
8580   int direction = MovDir[x][y];
8581   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8582   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8583   int newx = x + dx, newy = y + dy;
8584   int stored = Store[x][y];
8585   int stored_new = Store[newx][newy];
8586   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8587   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8588   boolean last_line = (newy == lev_fieldy - 1);
8589   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8590
8591   if (pushed_by_player)         // special case: moving object pushed by player
8592   {
8593     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8594   }
8595   else if (use_step_delay)      // special case: moving object has step delay
8596   {
8597     if (!MovDelay[x][y])
8598       MovPos[x][y] += getElementMoveStepsize(x, y);
8599
8600     if (MovDelay[x][y])
8601       MovDelay[x][y]--;
8602     else
8603       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8604
8605     if (MovDelay[x][y])
8606     {
8607       TEST_DrawLevelField(x, y);
8608
8609       return;   // element is still waiting
8610     }
8611   }
8612   else                          // normal case: generically moving object
8613   {
8614     MovPos[x][y] += getElementMoveStepsize(x, y);
8615   }
8616
8617   if (ABS(MovPos[x][y]) < TILEX)
8618   {
8619     TEST_DrawLevelField(x, y);
8620
8621     return;     // element is still moving
8622   }
8623
8624   // element reached destination field
8625
8626   Tile[x][y] = EL_EMPTY;
8627   Tile[newx][newy] = element;
8628   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8629
8630   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8631   {
8632     element = Tile[newx][newy] = EL_ACID;
8633   }
8634   else if (element == EL_MOLE)
8635   {
8636     Tile[x][y] = EL_SAND;
8637
8638     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8639   }
8640   else if (element == EL_QUICKSAND_FILLING)
8641   {
8642     element = Tile[newx][newy] = get_next_element(element);
8643     Store[newx][newy] = Store[x][y];
8644   }
8645   else if (element == EL_QUICKSAND_EMPTYING)
8646   {
8647     Tile[x][y] = get_next_element(element);
8648     element = Tile[newx][newy] = Store[x][y];
8649   }
8650   else if (element == EL_QUICKSAND_FAST_FILLING)
8651   {
8652     element = Tile[newx][newy] = get_next_element(element);
8653     Store[newx][newy] = Store[x][y];
8654   }
8655   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8656   {
8657     Tile[x][y] = get_next_element(element);
8658     element = Tile[newx][newy] = Store[x][y];
8659   }
8660   else if (element == EL_MAGIC_WALL_FILLING)
8661   {
8662     element = Tile[newx][newy] = get_next_element(element);
8663     if (!game.magic_wall_active)
8664       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8665     Store[newx][newy] = Store[x][y];
8666   }
8667   else if (element == EL_MAGIC_WALL_EMPTYING)
8668   {
8669     Tile[x][y] = get_next_element(element);
8670     if (!game.magic_wall_active)
8671       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8672     element = Tile[newx][newy] = Store[x][y];
8673
8674     InitField(newx, newy, FALSE);
8675   }
8676   else if (element == EL_BD_MAGIC_WALL_FILLING)
8677   {
8678     element = Tile[newx][newy] = get_next_element(element);
8679     if (!game.magic_wall_active)
8680       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8681     Store[newx][newy] = Store[x][y];
8682   }
8683   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8684   {
8685     Tile[x][y] = get_next_element(element);
8686     if (!game.magic_wall_active)
8687       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8688     element = Tile[newx][newy] = Store[x][y];
8689
8690     InitField(newx, newy, FALSE);
8691   }
8692   else if (element == EL_DC_MAGIC_WALL_FILLING)
8693   {
8694     element = Tile[newx][newy] = get_next_element(element);
8695     if (!game.magic_wall_active)
8696       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8697     Store[newx][newy] = Store[x][y];
8698   }
8699   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8700   {
8701     Tile[x][y] = get_next_element(element);
8702     if (!game.magic_wall_active)
8703       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8704     element = Tile[newx][newy] = Store[x][y];
8705
8706     InitField(newx, newy, FALSE);
8707   }
8708   else if (element == EL_AMOEBA_DROPPING)
8709   {
8710     Tile[x][y] = get_next_element(element);
8711     element = Tile[newx][newy] = Store[x][y];
8712   }
8713   else if (element == EL_SOKOBAN_OBJECT)
8714   {
8715     if (Back[x][y])
8716       Tile[x][y] = Back[x][y];
8717
8718     if (Back[newx][newy])
8719       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8720
8721     Back[x][y] = Back[newx][newy] = 0;
8722   }
8723
8724   Store[x][y] = EL_EMPTY;
8725   MovPos[x][y] = 0;
8726   MovDir[x][y] = 0;
8727   MovDelay[x][y] = 0;
8728
8729   MovDelay[newx][newy] = 0;
8730
8731   if (CAN_CHANGE_OR_HAS_ACTION(element))
8732   {
8733     // copy element change control values to new field
8734     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8735     ChangePage[newx][newy]  = ChangePage[x][y];
8736     ChangeCount[newx][newy] = ChangeCount[x][y];
8737     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8738   }
8739
8740   CustomValue[newx][newy] = CustomValue[x][y];
8741
8742   ChangeDelay[x][y] = 0;
8743   ChangePage[x][y] = -1;
8744   ChangeCount[x][y] = 0;
8745   ChangeEvent[x][y] = -1;
8746
8747   CustomValue[x][y] = 0;
8748
8749   // copy animation control values to new field
8750   GfxFrame[newx][newy]  = GfxFrame[x][y];
8751   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8752   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8753   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8754
8755   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8756
8757   // some elements can leave other elements behind after moving
8758   if (ei->move_leave_element != EL_EMPTY &&
8759       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8760       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8761   {
8762     int move_leave_element = ei->move_leave_element;
8763
8764     // this makes it possible to leave the removed element again
8765     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8766       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8767
8768     Tile[x][y] = move_leave_element;
8769
8770     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8771       MovDir[x][y] = direction;
8772
8773     InitField(x, y, FALSE);
8774
8775     if (GFX_CRUMBLED(Tile[x][y]))
8776       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8777
8778     if (ELEM_IS_PLAYER(move_leave_element))
8779       RelocatePlayer(x, y, move_leave_element);
8780   }
8781
8782   // do this after checking for left-behind element
8783   ResetGfxAnimation(x, y);      // reset animation values for old field
8784
8785   if (!CAN_MOVE(element) ||
8786       (CAN_FALL(element) && direction == MV_DOWN &&
8787        (element == EL_SPRING ||
8788         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8789         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8790     GfxDir[x][y] = MovDir[newx][newy] = 0;
8791
8792   TEST_DrawLevelField(x, y);
8793   TEST_DrawLevelField(newx, newy);
8794
8795   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8796
8797   // prevent pushed element from moving on in pushed direction
8798   if (pushed_by_player && CAN_MOVE(element) &&
8799       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8800       !(element_info[element].move_pattern & direction))
8801     TurnRound(newx, newy);
8802
8803   // prevent elements on conveyor belt from moving on in last direction
8804   if (pushed_by_conveyor && CAN_FALL(element) &&
8805       direction & MV_HORIZONTAL)
8806     MovDir[newx][newy] = 0;
8807
8808   if (!pushed_by_player)
8809   {
8810     int nextx = newx + dx, nexty = newy + dy;
8811     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8812
8813     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8814
8815     if (CAN_FALL(element) && direction == MV_DOWN)
8816       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8817
8818     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8819       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8820
8821     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8822       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8823   }
8824
8825   if (DONT_TOUCH(element))      // object may be nasty to player or others
8826   {
8827     TestIfBadThingTouchesPlayer(newx, newy);
8828     TestIfBadThingTouchesFriend(newx, newy);
8829
8830     if (!IS_CUSTOM_ELEMENT(element))
8831       TestIfBadThingTouchesOtherBadThing(newx, newy);
8832   }
8833   else if (element == EL_PENGUIN)
8834     TestIfFriendTouchesBadThing(newx, newy);
8835
8836   if (DONT_GET_HIT_BY(element))
8837   {
8838     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8839   }
8840
8841   // give the player one last chance (one more frame) to move away
8842   if (CAN_FALL(element) && direction == MV_DOWN &&
8843       (last_line || (!IS_FREE(x, newy + 1) &&
8844                      (!IS_PLAYER(x, newy + 1) ||
8845                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8846     Impact(x, newy);
8847
8848   if (pushed_by_player && !game.use_change_when_pushing_bug)
8849   {
8850     int push_side = MV_DIR_OPPOSITE(direction);
8851     struct PlayerInfo *player = PLAYERINFO(x, y);
8852
8853     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8854                                player->index_bit, push_side);
8855     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8856                                         player->index_bit, push_side);
8857   }
8858
8859   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8860     MovDelay[newx][newy] = 1;
8861
8862   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8863
8864   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8865   TestIfElementHitsCustomElement(newx, newy, direction);
8866   TestIfPlayerTouchesCustomElement(newx, newy);
8867   TestIfElementTouchesCustomElement(newx, newy);
8868
8869   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8870       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8871     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8872                              MV_DIR_OPPOSITE(direction));
8873 }
8874
8875 int AmoebaNeighbourNr(int ax, int ay)
8876 {
8877   int i;
8878   int element = Tile[ax][ay];
8879   int group_nr = 0;
8880   static int xy[4][2] =
8881   {
8882     { 0, -1 },
8883     { -1, 0 },
8884     { +1, 0 },
8885     { 0, +1 }
8886   };
8887
8888   for (i = 0; i < NUM_DIRECTIONS; i++)
8889   {
8890     int x = ax + xy[i][0];
8891     int y = ay + xy[i][1];
8892
8893     if (!IN_LEV_FIELD(x, y))
8894       continue;
8895
8896     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8897       group_nr = AmoebaNr[x][y];
8898   }
8899
8900   return group_nr;
8901 }
8902
8903 static void AmoebaMerge(int ax, int ay)
8904 {
8905   int i, x, y, xx, yy;
8906   int new_group_nr = AmoebaNr[ax][ay];
8907   static int xy[4][2] =
8908   {
8909     { 0, -1 },
8910     { -1, 0 },
8911     { +1, 0 },
8912     { 0, +1 }
8913   };
8914
8915   if (new_group_nr == 0)
8916     return;
8917
8918   for (i = 0; i < NUM_DIRECTIONS; i++)
8919   {
8920     x = ax + xy[i][0];
8921     y = ay + xy[i][1];
8922
8923     if (!IN_LEV_FIELD(x, y))
8924       continue;
8925
8926     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8927          Tile[x][y] == EL_BD_AMOEBA ||
8928          Tile[x][y] == EL_AMOEBA_DEAD) &&
8929         AmoebaNr[x][y] != new_group_nr)
8930     {
8931       int old_group_nr = AmoebaNr[x][y];
8932
8933       if (old_group_nr == 0)
8934         return;
8935
8936       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8937       AmoebaCnt[old_group_nr] = 0;
8938       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8939       AmoebaCnt2[old_group_nr] = 0;
8940
8941       SCAN_PLAYFIELD(xx, yy)
8942       {
8943         if (AmoebaNr[xx][yy] == old_group_nr)
8944           AmoebaNr[xx][yy] = new_group_nr;
8945       }
8946     }
8947   }
8948 }
8949
8950 void AmoebaToDiamond(int ax, int ay)
8951 {
8952   int i, x, y;
8953
8954   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8955   {
8956     int group_nr = AmoebaNr[ax][ay];
8957
8958 #ifdef DEBUG
8959     if (group_nr == 0)
8960     {
8961       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8962       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8963
8964       return;
8965     }
8966 #endif
8967
8968     SCAN_PLAYFIELD(x, y)
8969     {
8970       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8971       {
8972         AmoebaNr[x][y] = 0;
8973         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8974       }
8975     }
8976
8977     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8978                             SND_AMOEBA_TURNING_TO_GEM :
8979                             SND_AMOEBA_TURNING_TO_ROCK));
8980     Bang(ax, ay);
8981   }
8982   else
8983   {
8984     static int xy[4][2] =
8985     {
8986       { 0, -1 },
8987       { -1, 0 },
8988       { +1, 0 },
8989       { 0, +1 }
8990     };
8991
8992     for (i = 0; i < NUM_DIRECTIONS; i++)
8993     {
8994       x = ax + xy[i][0];
8995       y = ay + xy[i][1];
8996
8997       if (!IN_LEV_FIELD(x, y))
8998         continue;
8999
9000       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9001       {
9002         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9003                               SND_AMOEBA_TURNING_TO_GEM :
9004                               SND_AMOEBA_TURNING_TO_ROCK));
9005         Bang(x, y);
9006       }
9007     }
9008   }
9009 }
9010
9011 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9012 {
9013   int x, y;
9014   int group_nr = AmoebaNr[ax][ay];
9015   boolean done = FALSE;
9016
9017 #ifdef DEBUG
9018   if (group_nr == 0)
9019   {
9020     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9021     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9022
9023     return;
9024   }
9025 #endif
9026
9027   SCAN_PLAYFIELD(x, y)
9028   {
9029     if (AmoebaNr[x][y] == group_nr &&
9030         (Tile[x][y] == EL_AMOEBA_DEAD ||
9031          Tile[x][y] == EL_BD_AMOEBA ||
9032          Tile[x][y] == EL_AMOEBA_GROWING))
9033     {
9034       AmoebaNr[x][y] = 0;
9035       Tile[x][y] = new_element;
9036       InitField(x, y, FALSE);
9037       TEST_DrawLevelField(x, y);
9038       done = TRUE;
9039     }
9040   }
9041
9042   if (done)
9043     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9044                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9045                             SND_BD_AMOEBA_TURNING_TO_GEM));
9046 }
9047
9048 static void AmoebaGrowing(int x, int y)
9049 {
9050   static unsigned int sound_delay = 0;
9051   static unsigned int sound_delay_value = 0;
9052
9053   if (!MovDelay[x][y])          // start new growing cycle
9054   {
9055     MovDelay[x][y] = 7;
9056
9057     if (DelayReached(&sound_delay, sound_delay_value))
9058     {
9059       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9060       sound_delay_value = 30;
9061     }
9062   }
9063
9064   if (MovDelay[x][y])           // wait some time before growing bigger
9065   {
9066     MovDelay[x][y]--;
9067     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9068     {
9069       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9070                                            6 - MovDelay[x][y]);
9071
9072       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9073     }
9074
9075     if (!MovDelay[x][y])
9076     {
9077       Tile[x][y] = Store[x][y];
9078       Store[x][y] = 0;
9079       TEST_DrawLevelField(x, y);
9080     }
9081   }
9082 }
9083
9084 static void AmoebaShrinking(int x, int y)
9085 {
9086   static unsigned int sound_delay = 0;
9087   static unsigned int sound_delay_value = 0;
9088
9089   if (!MovDelay[x][y])          // start new shrinking cycle
9090   {
9091     MovDelay[x][y] = 7;
9092
9093     if (DelayReached(&sound_delay, sound_delay_value))
9094       sound_delay_value = 30;
9095   }
9096
9097   if (MovDelay[x][y])           // wait some time before shrinking
9098   {
9099     MovDelay[x][y]--;
9100     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9101     {
9102       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9103                                            6 - MovDelay[x][y]);
9104
9105       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9106     }
9107
9108     if (!MovDelay[x][y])
9109     {
9110       Tile[x][y] = EL_EMPTY;
9111       TEST_DrawLevelField(x, y);
9112
9113       // don't let mole enter this field in this cycle;
9114       // (give priority to objects falling to this field from above)
9115       Stop[x][y] = TRUE;
9116     }
9117   }
9118 }
9119
9120 static void AmoebaReproduce(int ax, int ay)
9121 {
9122   int i;
9123   int element = Tile[ax][ay];
9124   int graphic = el2img(element);
9125   int newax = ax, neway = ay;
9126   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9127   static int xy[4][2] =
9128   {
9129     { 0, -1 },
9130     { -1, 0 },
9131     { +1, 0 },
9132     { 0, +1 }
9133   };
9134
9135   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9136   {
9137     Tile[ax][ay] = EL_AMOEBA_DEAD;
9138     TEST_DrawLevelField(ax, ay);
9139     return;
9140   }
9141
9142   if (IS_ANIMATED(graphic))
9143     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9144
9145   if (!MovDelay[ax][ay])        // start making new amoeba field
9146     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9147
9148   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9149   {
9150     MovDelay[ax][ay]--;
9151     if (MovDelay[ax][ay])
9152       return;
9153   }
9154
9155   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9156   {
9157     int start = RND(4);
9158     int x = ax + xy[start][0];
9159     int y = ay + xy[start][1];
9160
9161     if (!IN_LEV_FIELD(x, y))
9162       return;
9163
9164     if (IS_FREE(x, y) ||
9165         CAN_GROW_INTO(Tile[x][y]) ||
9166         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9167         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9168     {
9169       newax = x;
9170       neway = y;
9171     }
9172
9173     if (newax == ax && neway == ay)
9174       return;
9175   }
9176   else                          // normal or "filled" (BD style) amoeba
9177   {
9178     int start = RND(4);
9179     boolean waiting_for_player = FALSE;
9180
9181     for (i = 0; i < NUM_DIRECTIONS; i++)
9182     {
9183       int j = (start + i) % 4;
9184       int x = ax + xy[j][0];
9185       int y = ay + xy[j][1];
9186
9187       if (!IN_LEV_FIELD(x, y))
9188         continue;
9189
9190       if (IS_FREE(x, y) ||
9191           CAN_GROW_INTO(Tile[x][y]) ||
9192           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9193           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9194       {
9195         newax = x;
9196         neway = y;
9197         break;
9198       }
9199       else if (IS_PLAYER(x, y))
9200         waiting_for_player = TRUE;
9201     }
9202
9203     if (newax == ax && neway == ay)             // amoeba cannot grow
9204     {
9205       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9206       {
9207         Tile[ax][ay] = EL_AMOEBA_DEAD;
9208         TEST_DrawLevelField(ax, ay);
9209         AmoebaCnt[AmoebaNr[ax][ay]]--;
9210
9211         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9212         {
9213           if (element == EL_AMOEBA_FULL)
9214             AmoebaToDiamond(ax, ay);
9215           else if (element == EL_BD_AMOEBA)
9216             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9217         }
9218       }
9219       return;
9220     }
9221     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9222     {
9223       // amoeba gets larger by growing in some direction
9224
9225       int new_group_nr = AmoebaNr[ax][ay];
9226
9227 #ifdef DEBUG
9228   if (new_group_nr == 0)
9229   {
9230     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9231           newax, neway);
9232     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9233
9234     return;
9235   }
9236 #endif
9237
9238       AmoebaNr[newax][neway] = new_group_nr;
9239       AmoebaCnt[new_group_nr]++;
9240       AmoebaCnt2[new_group_nr]++;
9241
9242       // if amoeba touches other amoeba(s) after growing, unify them
9243       AmoebaMerge(newax, neway);
9244
9245       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9246       {
9247         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9248         return;
9249       }
9250     }
9251   }
9252
9253   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9254       (neway == lev_fieldy - 1 && newax != ax))
9255   {
9256     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9257     Store[newax][neway] = element;
9258   }
9259   else if (neway == ay || element == EL_EMC_DRIPPER)
9260   {
9261     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9262
9263     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9264   }
9265   else
9266   {
9267     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9268     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9269     Store[ax][ay] = EL_AMOEBA_DROP;
9270     ContinueMoving(ax, ay);
9271     return;
9272   }
9273
9274   TEST_DrawLevelField(newax, neway);
9275 }
9276
9277 static void Life(int ax, int ay)
9278 {
9279   int x1, y1, x2, y2;
9280   int life_time = 40;
9281   int element = Tile[ax][ay];
9282   int graphic = el2img(element);
9283   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9284                          level.biomaze);
9285   boolean changed = FALSE;
9286
9287   if (IS_ANIMATED(graphic))
9288     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9289
9290   if (Stop[ax][ay])
9291     return;
9292
9293   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9294     MovDelay[ax][ay] = life_time;
9295
9296   if (MovDelay[ax][ay])         // wait some time before next cycle
9297   {
9298     MovDelay[ax][ay]--;
9299     if (MovDelay[ax][ay])
9300       return;
9301   }
9302
9303   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9304   {
9305     int xx = ax+x1, yy = ay+y1;
9306     int old_element = Tile[xx][yy];
9307     int num_neighbours = 0;
9308
9309     if (!IN_LEV_FIELD(xx, yy))
9310       continue;
9311
9312     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9313     {
9314       int x = xx+x2, y = yy+y2;
9315
9316       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9317         continue;
9318
9319       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9320       boolean is_neighbour = FALSE;
9321
9322       if (level.use_life_bugs)
9323         is_neighbour =
9324           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9325            (IS_FREE(x, y)                             &&  Stop[x][y]));
9326       else
9327         is_neighbour =
9328           (Last[x][y] == element || is_player_cell);
9329
9330       if (is_neighbour)
9331         num_neighbours++;
9332     }
9333
9334     boolean is_free = FALSE;
9335
9336     if (level.use_life_bugs)
9337       is_free = (IS_FREE(xx, yy));
9338     else
9339       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9340
9341     if (xx == ax && yy == ay)           // field in the middle
9342     {
9343       if (num_neighbours < life_parameter[0] ||
9344           num_neighbours > life_parameter[1])
9345       {
9346         Tile[xx][yy] = EL_EMPTY;
9347         if (Tile[xx][yy] != old_element)
9348           TEST_DrawLevelField(xx, yy);
9349         Stop[xx][yy] = TRUE;
9350         changed = TRUE;
9351       }
9352     }
9353     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9354     {                                   // free border field
9355       if (num_neighbours >= life_parameter[2] &&
9356           num_neighbours <= life_parameter[3])
9357       {
9358         Tile[xx][yy] = element;
9359         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9360         if (Tile[xx][yy] != old_element)
9361           TEST_DrawLevelField(xx, yy);
9362         Stop[xx][yy] = TRUE;
9363         changed = TRUE;
9364       }
9365     }
9366   }
9367
9368   if (changed)
9369     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9370                    SND_GAME_OF_LIFE_GROWING);
9371 }
9372
9373 static void InitRobotWheel(int x, int y)
9374 {
9375   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9376 }
9377
9378 static void RunRobotWheel(int x, int y)
9379 {
9380   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9381 }
9382
9383 static void StopRobotWheel(int x, int y)
9384 {
9385   if (game.robot_wheel_x == x &&
9386       game.robot_wheel_y == y)
9387   {
9388     game.robot_wheel_x = -1;
9389     game.robot_wheel_y = -1;
9390     game.robot_wheel_active = FALSE;
9391   }
9392 }
9393
9394 static void InitTimegateWheel(int x, int y)
9395 {
9396   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9397 }
9398
9399 static void RunTimegateWheel(int x, int y)
9400 {
9401   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9402 }
9403
9404 static void InitMagicBallDelay(int x, int y)
9405 {
9406   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9407 }
9408
9409 static void ActivateMagicBall(int bx, int by)
9410 {
9411   int x, y;
9412
9413   if (level.ball_random)
9414   {
9415     int pos_border = RND(8);    // select one of the eight border elements
9416     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9417     int xx = pos_content % 3;
9418     int yy = pos_content / 3;
9419
9420     x = bx - 1 + xx;
9421     y = by - 1 + yy;
9422
9423     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9424       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9425   }
9426   else
9427   {
9428     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9429     {
9430       int xx = x - bx + 1;
9431       int yy = y - by + 1;
9432
9433       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9434         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9435     }
9436   }
9437
9438   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9439 }
9440
9441 static void CheckExit(int x, int y)
9442 {
9443   if (game.gems_still_needed > 0 ||
9444       game.sokoban_fields_still_needed > 0 ||
9445       game.sokoban_objects_still_needed > 0 ||
9446       game.lights_still_needed > 0)
9447   {
9448     int element = Tile[x][y];
9449     int graphic = el2img(element);
9450
9451     if (IS_ANIMATED(graphic))
9452       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9453
9454     return;
9455   }
9456
9457   // do not re-open exit door closed after last player
9458   if (game.all_players_gone)
9459     return;
9460
9461   Tile[x][y] = EL_EXIT_OPENING;
9462
9463   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9464 }
9465
9466 static void CheckExitEM(int x, int y)
9467 {
9468   if (game.gems_still_needed > 0 ||
9469       game.sokoban_fields_still_needed > 0 ||
9470       game.sokoban_objects_still_needed > 0 ||
9471       game.lights_still_needed > 0)
9472   {
9473     int element = Tile[x][y];
9474     int graphic = el2img(element);
9475
9476     if (IS_ANIMATED(graphic))
9477       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9478
9479     return;
9480   }
9481
9482   // do not re-open exit door closed after last player
9483   if (game.all_players_gone)
9484     return;
9485
9486   Tile[x][y] = EL_EM_EXIT_OPENING;
9487
9488   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9489 }
9490
9491 static void CheckExitSteel(int x, int y)
9492 {
9493   if (game.gems_still_needed > 0 ||
9494       game.sokoban_fields_still_needed > 0 ||
9495       game.sokoban_objects_still_needed > 0 ||
9496       game.lights_still_needed > 0)
9497   {
9498     int element = Tile[x][y];
9499     int graphic = el2img(element);
9500
9501     if (IS_ANIMATED(graphic))
9502       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9503
9504     return;
9505   }
9506
9507   // do not re-open exit door closed after last player
9508   if (game.all_players_gone)
9509     return;
9510
9511   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9512
9513   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9514 }
9515
9516 static void CheckExitSteelEM(int x, int y)
9517 {
9518   if (game.gems_still_needed > 0 ||
9519       game.sokoban_fields_still_needed > 0 ||
9520       game.sokoban_objects_still_needed > 0 ||
9521       game.lights_still_needed > 0)
9522   {
9523     int element = Tile[x][y];
9524     int graphic = el2img(element);
9525
9526     if (IS_ANIMATED(graphic))
9527       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9528
9529     return;
9530   }
9531
9532   // do not re-open exit door closed after last player
9533   if (game.all_players_gone)
9534     return;
9535
9536   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9537
9538   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9539 }
9540
9541 static void CheckExitSP(int x, int y)
9542 {
9543   if (game.gems_still_needed > 0)
9544   {
9545     int element = Tile[x][y];
9546     int graphic = el2img(element);
9547
9548     if (IS_ANIMATED(graphic))
9549       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9550
9551     return;
9552   }
9553
9554   // do not re-open exit door closed after last player
9555   if (game.all_players_gone)
9556     return;
9557
9558   Tile[x][y] = EL_SP_EXIT_OPENING;
9559
9560   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9561 }
9562
9563 static void CloseAllOpenTimegates(void)
9564 {
9565   int x, y;
9566
9567   SCAN_PLAYFIELD(x, y)
9568   {
9569     int element = Tile[x][y];
9570
9571     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9572     {
9573       Tile[x][y] = EL_TIMEGATE_CLOSING;
9574
9575       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9576     }
9577   }
9578 }
9579
9580 static void DrawTwinkleOnField(int x, int y)
9581 {
9582   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9583     return;
9584
9585   if (Tile[x][y] == EL_BD_DIAMOND)
9586     return;
9587
9588   if (MovDelay[x][y] == 0)      // next animation frame
9589     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9590
9591   if (MovDelay[x][y] != 0)      // wait some time before next frame
9592   {
9593     MovDelay[x][y]--;
9594
9595     DrawLevelElementAnimation(x, y, Tile[x][y]);
9596
9597     if (MovDelay[x][y] != 0)
9598     {
9599       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9600                                            10 - MovDelay[x][y]);
9601
9602       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9603     }
9604   }
9605 }
9606
9607 static void MauerWaechst(int x, int y)
9608 {
9609   int delay = 6;
9610
9611   if (!MovDelay[x][y])          // next animation frame
9612     MovDelay[x][y] = 3 * delay;
9613
9614   if (MovDelay[x][y])           // wait some time before next frame
9615   {
9616     MovDelay[x][y]--;
9617
9618     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9619     {
9620       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9621       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9622
9623       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9624     }
9625
9626     if (!MovDelay[x][y])
9627     {
9628       if (MovDir[x][y] == MV_LEFT)
9629       {
9630         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9631           TEST_DrawLevelField(x - 1, y);
9632       }
9633       else if (MovDir[x][y] == MV_RIGHT)
9634       {
9635         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9636           TEST_DrawLevelField(x + 1, y);
9637       }
9638       else if (MovDir[x][y] == MV_UP)
9639       {
9640         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9641           TEST_DrawLevelField(x, y - 1);
9642       }
9643       else
9644       {
9645         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9646           TEST_DrawLevelField(x, y + 1);
9647       }
9648
9649       Tile[x][y] = Store[x][y];
9650       Store[x][y] = 0;
9651       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9652       TEST_DrawLevelField(x, y);
9653     }
9654   }
9655 }
9656
9657 static void MauerAbleger(int ax, int ay)
9658 {
9659   int element = Tile[ax][ay];
9660   int graphic = el2img(element);
9661   boolean oben_frei = FALSE, unten_frei = FALSE;
9662   boolean links_frei = FALSE, rechts_frei = FALSE;
9663   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9664   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9665   boolean new_wall = FALSE;
9666
9667   if (IS_ANIMATED(graphic))
9668     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9669
9670   if (!MovDelay[ax][ay])        // start building new wall
9671     MovDelay[ax][ay] = 6;
9672
9673   if (MovDelay[ax][ay])         // wait some time before building new wall
9674   {
9675     MovDelay[ax][ay]--;
9676     if (MovDelay[ax][ay])
9677       return;
9678   }
9679
9680   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9681     oben_frei = TRUE;
9682   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9683     unten_frei = TRUE;
9684   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9685     links_frei = TRUE;
9686   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9687     rechts_frei = TRUE;
9688
9689   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9690       element == EL_EXPANDABLE_WALL_ANY)
9691   {
9692     if (oben_frei)
9693     {
9694       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9695       Store[ax][ay-1] = element;
9696       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9697       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9698         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9699                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9700       new_wall = TRUE;
9701     }
9702     if (unten_frei)
9703     {
9704       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9705       Store[ax][ay+1] = element;
9706       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9707       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9708         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9709                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9710       new_wall = TRUE;
9711     }
9712   }
9713
9714   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9715       element == EL_EXPANDABLE_WALL_ANY ||
9716       element == EL_EXPANDABLE_WALL ||
9717       element == EL_BD_EXPANDABLE_WALL)
9718   {
9719     if (links_frei)
9720     {
9721       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9722       Store[ax-1][ay] = element;
9723       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9724       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9725         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9726                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9727       new_wall = TRUE;
9728     }
9729
9730     if (rechts_frei)
9731     {
9732       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9733       Store[ax+1][ay] = element;
9734       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9735       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9736         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9737                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9738       new_wall = TRUE;
9739     }
9740   }
9741
9742   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9743     TEST_DrawLevelField(ax, ay);
9744
9745   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9746     oben_massiv = TRUE;
9747   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9748     unten_massiv = TRUE;
9749   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9750     links_massiv = TRUE;
9751   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9752     rechts_massiv = TRUE;
9753
9754   if (((oben_massiv && unten_massiv) ||
9755        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9756        element == EL_EXPANDABLE_WALL) &&
9757       ((links_massiv && rechts_massiv) ||
9758        element == EL_EXPANDABLE_WALL_VERTICAL))
9759     Tile[ax][ay] = EL_WALL;
9760
9761   if (new_wall)
9762     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9763 }
9764
9765 static void MauerAblegerStahl(int ax, int ay)
9766 {
9767   int element = Tile[ax][ay];
9768   int graphic = el2img(element);
9769   boolean oben_frei = FALSE, unten_frei = FALSE;
9770   boolean links_frei = FALSE, rechts_frei = FALSE;
9771   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9772   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9773   boolean new_wall = FALSE;
9774
9775   if (IS_ANIMATED(graphic))
9776     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9777
9778   if (!MovDelay[ax][ay])        // start building new wall
9779     MovDelay[ax][ay] = 6;
9780
9781   if (MovDelay[ax][ay])         // wait some time before building new wall
9782   {
9783     MovDelay[ax][ay]--;
9784     if (MovDelay[ax][ay])
9785       return;
9786   }
9787
9788   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9789     oben_frei = TRUE;
9790   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9791     unten_frei = TRUE;
9792   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9793     links_frei = TRUE;
9794   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9795     rechts_frei = TRUE;
9796
9797   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9798       element == EL_EXPANDABLE_STEELWALL_ANY)
9799   {
9800     if (oben_frei)
9801     {
9802       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9803       Store[ax][ay-1] = element;
9804       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9805       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9806         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9807                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9808       new_wall = TRUE;
9809     }
9810     if (unten_frei)
9811     {
9812       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9813       Store[ax][ay+1] = element;
9814       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9815       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9816         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9817                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9818       new_wall = TRUE;
9819     }
9820   }
9821
9822   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9823       element == EL_EXPANDABLE_STEELWALL_ANY)
9824   {
9825     if (links_frei)
9826     {
9827       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9828       Store[ax-1][ay] = element;
9829       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9830       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9831         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9832                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9833       new_wall = TRUE;
9834     }
9835
9836     if (rechts_frei)
9837     {
9838       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9839       Store[ax+1][ay] = element;
9840       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9841       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9842         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9843                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9844       new_wall = TRUE;
9845     }
9846   }
9847
9848   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9849     oben_massiv = TRUE;
9850   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9851     unten_massiv = TRUE;
9852   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9853     links_massiv = TRUE;
9854   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9855     rechts_massiv = TRUE;
9856
9857   if (((oben_massiv && unten_massiv) ||
9858        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9859       ((links_massiv && rechts_massiv) ||
9860        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9861     Tile[ax][ay] = EL_STEELWALL;
9862
9863   if (new_wall)
9864     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9865 }
9866
9867 static void CheckForDragon(int x, int y)
9868 {
9869   int i, j;
9870   boolean dragon_found = FALSE;
9871   static int xy[4][2] =
9872   {
9873     { 0, -1 },
9874     { -1, 0 },
9875     { +1, 0 },
9876     { 0, +1 }
9877   };
9878
9879   for (i = 0; i < NUM_DIRECTIONS; i++)
9880   {
9881     for (j = 0; j < 4; j++)
9882     {
9883       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9884
9885       if (IN_LEV_FIELD(xx, yy) &&
9886           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9887       {
9888         if (Tile[xx][yy] == EL_DRAGON)
9889           dragon_found = TRUE;
9890       }
9891       else
9892         break;
9893     }
9894   }
9895
9896   if (!dragon_found)
9897   {
9898     for (i = 0; i < NUM_DIRECTIONS; i++)
9899     {
9900       for (j = 0; j < 3; j++)
9901       {
9902         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9903   
9904         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9905         {
9906           Tile[xx][yy] = EL_EMPTY;
9907           TEST_DrawLevelField(xx, yy);
9908         }
9909         else
9910           break;
9911       }
9912     }
9913   }
9914 }
9915
9916 static void InitBuggyBase(int x, int y)
9917 {
9918   int element = Tile[x][y];
9919   int activating_delay = FRAMES_PER_SECOND / 4;
9920
9921   ChangeDelay[x][y] =
9922     (element == EL_SP_BUGGY_BASE ?
9923      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9924      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9925      activating_delay :
9926      element == EL_SP_BUGGY_BASE_ACTIVE ?
9927      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9928 }
9929
9930 static void WarnBuggyBase(int x, int y)
9931 {
9932   int i;
9933   static int xy[4][2] =
9934   {
9935     { 0, -1 },
9936     { -1, 0 },
9937     { +1, 0 },
9938     { 0, +1 }
9939   };
9940
9941   for (i = 0; i < NUM_DIRECTIONS; i++)
9942   {
9943     int xx = x + xy[i][0];
9944     int yy = y + xy[i][1];
9945
9946     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9947     {
9948       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9949
9950       break;
9951     }
9952   }
9953 }
9954
9955 static void InitTrap(int x, int y)
9956 {
9957   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9958 }
9959
9960 static void ActivateTrap(int x, int y)
9961 {
9962   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9963 }
9964
9965 static void ChangeActiveTrap(int x, int y)
9966 {
9967   int graphic = IMG_TRAP_ACTIVE;
9968
9969   // if new animation frame was drawn, correct crumbled sand border
9970   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9971     TEST_DrawLevelFieldCrumbled(x, y);
9972 }
9973
9974 static int getSpecialActionElement(int element, int number, int base_element)
9975 {
9976   return (element != EL_EMPTY ? element :
9977           number != -1 ? base_element + number - 1 :
9978           EL_EMPTY);
9979 }
9980
9981 static int getModifiedActionNumber(int value_old, int operator, int operand,
9982                                    int value_min, int value_max)
9983 {
9984   int value_new = (operator == CA_MODE_SET      ? operand :
9985                    operator == CA_MODE_ADD      ? value_old + operand :
9986                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9987                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9988                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9989                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9990                    value_old);
9991
9992   return (value_new < value_min ? value_min :
9993           value_new > value_max ? value_max :
9994           value_new);
9995 }
9996
9997 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9998 {
9999   struct ElementInfo *ei = &element_info[element];
10000   struct ElementChangeInfo *change = &ei->change_page[page];
10001   int target_element = change->target_element;
10002   int action_type = change->action_type;
10003   int action_mode = change->action_mode;
10004   int action_arg = change->action_arg;
10005   int action_element = change->action_element;
10006   int i;
10007
10008   if (!change->has_action)
10009     return;
10010
10011   // ---------- determine action paramater values -----------------------------
10012
10013   int level_time_value =
10014     (level.time > 0 ? TimeLeft :
10015      TimePlayed);
10016
10017   int action_arg_element_raw =
10018     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10019      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10020      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10021      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10022      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10023      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10024      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10025      EL_EMPTY);
10026   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10027
10028   int action_arg_direction =
10029     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10030      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10031      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10032      change->actual_trigger_side :
10033      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10034      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10035      MV_NONE);
10036
10037   int action_arg_number_min =
10038     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10039      CA_ARG_MIN);
10040
10041   int action_arg_number_max =
10042     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10043      action_type == CA_SET_LEVEL_GEMS ? 999 :
10044      action_type == CA_SET_LEVEL_TIME ? 9999 :
10045      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10046      action_type == CA_SET_CE_VALUE ? 9999 :
10047      action_type == CA_SET_CE_SCORE ? 9999 :
10048      CA_ARG_MAX);
10049
10050   int action_arg_number_reset =
10051     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10052      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10053      action_type == CA_SET_LEVEL_TIME ? level.time :
10054      action_type == CA_SET_LEVEL_SCORE ? 0 :
10055      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10056      action_type == CA_SET_CE_SCORE ? 0 :
10057      0);
10058
10059   int action_arg_number =
10060     (action_arg <= CA_ARG_MAX ? action_arg :
10061      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10062      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10063      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10064      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10065      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10066      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10067      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10068      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10069      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10070      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10071      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10072      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10073      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10074      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10075      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10076      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10077      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10078      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10079      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10080      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10081      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10082      -1);
10083
10084   int action_arg_number_old =
10085     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10086      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10087      action_type == CA_SET_LEVEL_SCORE ? game.score :
10088      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10089      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10090      0);
10091
10092   int action_arg_number_new =
10093     getModifiedActionNumber(action_arg_number_old,
10094                             action_mode, action_arg_number,
10095                             action_arg_number_min, action_arg_number_max);
10096
10097   int trigger_player_bits =
10098     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10099      change->actual_trigger_player_bits : change->trigger_player);
10100
10101   int action_arg_player_bits =
10102     (action_arg >= CA_ARG_PLAYER_1 &&
10103      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10104      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10105      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10106      PLAYER_BITS_ANY);
10107
10108   // ---------- execute action  -----------------------------------------------
10109
10110   switch (action_type)
10111   {
10112     case CA_NO_ACTION:
10113     {
10114       return;
10115     }
10116
10117     // ---------- level actions  ----------------------------------------------
10118
10119     case CA_RESTART_LEVEL:
10120     {
10121       game.restart_level = TRUE;
10122
10123       break;
10124     }
10125
10126     case CA_SHOW_ENVELOPE:
10127     {
10128       int element = getSpecialActionElement(action_arg_element,
10129                                             action_arg_number, EL_ENVELOPE_1);
10130
10131       if (IS_ENVELOPE(element))
10132         local_player->show_envelope = element;
10133
10134       break;
10135     }
10136
10137     case CA_SET_LEVEL_TIME:
10138     {
10139       if (level.time > 0)       // only modify limited time value
10140       {
10141         TimeLeft = action_arg_number_new;
10142
10143         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10144
10145         DisplayGameControlValues();
10146
10147         if (!TimeLeft && setup.time_limit)
10148           for (i = 0; i < MAX_PLAYERS; i++)
10149             KillPlayer(&stored_player[i]);
10150       }
10151
10152       break;
10153     }
10154
10155     case CA_SET_LEVEL_SCORE:
10156     {
10157       game.score = action_arg_number_new;
10158
10159       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10160
10161       DisplayGameControlValues();
10162
10163       break;
10164     }
10165
10166     case CA_SET_LEVEL_GEMS:
10167     {
10168       game.gems_still_needed = action_arg_number_new;
10169
10170       game.snapshot.collected_item = TRUE;
10171
10172       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10173
10174       DisplayGameControlValues();
10175
10176       break;
10177     }
10178
10179     case CA_SET_LEVEL_WIND:
10180     {
10181       game.wind_direction = action_arg_direction;
10182
10183       break;
10184     }
10185
10186     case CA_SET_LEVEL_RANDOM_SEED:
10187     {
10188       // ensure that setting a new random seed while playing is predictable
10189       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10190
10191       break;
10192     }
10193
10194     // ---------- player actions  ---------------------------------------------
10195
10196     case CA_MOVE_PLAYER:
10197     case CA_MOVE_PLAYER_NEW:
10198     {
10199       // automatically move to the next field in specified direction
10200       for (i = 0; i < MAX_PLAYERS; i++)
10201         if (trigger_player_bits & (1 << i))
10202           if (action_type == CA_MOVE_PLAYER ||
10203               stored_player[i].MovPos == 0)
10204             stored_player[i].programmed_action = action_arg_direction;
10205
10206       break;
10207     }
10208
10209     case CA_EXIT_PLAYER:
10210     {
10211       for (i = 0; i < MAX_PLAYERS; i++)
10212         if (action_arg_player_bits & (1 << i))
10213           ExitPlayer(&stored_player[i]);
10214
10215       if (game.players_still_needed == 0)
10216         LevelSolved();
10217
10218       break;
10219     }
10220
10221     case CA_KILL_PLAYER:
10222     {
10223       for (i = 0; i < MAX_PLAYERS; i++)
10224         if (action_arg_player_bits & (1 << i))
10225           KillPlayer(&stored_player[i]);
10226
10227       break;
10228     }
10229
10230     case CA_SET_PLAYER_KEYS:
10231     {
10232       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10233       int element = getSpecialActionElement(action_arg_element,
10234                                             action_arg_number, EL_KEY_1);
10235
10236       if (IS_KEY(element))
10237       {
10238         for (i = 0; i < MAX_PLAYERS; i++)
10239         {
10240           if (trigger_player_bits & (1 << i))
10241           {
10242             stored_player[i].key[KEY_NR(element)] = key_state;
10243
10244             DrawGameDoorValues();
10245           }
10246         }
10247       }
10248
10249       break;
10250     }
10251
10252     case CA_SET_PLAYER_SPEED:
10253     {
10254       for (i = 0; i < MAX_PLAYERS; i++)
10255       {
10256         if (trigger_player_bits & (1 << i))
10257         {
10258           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10259
10260           if (action_arg == CA_ARG_SPEED_FASTER &&
10261               stored_player[i].cannot_move)
10262           {
10263             action_arg_number = STEPSIZE_VERY_SLOW;
10264           }
10265           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10266                    action_arg == CA_ARG_SPEED_FASTER)
10267           {
10268             action_arg_number = 2;
10269             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10270                            CA_MODE_MULTIPLY);
10271           }
10272           else if (action_arg == CA_ARG_NUMBER_RESET)
10273           {
10274             action_arg_number = level.initial_player_stepsize[i];
10275           }
10276
10277           move_stepsize =
10278             getModifiedActionNumber(move_stepsize,
10279                                     action_mode,
10280                                     action_arg_number,
10281                                     action_arg_number_min,
10282                                     action_arg_number_max);
10283
10284           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10285         }
10286       }
10287
10288       break;
10289     }
10290
10291     case CA_SET_PLAYER_SHIELD:
10292     {
10293       for (i = 0; i < MAX_PLAYERS; i++)
10294       {
10295         if (trigger_player_bits & (1 << i))
10296         {
10297           if (action_arg == CA_ARG_SHIELD_OFF)
10298           {
10299             stored_player[i].shield_normal_time_left = 0;
10300             stored_player[i].shield_deadly_time_left = 0;
10301           }
10302           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10303           {
10304             stored_player[i].shield_normal_time_left = 999999;
10305           }
10306           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10307           {
10308             stored_player[i].shield_normal_time_left = 999999;
10309             stored_player[i].shield_deadly_time_left = 999999;
10310           }
10311         }
10312       }
10313
10314       break;
10315     }
10316
10317     case CA_SET_PLAYER_GRAVITY:
10318     {
10319       for (i = 0; i < MAX_PLAYERS; i++)
10320       {
10321         if (trigger_player_bits & (1 << i))
10322         {
10323           stored_player[i].gravity =
10324             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10325              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10326              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10327              stored_player[i].gravity);
10328         }
10329       }
10330
10331       break;
10332     }
10333
10334     case CA_SET_PLAYER_ARTWORK:
10335     {
10336       for (i = 0; i < MAX_PLAYERS; i++)
10337       {
10338         if (trigger_player_bits & (1 << i))
10339         {
10340           int artwork_element = action_arg_element;
10341
10342           if (action_arg == CA_ARG_ELEMENT_RESET)
10343             artwork_element =
10344               (level.use_artwork_element[i] ? level.artwork_element[i] :
10345                stored_player[i].element_nr);
10346
10347           if (stored_player[i].artwork_element != artwork_element)
10348             stored_player[i].Frame = 0;
10349
10350           stored_player[i].artwork_element = artwork_element;
10351
10352           SetPlayerWaiting(&stored_player[i], FALSE);
10353
10354           // set number of special actions for bored and sleeping animation
10355           stored_player[i].num_special_action_bored =
10356             get_num_special_action(artwork_element,
10357                                    ACTION_BORING_1, ACTION_BORING_LAST);
10358           stored_player[i].num_special_action_sleeping =
10359             get_num_special_action(artwork_element,
10360                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10361         }
10362       }
10363
10364       break;
10365     }
10366
10367     case CA_SET_PLAYER_INVENTORY:
10368     {
10369       for (i = 0; i < MAX_PLAYERS; i++)
10370       {
10371         struct PlayerInfo *player = &stored_player[i];
10372         int j, k;
10373
10374         if (trigger_player_bits & (1 << i))
10375         {
10376           int inventory_element = action_arg_element;
10377
10378           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10379               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10380               action_arg == CA_ARG_ELEMENT_ACTION)
10381           {
10382             int element = inventory_element;
10383             int collect_count = element_info[element].collect_count_initial;
10384
10385             if (!IS_CUSTOM_ELEMENT(element))
10386               collect_count = 1;
10387
10388             if (collect_count == 0)
10389               player->inventory_infinite_element = element;
10390             else
10391               for (k = 0; k < collect_count; k++)
10392                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10393                   player->inventory_element[player->inventory_size++] =
10394                     element;
10395           }
10396           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10397                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10398                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10399           {
10400             if (player->inventory_infinite_element != EL_UNDEFINED &&
10401                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10402                                      action_arg_element_raw))
10403               player->inventory_infinite_element = EL_UNDEFINED;
10404
10405             for (k = 0, j = 0; j < player->inventory_size; j++)
10406             {
10407               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10408                                         action_arg_element_raw))
10409                 player->inventory_element[k++] = player->inventory_element[j];
10410             }
10411
10412             player->inventory_size = k;
10413           }
10414           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10415           {
10416             if (player->inventory_size > 0)
10417             {
10418               for (j = 0; j < player->inventory_size - 1; j++)
10419                 player->inventory_element[j] = player->inventory_element[j + 1];
10420
10421               player->inventory_size--;
10422             }
10423           }
10424           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10425           {
10426             if (player->inventory_size > 0)
10427               player->inventory_size--;
10428           }
10429           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10430           {
10431             player->inventory_infinite_element = EL_UNDEFINED;
10432             player->inventory_size = 0;
10433           }
10434           else if (action_arg == CA_ARG_INVENTORY_RESET)
10435           {
10436             player->inventory_infinite_element = EL_UNDEFINED;
10437             player->inventory_size = 0;
10438
10439             if (level.use_initial_inventory[i])
10440             {
10441               for (j = 0; j < level.initial_inventory_size[i]; j++)
10442               {
10443                 int element = level.initial_inventory_content[i][j];
10444                 int collect_count = element_info[element].collect_count_initial;
10445
10446                 if (!IS_CUSTOM_ELEMENT(element))
10447                   collect_count = 1;
10448
10449                 if (collect_count == 0)
10450                   player->inventory_infinite_element = element;
10451                 else
10452                   for (k = 0; k < collect_count; k++)
10453                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10454                       player->inventory_element[player->inventory_size++] =
10455                         element;
10456               }
10457             }
10458           }
10459         }
10460       }
10461
10462       break;
10463     }
10464
10465     // ---------- CE actions  -------------------------------------------------
10466
10467     case CA_SET_CE_VALUE:
10468     {
10469       int last_ce_value = CustomValue[x][y];
10470
10471       CustomValue[x][y] = action_arg_number_new;
10472
10473       if (CustomValue[x][y] != last_ce_value)
10474       {
10475         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10476         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10477
10478         if (CustomValue[x][y] == 0)
10479         {
10480           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10481           ChangeCount[x][y] = 0;        // allow at least one more change
10482
10483           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10484           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10485         }
10486       }
10487
10488       break;
10489     }
10490
10491     case CA_SET_CE_SCORE:
10492     {
10493       int last_ce_score = ei->collect_score;
10494
10495       ei->collect_score = action_arg_number_new;
10496
10497       if (ei->collect_score != last_ce_score)
10498       {
10499         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10500         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10501
10502         if (ei->collect_score == 0)
10503         {
10504           int xx, yy;
10505
10506           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10507           ChangeCount[x][y] = 0;        // allow at least one more change
10508
10509           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10510           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10511
10512           /*
10513             This is a very special case that seems to be a mixture between
10514             CheckElementChange() and CheckTriggeredElementChange(): while
10515             the first one only affects single elements that are triggered
10516             directly, the second one affects multiple elements in the playfield
10517             that are triggered indirectly by another element. This is a third
10518             case: Changing the CE score always affects multiple identical CEs,
10519             so every affected CE must be checked, not only the single CE for
10520             which the CE score was changed in the first place (as every instance
10521             of that CE shares the same CE score, and therefore also can change)!
10522           */
10523           SCAN_PLAYFIELD(xx, yy)
10524           {
10525             if (Tile[xx][yy] == element)
10526               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10527                                  CE_SCORE_GETS_ZERO);
10528           }
10529         }
10530       }
10531
10532       break;
10533     }
10534
10535     case CA_SET_CE_ARTWORK:
10536     {
10537       int artwork_element = action_arg_element;
10538       boolean reset_frame = FALSE;
10539       int xx, yy;
10540
10541       if (action_arg == CA_ARG_ELEMENT_RESET)
10542         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10543                            element);
10544
10545       if (ei->gfx_element != artwork_element)
10546         reset_frame = TRUE;
10547
10548       ei->gfx_element = artwork_element;
10549
10550       SCAN_PLAYFIELD(xx, yy)
10551       {
10552         if (Tile[xx][yy] == element)
10553         {
10554           if (reset_frame)
10555           {
10556             ResetGfxAnimation(xx, yy);
10557             ResetRandomAnimationValue(xx, yy);
10558           }
10559
10560           TEST_DrawLevelField(xx, yy);
10561         }
10562       }
10563
10564       break;
10565     }
10566
10567     // ---------- engine actions  ---------------------------------------------
10568
10569     case CA_SET_ENGINE_SCAN_MODE:
10570     {
10571       InitPlayfieldScanMode(action_arg);
10572
10573       break;
10574     }
10575
10576     default:
10577       break;
10578   }
10579 }
10580
10581 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10582 {
10583   int old_element = Tile[x][y];
10584   int new_element = GetElementFromGroupElement(element);
10585   int previous_move_direction = MovDir[x][y];
10586   int last_ce_value = CustomValue[x][y];
10587   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10588   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10589   boolean add_player_onto_element = (new_element_is_player &&
10590                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10591                                      IS_WALKABLE(old_element));
10592
10593   if (!add_player_onto_element)
10594   {
10595     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10596       RemoveMovingField(x, y);
10597     else
10598       RemoveField(x, y);
10599
10600     Tile[x][y] = new_element;
10601
10602     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10603       MovDir[x][y] = previous_move_direction;
10604
10605     if (element_info[new_element].use_last_ce_value)
10606       CustomValue[x][y] = last_ce_value;
10607
10608     InitField_WithBug1(x, y, FALSE);
10609
10610     new_element = Tile[x][y];   // element may have changed
10611
10612     ResetGfxAnimation(x, y);
10613     ResetRandomAnimationValue(x, y);
10614
10615     TEST_DrawLevelField(x, y);
10616
10617     if (GFX_CRUMBLED(new_element))
10618       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10619   }
10620
10621   // check if element under the player changes from accessible to unaccessible
10622   // (needed for special case of dropping element which then changes)
10623   // (must be checked after creating new element for walkable group elements)
10624   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10625       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10626   {
10627     Bang(x, y);
10628
10629     return;
10630   }
10631
10632   // "ChangeCount" not set yet to allow "entered by player" change one time
10633   if (new_element_is_player)
10634     RelocatePlayer(x, y, new_element);
10635
10636   if (is_change)
10637     ChangeCount[x][y]++;        // count number of changes in the same frame
10638
10639   TestIfBadThingTouchesPlayer(x, y);
10640   TestIfPlayerTouchesCustomElement(x, y);
10641   TestIfElementTouchesCustomElement(x, y);
10642 }
10643
10644 static void CreateField(int x, int y, int element)
10645 {
10646   CreateFieldExt(x, y, element, FALSE);
10647 }
10648
10649 static void CreateElementFromChange(int x, int y, int element)
10650 {
10651   element = GET_VALID_RUNTIME_ELEMENT(element);
10652
10653   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10654   {
10655     int old_element = Tile[x][y];
10656
10657     // prevent changed element from moving in same engine frame
10658     // unless both old and new element can either fall or move
10659     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10660         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10661       Stop[x][y] = TRUE;
10662   }
10663
10664   CreateFieldExt(x, y, element, TRUE);
10665 }
10666
10667 static boolean ChangeElement(int x, int y, int element, int page)
10668 {
10669   struct ElementInfo *ei = &element_info[element];
10670   struct ElementChangeInfo *change = &ei->change_page[page];
10671   int ce_value = CustomValue[x][y];
10672   int ce_score = ei->collect_score;
10673   int target_element;
10674   int old_element = Tile[x][y];
10675
10676   // always use default change event to prevent running into a loop
10677   if (ChangeEvent[x][y] == -1)
10678     ChangeEvent[x][y] = CE_DELAY;
10679
10680   if (ChangeEvent[x][y] == CE_DELAY)
10681   {
10682     // reset actual trigger element, trigger player and action element
10683     change->actual_trigger_element = EL_EMPTY;
10684     change->actual_trigger_player = EL_EMPTY;
10685     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10686     change->actual_trigger_side = CH_SIDE_NONE;
10687     change->actual_trigger_ce_value = 0;
10688     change->actual_trigger_ce_score = 0;
10689   }
10690
10691   // do not change elements more than a specified maximum number of changes
10692   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10693     return FALSE;
10694
10695   ChangeCount[x][y]++;          // count number of changes in the same frame
10696
10697   if (change->explode)
10698   {
10699     Bang(x, y);
10700
10701     return TRUE;
10702   }
10703
10704   if (change->use_target_content)
10705   {
10706     boolean complete_replace = TRUE;
10707     boolean can_replace[3][3];
10708     int xx, yy;
10709
10710     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10711     {
10712       boolean is_empty;
10713       boolean is_walkable;
10714       boolean is_diggable;
10715       boolean is_collectible;
10716       boolean is_removable;
10717       boolean is_destructible;
10718       int ex = x + xx - 1;
10719       int ey = y + yy - 1;
10720       int content_element = change->target_content.e[xx][yy];
10721       int e;
10722
10723       can_replace[xx][yy] = TRUE;
10724
10725       if (ex == x && ey == y)   // do not check changing element itself
10726         continue;
10727
10728       if (content_element == EL_EMPTY_SPACE)
10729       {
10730         can_replace[xx][yy] = FALSE;    // do not replace border with space
10731
10732         continue;
10733       }
10734
10735       if (!IN_LEV_FIELD(ex, ey))
10736       {
10737         can_replace[xx][yy] = FALSE;
10738         complete_replace = FALSE;
10739
10740         continue;
10741       }
10742
10743       e = Tile[ex][ey];
10744
10745       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10746         e = MovingOrBlocked2Element(ex, ey);
10747
10748       is_empty = (IS_FREE(ex, ey) ||
10749                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10750
10751       is_walkable     = (is_empty || IS_WALKABLE(e));
10752       is_diggable     = (is_empty || IS_DIGGABLE(e));
10753       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10754       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10755       is_removable    = (is_diggable || is_collectible);
10756
10757       can_replace[xx][yy] =
10758         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10759           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10760           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10761           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10762           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10763           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10764          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10765
10766       if (!can_replace[xx][yy])
10767         complete_replace = FALSE;
10768     }
10769
10770     if (!change->only_if_complete || complete_replace)
10771     {
10772       boolean something_has_changed = FALSE;
10773
10774       if (change->only_if_complete && change->use_random_replace &&
10775           RND(100) < change->random_percentage)
10776         return FALSE;
10777
10778       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10779       {
10780         int ex = x + xx - 1;
10781         int ey = y + yy - 1;
10782         int content_element;
10783
10784         if (can_replace[xx][yy] && (!change->use_random_replace ||
10785                                     RND(100) < change->random_percentage))
10786         {
10787           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10788             RemoveMovingField(ex, ey);
10789
10790           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10791
10792           content_element = change->target_content.e[xx][yy];
10793           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10794                                               ce_value, ce_score);
10795
10796           CreateElementFromChange(ex, ey, target_element);
10797
10798           something_has_changed = TRUE;
10799
10800           // for symmetry reasons, freeze newly created border elements
10801           if (ex != x || ey != y)
10802             Stop[ex][ey] = TRUE;        // no more moving in this frame
10803         }
10804       }
10805
10806       if (something_has_changed)
10807       {
10808         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10809         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10810       }
10811     }
10812   }
10813   else
10814   {
10815     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10816                                         ce_value, ce_score);
10817
10818     if (element == EL_DIAGONAL_GROWING ||
10819         element == EL_DIAGONAL_SHRINKING)
10820     {
10821       target_element = Store[x][y];
10822
10823       Store[x][y] = EL_EMPTY;
10824     }
10825
10826     CreateElementFromChange(x, y, target_element);
10827
10828     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10829     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10830   }
10831
10832   // this uses direct change before indirect change
10833   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10834
10835   return TRUE;
10836 }
10837
10838 static void HandleElementChange(int x, int y, int page)
10839 {
10840   int element = MovingOrBlocked2Element(x, y);
10841   struct ElementInfo *ei = &element_info[element];
10842   struct ElementChangeInfo *change = &ei->change_page[page];
10843   boolean handle_action_before_change = FALSE;
10844
10845 #ifdef DEBUG
10846   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10847       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10848   {
10849     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10850           x, y, element, element_info[element].token_name);
10851     Debug("game:playing:HandleElementChange", "This should never happen!");
10852   }
10853 #endif
10854
10855   // this can happen with classic bombs on walkable, changing elements
10856   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10857   {
10858     return;
10859   }
10860
10861   if (ChangeDelay[x][y] == 0)           // initialize element change
10862   {
10863     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10864
10865     if (change->can_change)
10866     {
10867       // !!! not clear why graphic animation should be reset at all here !!!
10868       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10869       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10870
10871       /*
10872         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10873
10874         When using an animation frame delay of 1 (this only happens with
10875         "sp_zonk.moving.left/right" in the classic graphics), the default
10876         (non-moving) animation shows wrong animation frames (while the
10877         moving animation, like "sp_zonk.moving.left/right", is correct,
10878         so this graphical bug never shows up with the classic graphics).
10879         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10880         be drawn instead of the correct frames 0,1,2,3. This is caused by
10881         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10882         an element change: First when the change delay ("ChangeDelay[][]")
10883         counter has reached zero after decrementing, then a second time in
10884         the next frame (after "GfxFrame[][]" was already incremented) when
10885         "ChangeDelay[][]" is reset to the initial delay value again.
10886
10887         This causes frame 0 to be drawn twice, while the last frame won't
10888         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10889
10890         As some animations may already be cleverly designed around this bug
10891         (at least the "Snake Bite" snake tail animation does this), it cannot
10892         simply be fixed here without breaking such existing animations.
10893         Unfortunately, it cannot easily be detected if a graphics set was
10894         designed "before" or "after" the bug was fixed. As a workaround,
10895         a new graphics set option "game.graphics_engine_version" was added
10896         to be able to specify the game's major release version for which the
10897         graphics set was designed, which can then be used to decide if the
10898         bugfix should be used (version 4 and above) or not (version 3 or
10899         below, or if no version was specified at all, as with old sets).
10900
10901         (The wrong/fixed animation frames can be tested with the test level set
10902         "test_gfxframe" and level "000", which contains a specially prepared
10903         custom element at level position (x/y) == (11/9) which uses the zonk
10904         animation mentioned above. Using "game.graphics_engine_version: 4"
10905         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10906         This can also be seen from the debug output for this test element.)
10907       */
10908
10909       // when a custom element is about to change (for example by change delay),
10910       // do not reset graphic animation when the custom element is moving
10911       if (game.graphics_engine_version < 4 &&
10912           !IS_MOVING(x, y))
10913       {
10914         ResetGfxAnimation(x, y);
10915         ResetRandomAnimationValue(x, y);
10916       }
10917
10918       if (change->pre_change_function)
10919         change->pre_change_function(x, y);
10920     }
10921   }
10922
10923   ChangeDelay[x][y]--;
10924
10925   if (ChangeDelay[x][y] != 0)           // continue element change
10926   {
10927     if (change->can_change)
10928     {
10929       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10930
10931       if (IS_ANIMATED(graphic))
10932         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10933
10934       if (change->change_function)
10935         change->change_function(x, y);
10936     }
10937   }
10938   else                                  // finish element change
10939   {
10940     if (ChangePage[x][y] != -1)         // remember page from delayed change
10941     {
10942       page = ChangePage[x][y];
10943       ChangePage[x][y] = -1;
10944
10945       change = &ei->change_page[page];
10946     }
10947
10948     if (IS_MOVING(x, y))                // never change a running system ;-)
10949     {
10950       ChangeDelay[x][y] = 1;            // try change after next move step
10951       ChangePage[x][y] = page;          // remember page to use for change
10952
10953       return;
10954     }
10955
10956     // special case: set new level random seed before changing element
10957     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10958       handle_action_before_change = TRUE;
10959
10960     if (change->has_action && handle_action_before_change)
10961       ExecuteCustomElementAction(x, y, element, page);
10962
10963     if (change->can_change)
10964     {
10965       if (ChangeElement(x, y, element, page))
10966       {
10967         if (change->post_change_function)
10968           change->post_change_function(x, y);
10969       }
10970     }
10971
10972     if (change->has_action && !handle_action_before_change)
10973       ExecuteCustomElementAction(x, y, element, page);
10974   }
10975 }
10976
10977 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10978                                               int trigger_element,
10979                                               int trigger_event,
10980                                               int trigger_player,
10981                                               int trigger_side,
10982                                               int trigger_page)
10983 {
10984   boolean change_done_any = FALSE;
10985   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10986   int i;
10987
10988   if (!(trigger_events[trigger_element][trigger_event]))
10989     return FALSE;
10990
10991   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10992
10993   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10994   {
10995     int element = EL_CUSTOM_START + i;
10996     boolean change_done = FALSE;
10997     int p;
10998
10999     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11000         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11001       continue;
11002
11003     for (p = 0; p < element_info[element].num_change_pages; p++)
11004     {
11005       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11006
11007       if (change->can_change_or_has_action &&
11008           change->has_event[trigger_event] &&
11009           change->trigger_side & trigger_side &&
11010           change->trigger_player & trigger_player &&
11011           change->trigger_page & trigger_page_bits &&
11012           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11013       {
11014         change->actual_trigger_element = trigger_element;
11015         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11016         change->actual_trigger_player_bits = trigger_player;
11017         change->actual_trigger_side = trigger_side;
11018         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11019         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11020
11021         if ((change->can_change && !change_done) || change->has_action)
11022         {
11023           int x, y;
11024
11025           SCAN_PLAYFIELD(x, y)
11026           {
11027             if (Tile[x][y] == element)
11028             {
11029               if (change->can_change && !change_done)
11030               {
11031                 // if element already changed in this frame, not only prevent
11032                 // another element change (checked in ChangeElement()), but
11033                 // also prevent additional element actions for this element
11034
11035                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11036                     !level.use_action_after_change_bug)
11037                   continue;
11038
11039                 ChangeDelay[x][y] = 1;
11040                 ChangeEvent[x][y] = trigger_event;
11041
11042                 HandleElementChange(x, y, p);
11043               }
11044               else if (change->has_action)
11045               {
11046                 // if element already changed in this frame, not only prevent
11047                 // another element change (checked in ChangeElement()), but
11048                 // also prevent additional element actions for this element
11049
11050                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11051                     !level.use_action_after_change_bug)
11052                   continue;
11053
11054                 ExecuteCustomElementAction(x, y, element, p);
11055                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11056               }
11057             }
11058           }
11059
11060           if (change->can_change)
11061           {
11062             change_done = TRUE;
11063             change_done_any = TRUE;
11064           }
11065         }
11066       }
11067     }
11068   }
11069
11070   RECURSION_LOOP_DETECTION_END();
11071
11072   return change_done_any;
11073 }
11074
11075 static boolean CheckElementChangeExt(int x, int y,
11076                                      int element,
11077                                      int trigger_element,
11078                                      int trigger_event,
11079                                      int trigger_player,
11080                                      int trigger_side)
11081 {
11082   boolean change_done = FALSE;
11083   int p;
11084
11085   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11086       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11087     return FALSE;
11088
11089   if (Tile[x][y] == EL_BLOCKED)
11090   {
11091     Blocked2Moving(x, y, &x, &y);
11092     element = Tile[x][y];
11093   }
11094
11095   // check if element has already changed or is about to change after moving
11096   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11097        Tile[x][y] != element) ||
11098
11099       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11100        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11101         ChangePage[x][y] != -1)))
11102     return FALSE;
11103
11104   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11105
11106   for (p = 0; p < element_info[element].num_change_pages; p++)
11107   {
11108     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11109
11110     /* check trigger element for all events where the element that is checked
11111        for changing interacts with a directly adjacent element -- this is
11112        different to element changes that affect other elements to change on the
11113        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11114     boolean check_trigger_element =
11115       (trigger_event == CE_TOUCHING_X ||
11116        trigger_event == CE_HITTING_X ||
11117        trigger_event == CE_HIT_BY_X ||
11118        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11119
11120     if (change->can_change_or_has_action &&
11121         change->has_event[trigger_event] &&
11122         change->trigger_side & trigger_side &&
11123         change->trigger_player & trigger_player &&
11124         (!check_trigger_element ||
11125          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11126     {
11127       change->actual_trigger_element = trigger_element;
11128       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11129       change->actual_trigger_player_bits = trigger_player;
11130       change->actual_trigger_side = trigger_side;
11131       change->actual_trigger_ce_value = CustomValue[x][y];
11132       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11133
11134       // special case: trigger element not at (x,y) position for some events
11135       if (check_trigger_element)
11136       {
11137         static struct
11138         {
11139           int dx, dy;
11140         } move_xy[] =
11141           {
11142             {  0,  0 },
11143             { -1,  0 },
11144             { +1,  0 },
11145             {  0,  0 },
11146             {  0, -1 },
11147             {  0,  0 }, { 0, 0 }, { 0, 0 },
11148             {  0, +1 }
11149           };
11150
11151         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11152         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11153
11154         change->actual_trigger_ce_value = CustomValue[xx][yy];
11155         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11156       }
11157
11158       if (change->can_change && !change_done)
11159       {
11160         ChangeDelay[x][y] = 1;
11161         ChangeEvent[x][y] = trigger_event;
11162
11163         HandleElementChange(x, y, p);
11164
11165         change_done = TRUE;
11166       }
11167       else if (change->has_action)
11168       {
11169         ExecuteCustomElementAction(x, y, element, p);
11170         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11171       }
11172     }
11173   }
11174
11175   RECURSION_LOOP_DETECTION_END();
11176
11177   return change_done;
11178 }
11179
11180 static void PlayPlayerSound(struct PlayerInfo *player)
11181 {
11182   int jx = player->jx, jy = player->jy;
11183   int sound_element = player->artwork_element;
11184   int last_action = player->last_action_waiting;
11185   int action = player->action_waiting;
11186
11187   if (player->is_waiting)
11188   {
11189     if (action != last_action)
11190       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11191     else
11192       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11193   }
11194   else
11195   {
11196     if (action != last_action)
11197       StopSound(element_info[sound_element].sound[last_action]);
11198
11199     if (last_action == ACTION_SLEEPING)
11200       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11201   }
11202 }
11203
11204 static void PlayAllPlayersSound(void)
11205 {
11206   int i;
11207
11208   for (i = 0; i < MAX_PLAYERS; i++)
11209     if (stored_player[i].active)
11210       PlayPlayerSound(&stored_player[i]);
11211 }
11212
11213 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11214 {
11215   boolean last_waiting = player->is_waiting;
11216   int move_dir = player->MovDir;
11217
11218   player->dir_waiting = move_dir;
11219   player->last_action_waiting = player->action_waiting;
11220
11221   if (is_waiting)
11222   {
11223     if (!last_waiting)          // not waiting -> waiting
11224     {
11225       player->is_waiting = TRUE;
11226
11227       player->frame_counter_bored =
11228         FrameCounter +
11229         game.player_boring_delay_fixed +
11230         GetSimpleRandom(game.player_boring_delay_random);
11231       player->frame_counter_sleeping =
11232         FrameCounter +
11233         game.player_sleeping_delay_fixed +
11234         GetSimpleRandom(game.player_sleeping_delay_random);
11235
11236       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11237     }
11238
11239     if (game.player_sleeping_delay_fixed +
11240         game.player_sleeping_delay_random > 0 &&
11241         player->anim_delay_counter == 0 &&
11242         player->post_delay_counter == 0 &&
11243         FrameCounter >= player->frame_counter_sleeping)
11244       player->is_sleeping = TRUE;
11245     else if (game.player_boring_delay_fixed +
11246              game.player_boring_delay_random > 0 &&
11247              FrameCounter >= player->frame_counter_bored)
11248       player->is_bored = TRUE;
11249
11250     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11251                               player->is_bored ? ACTION_BORING :
11252                               ACTION_WAITING);
11253
11254     if (player->is_sleeping && player->use_murphy)
11255     {
11256       // special case for sleeping Murphy when leaning against non-free tile
11257
11258       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11259           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11260            !IS_MOVING(player->jx - 1, player->jy)))
11261         move_dir = MV_LEFT;
11262       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11263                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11264                 !IS_MOVING(player->jx + 1, player->jy)))
11265         move_dir = MV_RIGHT;
11266       else
11267         player->is_sleeping = FALSE;
11268
11269       player->dir_waiting = move_dir;
11270     }
11271
11272     if (player->is_sleeping)
11273     {
11274       if (player->num_special_action_sleeping > 0)
11275       {
11276         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11277         {
11278           int last_special_action = player->special_action_sleeping;
11279           int num_special_action = player->num_special_action_sleeping;
11280           int special_action =
11281             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11282              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11283              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11284              last_special_action + 1 : ACTION_SLEEPING);
11285           int special_graphic =
11286             el_act_dir2img(player->artwork_element, special_action, move_dir);
11287
11288           player->anim_delay_counter =
11289             graphic_info[special_graphic].anim_delay_fixed +
11290             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11291           player->post_delay_counter =
11292             graphic_info[special_graphic].post_delay_fixed +
11293             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11294
11295           player->special_action_sleeping = special_action;
11296         }
11297
11298         if (player->anim_delay_counter > 0)
11299         {
11300           player->action_waiting = player->special_action_sleeping;
11301           player->anim_delay_counter--;
11302         }
11303         else if (player->post_delay_counter > 0)
11304         {
11305           player->post_delay_counter--;
11306         }
11307       }
11308     }
11309     else if (player->is_bored)
11310     {
11311       if (player->num_special_action_bored > 0)
11312       {
11313         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11314         {
11315           int special_action =
11316             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11317           int special_graphic =
11318             el_act_dir2img(player->artwork_element, special_action, move_dir);
11319
11320           player->anim_delay_counter =
11321             graphic_info[special_graphic].anim_delay_fixed +
11322             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11323           player->post_delay_counter =
11324             graphic_info[special_graphic].post_delay_fixed +
11325             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11326
11327           player->special_action_bored = special_action;
11328         }
11329
11330         if (player->anim_delay_counter > 0)
11331         {
11332           player->action_waiting = player->special_action_bored;
11333           player->anim_delay_counter--;
11334         }
11335         else if (player->post_delay_counter > 0)
11336         {
11337           player->post_delay_counter--;
11338         }
11339       }
11340     }
11341   }
11342   else if (last_waiting)        // waiting -> not waiting
11343   {
11344     player->is_waiting = FALSE;
11345     player->is_bored = FALSE;
11346     player->is_sleeping = FALSE;
11347
11348     player->frame_counter_bored = -1;
11349     player->frame_counter_sleeping = -1;
11350
11351     player->anim_delay_counter = 0;
11352     player->post_delay_counter = 0;
11353
11354     player->dir_waiting = player->MovDir;
11355     player->action_waiting = ACTION_DEFAULT;
11356
11357     player->special_action_bored = ACTION_DEFAULT;
11358     player->special_action_sleeping = ACTION_DEFAULT;
11359   }
11360 }
11361
11362 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11363 {
11364   if ((!player->is_moving  && player->was_moving) ||
11365       (player->MovPos == 0 && player->was_moving) ||
11366       (player->is_snapping && !player->was_snapping) ||
11367       (player->is_dropping && !player->was_dropping))
11368   {
11369     if (!CheckSaveEngineSnapshotToList())
11370       return;
11371
11372     player->was_moving = FALSE;
11373     player->was_snapping = TRUE;
11374     player->was_dropping = TRUE;
11375   }
11376   else
11377   {
11378     if (player->is_moving)
11379       player->was_moving = TRUE;
11380
11381     if (!player->is_snapping)
11382       player->was_snapping = FALSE;
11383
11384     if (!player->is_dropping)
11385       player->was_dropping = FALSE;
11386   }
11387
11388   static struct MouseActionInfo mouse_action_last = { 0 };
11389   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11390   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11391
11392   if (new_released)
11393     CheckSaveEngineSnapshotToList();
11394
11395   mouse_action_last = mouse_action;
11396 }
11397
11398 static void CheckSingleStepMode(struct PlayerInfo *player)
11399 {
11400   if (tape.single_step && tape.recording && !tape.pausing)
11401   {
11402     // as it is called "single step mode", just return to pause mode when the
11403     // player stopped moving after one tile (or never starts moving at all)
11404     // (reverse logic needed here in case single step mode used in team mode)
11405     if (player->is_moving ||
11406         player->is_pushing ||
11407         player->is_dropping_pressed ||
11408         player->effective_mouse_action.button)
11409       game.enter_single_step_mode = FALSE;
11410   }
11411
11412   CheckSaveEngineSnapshot(player);
11413 }
11414
11415 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11416 {
11417   int left      = player_action & JOY_LEFT;
11418   int right     = player_action & JOY_RIGHT;
11419   int up        = player_action & JOY_UP;
11420   int down      = player_action & JOY_DOWN;
11421   int button1   = player_action & JOY_BUTTON_1;
11422   int button2   = player_action & JOY_BUTTON_2;
11423   int dx        = (left ? -1 : right ? 1 : 0);
11424   int dy        = (up   ? -1 : down  ? 1 : 0);
11425
11426   if (!player->active || tape.pausing)
11427     return 0;
11428
11429   if (player_action)
11430   {
11431     if (button1)
11432       SnapField(player, dx, dy);
11433     else
11434     {
11435       if (button2)
11436         DropElement(player);
11437
11438       MovePlayer(player, dx, dy);
11439     }
11440
11441     CheckSingleStepMode(player);
11442
11443     SetPlayerWaiting(player, FALSE);
11444
11445     return player_action;
11446   }
11447   else
11448   {
11449     // no actions for this player (no input at player's configured device)
11450
11451     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11452     SnapField(player, 0, 0);
11453     CheckGravityMovementWhenNotMoving(player);
11454
11455     if (player->MovPos == 0)
11456       SetPlayerWaiting(player, TRUE);
11457
11458     if (player->MovPos == 0)    // needed for tape.playing
11459       player->is_moving = FALSE;
11460
11461     player->is_dropping = FALSE;
11462     player->is_dropping_pressed = FALSE;
11463     player->drop_pressed_delay = 0;
11464
11465     CheckSingleStepMode(player);
11466
11467     return 0;
11468   }
11469 }
11470
11471 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11472                                          byte *tape_action)
11473 {
11474   if (!tape.use_mouse_actions)
11475     return;
11476
11477   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11478   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11479   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11480 }
11481
11482 static void SetTapeActionFromMouseAction(byte *tape_action,
11483                                          struct MouseActionInfo *mouse_action)
11484 {
11485   if (!tape.use_mouse_actions)
11486     return;
11487
11488   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11489   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11490   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11491 }
11492
11493 static void CheckLevelSolved(void)
11494 {
11495   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11496   {
11497     if (game_em.level_solved &&
11498         !game_em.game_over)                             // game won
11499     {
11500       LevelSolved();
11501
11502       game_em.game_over = TRUE;
11503
11504       game.all_players_gone = TRUE;
11505     }
11506
11507     if (game_em.game_over)                              // game lost
11508       game.all_players_gone = TRUE;
11509   }
11510   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11511   {
11512     if (game_sp.level_solved &&
11513         !game_sp.game_over)                             // game won
11514     {
11515       LevelSolved();
11516
11517       game_sp.game_over = TRUE;
11518
11519       game.all_players_gone = TRUE;
11520     }
11521
11522     if (game_sp.game_over)                              // game lost
11523       game.all_players_gone = TRUE;
11524   }
11525   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11526   {
11527     if (game_mm.level_solved &&
11528         !game_mm.game_over)                             // game won
11529     {
11530       LevelSolved();
11531
11532       game_mm.game_over = TRUE;
11533
11534       game.all_players_gone = TRUE;
11535     }
11536
11537     if (game_mm.game_over)                              // game lost
11538       game.all_players_gone = TRUE;
11539   }
11540 }
11541
11542 static void CheckLevelTime(void)
11543 {
11544   int i;
11545
11546   if (TimeFrames >= FRAMES_PER_SECOND)
11547   {
11548     TimeFrames = 0;
11549     TapeTime++;
11550
11551     for (i = 0; i < MAX_PLAYERS; i++)
11552     {
11553       struct PlayerInfo *player = &stored_player[i];
11554
11555       if (SHIELD_ON(player))
11556       {
11557         player->shield_normal_time_left--;
11558
11559         if (player->shield_deadly_time_left > 0)
11560           player->shield_deadly_time_left--;
11561       }
11562     }
11563
11564     if (!game.LevelSolved && !level.use_step_counter)
11565     {
11566       TimePlayed++;
11567
11568       if (TimeLeft > 0)
11569       {
11570         TimeLeft--;
11571
11572         if (TimeLeft <= 10 && setup.time_limit)
11573           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11574
11575         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11576            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11577
11578         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11579
11580         if (!TimeLeft && setup.time_limit)
11581         {
11582           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11583             game_em.lev->killed_out_of_time = TRUE;
11584           else
11585             for (i = 0; i < MAX_PLAYERS; i++)
11586               KillPlayer(&stored_player[i]);
11587         }
11588       }
11589       else if (game.no_time_limit && !game.all_players_gone)
11590       {
11591         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11592       }
11593
11594       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11595     }
11596
11597     if (tape.recording || tape.playing)
11598       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11599   }
11600
11601   if (tape.recording || tape.playing)
11602     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11603
11604   UpdateAndDisplayGameControlValues();
11605 }
11606
11607 void AdvanceFrameAndPlayerCounters(int player_nr)
11608 {
11609   int i;
11610
11611   // advance frame counters (global frame counter and time frame counter)
11612   FrameCounter++;
11613   TimeFrames++;
11614
11615   // advance player counters (counters for move delay, move animation etc.)
11616   for (i = 0; i < MAX_PLAYERS; i++)
11617   {
11618     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11619     int move_delay_value = stored_player[i].move_delay_value;
11620     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11621
11622     if (!advance_player_counters)       // not all players may be affected
11623       continue;
11624
11625     if (move_frames == 0)       // less than one move per game frame
11626     {
11627       int stepsize = TILEX / move_delay_value;
11628       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11629       int count = (stored_player[i].is_moving ?
11630                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11631
11632       if (count % delay == 0)
11633         move_frames = 1;
11634     }
11635
11636     stored_player[i].Frame += move_frames;
11637
11638     if (stored_player[i].MovPos != 0)
11639       stored_player[i].StepFrame += move_frames;
11640
11641     if (stored_player[i].move_delay > 0)
11642       stored_player[i].move_delay--;
11643
11644     // due to bugs in previous versions, counter must count up, not down
11645     if (stored_player[i].push_delay != -1)
11646       stored_player[i].push_delay++;
11647
11648     if (stored_player[i].drop_delay > 0)
11649       stored_player[i].drop_delay--;
11650
11651     if (stored_player[i].is_dropping_pressed)
11652       stored_player[i].drop_pressed_delay++;
11653   }
11654 }
11655
11656 void StartGameActions(boolean init_network_game, boolean record_tape,
11657                       int random_seed)
11658 {
11659   unsigned int new_random_seed = InitRND(random_seed);
11660
11661   if (record_tape)
11662     TapeStartRecording(new_random_seed);
11663
11664   if (init_network_game)
11665   {
11666     SendToServer_LevelFile();
11667     SendToServer_StartPlaying();
11668
11669     return;
11670   }
11671
11672   InitGame();
11673 }
11674
11675 static void GameActionsExt(void)
11676 {
11677 #if 0
11678   static unsigned int game_frame_delay = 0;
11679 #endif
11680   unsigned int game_frame_delay_value;
11681   byte *recorded_player_action;
11682   byte summarized_player_action = 0;
11683   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11684   int i;
11685
11686   // detect endless loops, caused by custom element programming
11687   if (recursion_loop_detected && recursion_loop_depth == 0)
11688   {
11689     char *message = getStringCat3("Internal Error! Element ",
11690                                   EL_NAME(recursion_loop_element),
11691                                   " caused endless loop! Quit the game?");
11692
11693     Warn("element '%s' caused endless loop in game engine",
11694          EL_NAME(recursion_loop_element));
11695
11696     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11697
11698     recursion_loop_detected = FALSE;    // if game should be continued
11699
11700     free(message);
11701
11702     return;
11703   }
11704
11705   if (game.restart_level)
11706     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11707
11708   CheckLevelSolved();
11709
11710   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11711     GameWon();
11712
11713   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11714     TapeStop();
11715
11716   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11717     return;
11718
11719   game_frame_delay_value =
11720     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11721
11722   if (tape.playing && tape.warp_forward && !tape.pausing)
11723     game_frame_delay_value = 0;
11724
11725   SetVideoFrameDelay(game_frame_delay_value);
11726
11727   // (de)activate virtual buttons depending on current game status
11728   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11729   {
11730     if (game.all_players_gone)  // if no players there to be controlled anymore
11731       SetOverlayActive(FALSE);
11732     else if (!tape.playing)     // if game continues after tape stopped playing
11733       SetOverlayActive(TRUE);
11734   }
11735
11736 #if 0
11737 #if 0
11738   // ---------- main game synchronization point ----------
11739
11740   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11741
11742   Debug("game:playing:skip", "skip == %d", skip);
11743
11744 #else
11745   // ---------- main game synchronization point ----------
11746
11747   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11748 #endif
11749 #endif
11750
11751   if (network_playing && !network_player_action_received)
11752   {
11753     // try to get network player actions in time
11754
11755     // last chance to get network player actions without main loop delay
11756     HandleNetworking();
11757
11758     // game was quit by network peer
11759     if (game_status != GAME_MODE_PLAYING)
11760       return;
11761
11762     // check if network player actions still missing and game still running
11763     if (!network_player_action_received && !checkGameEnded())
11764       return;           // failed to get network player actions in time
11765
11766     // do not yet reset "network_player_action_received" (for tape.pausing)
11767   }
11768
11769   if (tape.pausing)
11770     return;
11771
11772   // at this point we know that we really continue executing the game
11773
11774   network_player_action_received = FALSE;
11775
11776   // when playing tape, read previously recorded player input from tape data
11777   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11778
11779   local_player->effective_mouse_action = local_player->mouse_action;
11780
11781   if (recorded_player_action != NULL)
11782     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11783                                  recorded_player_action);
11784
11785   // TapePlayAction() may return NULL when toggling to "pause before death"
11786   if (tape.pausing)
11787     return;
11788
11789   if (tape.set_centered_player)
11790   {
11791     game.centered_player_nr_next = tape.centered_player_nr_next;
11792     game.set_centered_player = TRUE;
11793   }
11794
11795   for (i = 0; i < MAX_PLAYERS; i++)
11796   {
11797     summarized_player_action |= stored_player[i].action;
11798
11799     if (!network_playing && (game.team_mode || tape.playing))
11800       stored_player[i].effective_action = stored_player[i].action;
11801   }
11802
11803   if (network_playing && !checkGameEnded())
11804     SendToServer_MovePlayer(summarized_player_action);
11805
11806   // summarize all actions at local players mapped input device position
11807   // (this allows using different input devices in single player mode)
11808   if (!network.enabled && !game.team_mode)
11809     stored_player[map_player_action[local_player->index_nr]].effective_action =
11810       summarized_player_action;
11811
11812   // summarize all actions at centered player in local team mode
11813   if (tape.recording &&
11814       setup.team_mode && !network.enabled &&
11815       setup.input_on_focus &&
11816       game.centered_player_nr != -1)
11817   {
11818     for (i = 0; i < MAX_PLAYERS; i++)
11819       stored_player[map_player_action[i]].effective_action =
11820         (i == game.centered_player_nr ? summarized_player_action : 0);
11821   }
11822
11823   if (recorded_player_action != NULL)
11824     for (i = 0; i < MAX_PLAYERS; i++)
11825       stored_player[i].effective_action = recorded_player_action[i];
11826
11827   for (i = 0; i < MAX_PLAYERS; i++)
11828   {
11829     tape_action[i] = stored_player[i].effective_action;
11830
11831     /* (this may happen in the RND game engine if a player was not present on
11832        the playfield on level start, but appeared later from a custom element */
11833     if (setup.team_mode &&
11834         tape.recording &&
11835         tape_action[i] &&
11836         !tape.player_participates[i])
11837       tape.player_participates[i] = TRUE;
11838   }
11839
11840   SetTapeActionFromMouseAction(tape_action,
11841                                &local_player->effective_mouse_action);
11842
11843   // only record actions from input devices, but not programmed actions
11844   if (tape.recording)
11845     TapeRecordAction(tape_action);
11846
11847   // remember if game was played (especially after tape stopped playing)
11848   if (!tape.playing && summarized_player_action)
11849     game.GamePlayed = TRUE;
11850
11851 #if USE_NEW_PLAYER_ASSIGNMENTS
11852   // !!! also map player actions in single player mode !!!
11853   // if (game.team_mode)
11854   if (1)
11855   {
11856     byte mapped_action[MAX_PLAYERS];
11857
11858 #if DEBUG_PLAYER_ACTIONS
11859     for (i = 0; i < MAX_PLAYERS; i++)
11860       DebugContinued("", "%d, ", stored_player[i].effective_action);
11861 #endif
11862
11863     for (i = 0; i < MAX_PLAYERS; i++)
11864       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11865
11866     for (i = 0; i < MAX_PLAYERS; i++)
11867       stored_player[i].effective_action = mapped_action[i];
11868
11869 #if DEBUG_PLAYER_ACTIONS
11870     DebugContinued("", "=> ");
11871     for (i = 0; i < MAX_PLAYERS; i++)
11872       DebugContinued("", "%d, ", stored_player[i].effective_action);
11873     DebugContinued("game:playing:player", "\n");
11874 #endif
11875   }
11876 #if DEBUG_PLAYER_ACTIONS
11877   else
11878   {
11879     for (i = 0; i < MAX_PLAYERS; i++)
11880       DebugContinued("", "%d, ", stored_player[i].effective_action);
11881     DebugContinued("game:playing:player", "\n");
11882   }
11883 #endif
11884 #endif
11885
11886   for (i = 0; i < MAX_PLAYERS; i++)
11887   {
11888     // allow engine snapshot in case of changed movement attempt
11889     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11890         (stored_player[i].effective_action & KEY_MOTION))
11891       game.snapshot.changed_action = TRUE;
11892
11893     // allow engine snapshot in case of snapping/dropping attempt
11894     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11895         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11896       game.snapshot.changed_action = TRUE;
11897
11898     game.snapshot.last_action[i] = stored_player[i].effective_action;
11899   }
11900
11901   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11902   {
11903     GameActions_EM_Main();
11904   }
11905   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11906   {
11907     GameActions_SP_Main();
11908   }
11909   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11910   {
11911     GameActions_MM_Main();
11912   }
11913   else
11914   {
11915     GameActions_RND_Main();
11916   }
11917
11918   BlitScreenToBitmap(backbuffer);
11919
11920   CheckLevelSolved();
11921   CheckLevelTime();
11922
11923   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11924
11925   if (global.show_frames_per_second)
11926   {
11927     static unsigned int fps_counter = 0;
11928     static int fps_frames = 0;
11929     unsigned int fps_delay_ms = Counter() - fps_counter;
11930
11931     fps_frames++;
11932
11933     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11934     {
11935       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11936
11937       fps_frames = 0;
11938       fps_counter = Counter();
11939
11940       // always draw FPS to screen after FPS value was updated
11941       redraw_mask |= REDRAW_FPS;
11942     }
11943
11944     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11945     if (GetDrawDeactivationMask() == REDRAW_NONE)
11946       redraw_mask |= REDRAW_FPS;
11947   }
11948 }
11949
11950 static void GameActions_CheckSaveEngineSnapshot(void)
11951 {
11952   if (!game.snapshot.save_snapshot)
11953     return;
11954
11955   // clear flag for saving snapshot _before_ saving snapshot
11956   game.snapshot.save_snapshot = FALSE;
11957
11958   SaveEngineSnapshotToList();
11959 }
11960
11961 void GameActions(void)
11962 {
11963   GameActionsExt();
11964
11965   GameActions_CheckSaveEngineSnapshot();
11966 }
11967
11968 void GameActions_EM_Main(void)
11969 {
11970   byte effective_action[MAX_PLAYERS];
11971   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11972   int i;
11973
11974   for (i = 0; i < MAX_PLAYERS; i++)
11975     effective_action[i] = stored_player[i].effective_action;
11976
11977   GameActions_EM(effective_action, warp_mode);
11978 }
11979
11980 void GameActions_SP_Main(void)
11981 {
11982   byte effective_action[MAX_PLAYERS];
11983   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11984   int i;
11985
11986   for (i = 0; i < MAX_PLAYERS; i++)
11987     effective_action[i] = stored_player[i].effective_action;
11988
11989   GameActions_SP(effective_action, warp_mode);
11990
11991   for (i = 0; i < MAX_PLAYERS; i++)
11992   {
11993     if (stored_player[i].force_dropping)
11994       stored_player[i].action |= KEY_BUTTON_DROP;
11995
11996     stored_player[i].force_dropping = FALSE;
11997   }
11998 }
11999
12000 void GameActions_MM_Main(void)
12001 {
12002   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12003
12004   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12005 }
12006
12007 void GameActions_RND_Main(void)
12008 {
12009   GameActions_RND();
12010 }
12011
12012 void GameActions_RND(void)
12013 {
12014   static struct MouseActionInfo mouse_action_last = { 0 };
12015   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12016   int magic_wall_x = 0, magic_wall_y = 0;
12017   int i, x, y, element, graphic, last_gfx_frame;
12018
12019   InitPlayfieldScanModeVars();
12020
12021   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12022   {
12023     SCAN_PLAYFIELD(x, y)
12024     {
12025       ChangeCount[x][y] = 0;
12026       ChangeEvent[x][y] = -1;
12027     }
12028   }
12029
12030   if (game.set_centered_player)
12031   {
12032     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12033
12034     // switching to "all players" only possible if all players fit to screen
12035     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12036     {
12037       game.centered_player_nr_next = game.centered_player_nr;
12038       game.set_centered_player = FALSE;
12039     }
12040
12041     // do not switch focus to non-existing (or non-active) player
12042     if (game.centered_player_nr_next >= 0 &&
12043         !stored_player[game.centered_player_nr_next].active)
12044     {
12045       game.centered_player_nr_next = game.centered_player_nr;
12046       game.set_centered_player = FALSE;
12047     }
12048   }
12049
12050   if (game.set_centered_player &&
12051       ScreenMovPos == 0)        // screen currently aligned at tile position
12052   {
12053     int sx, sy;
12054
12055     if (game.centered_player_nr_next == -1)
12056     {
12057       setScreenCenteredToAllPlayers(&sx, &sy);
12058     }
12059     else
12060     {
12061       sx = stored_player[game.centered_player_nr_next].jx;
12062       sy = stored_player[game.centered_player_nr_next].jy;
12063     }
12064
12065     game.centered_player_nr = game.centered_player_nr_next;
12066     game.set_centered_player = FALSE;
12067
12068     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12069     DrawGameDoorValues();
12070   }
12071
12072   // check single step mode (set flag and clear again if any player is active)
12073   game.enter_single_step_mode =
12074     (tape.single_step && tape.recording && !tape.pausing);
12075
12076   for (i = 0; i < MAX_PLAYERS; i++)
12077   {
12078     int actual_player_action = stored_player[i].effective_action;
12079
12080 #if 1
12081     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12082        - rnd_equinox_tetrachloride 048
12083        - rnd_equinox_tetrachloride_ii 096
12084        - rnd_emanuel_schmieg 002
12085        - doctor_sloan_ww 001, 020
12086     */
12087     if (stored_player[i].MovPos == 0)
12088       CheckGravityMovement(&stored_player[i]);
12089 #endif
12090
12091     // overwrite programmed action with tape action
12092     if (stored_player[i].programmed_action)
12093       actual_player_action = stored_player[i].programmed_action;
12094
12095     PlayerActions(&stored_player[i], actual_player_action);
12096
12097     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12098   }
12099
12100   // single step pause mode may already have been toggled by "ScrollPlayer()"
12101   if (game.enter_single_step_mode && !tape.pausing)
12102     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12103
12104   ScrollScreen(NULL, SCROLL_GO_ON);
12105
12106   /* for backwards compatibility, the following code emulates a fixed bug that
12107      occured when pushing elements (causing elements that just made their last
12108      pushing step to already (if possible) make their first falling step in the
12109      same game frame, which is bad); this code is also needed to use the famous
12110      "spring push bug" which is used in older levels and might be wanted to be
12111      used also in newer levels, but in this case the buggy pushing code is only
12112      affecting the "spring" element and no other elements */
12113
12114   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12115   {
12116     for (i = 0; i < MAX_PLAYERS; i++)
12117     {
12118       struct PlayerInfo *player = &stored_player[i];
12119       int x = player->jx;
12120       int y = player->jy;
12121
12122       if (player->active && player->is_pushing && player->is_moving &&
12123           IS_MOVING(x, y) &&
12124           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12125            Tile[x][y] == EL_SPRING))
12126       {
12127         ContinueMoving(x, y);
12128
12129         // continue moving after pushing (this is actually a bug)
12130         if (!IS_MOVING(x, y))
12131           Stop[x][y] = FALSE;
12132       }
12133     }
12134   }
12135
12136   SCAN_PLAYFIELD(x, y)
12137   {
12138     Last[x][y] = Tile[x][y];
12139
12140     ChangeCount[x][y] = 0;
12141     ChangeEvent[x][y] = -1;
12142
12143     // this must be handled before main playfield loop
12144     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12145     {
12146       MovDelay[x][y]--;
12147       if (MovDelay[x][y] <= 0)
12148         RemoveField(x, y);
12149     }
12150
12151     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12152     {
12153       MovDelay[x][y]--;
12154       if (MovDelay[x][y] <= 0)
12155       {
12156         int element = Store[x][y];
12157         int move_direction = MovDir[x][y];
12158         int player_index_bit = Store2[x][y];
12159
12160         Store[x][y] = 0;
12161         Store2[x][y] = 0;
12162
12163         RemoveField(x, y);
12164         TEST_DrawLevelField(x, y);
12165
12166         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12167       }
12168     }
12169
12170 #if DEBUG
12171     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12172     {
12173       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12174             x, y);
12175       Debug("game:playing:GameActions_RND", "This should never happen!");
12176
12177       ChangePage[x][y] = -1;
12178     }
12179 #endif
12180
12181     Stop[x][y] = FALSE;
12182     if (WasJustMoving[x][y] > 0)
12183       WasJustMoving[x][y]--;
12184     if (WasJustFalling[x][y] > 0)
12185       WasJustFalling[x][y]--;
12186     if (CheckCollision[x][y] > 0)
12187       CheckCollision[x][y]--;
12188     if (CheckImpact[x][y] > 0)
12189       CheckImpact[x][y]--;
12190
12191     GfxFrame[x][y]++;
12192
12193     /* reset finished pushing action (not done in ContinueMoving() to allow
12194        continuous pushing animation for elements with zero push delay) */
12195     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12196     {
12197       ResetGfxAnimation(x, y);
12198       TEST_DrawLevelField(x, y);
12199     }
12200
12201 #if DEBUG
12202     if (IS_BLOCKED(x, y))
12203     {
12204       int oldx, oldy;
12205
12206       Blocked2Moving(x, y, &oldx, &oldy);
12207       if (!IS_MOVING(oldx, oldy))
12208       {
12209         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12210         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12211         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12212         Debug("game:playing:GameActions_RND", "This should never happen!");
12213       }
12214     }
12215 #endif
12216   }
12217
12218   if (mouse_action.button)
12219   {
12220     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12221     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12222
12223     x = mouse_action.lx;
12224     y = mouse_action.ly;
12225     element = Tile[x][y];
12226
12227     if (new_button)
12228     {
12229       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12230       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12231                                          ch_button);
12232     }
12233
12234     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12235     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12236                                        ch_button);
12237   }
12238
12239   SCAN_PLAYFIELD(x, y)
12240   {
12241     element = Tile[x][y];
12242     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12243     last_gfx_frame = GfxFrame[x][y];
12244
12245     ResetGfxFrame(x, y);
12246
12247     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12248       DrawLevelGraphicAnimation(x, y, graphic);
12249
12250     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12251         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12252       ResetRandomAnimationValue(x, y);
12253
12254     SetRandomAnimationValue(x, y);
12255
12256     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12257
12258     if (IS_INACTIVE(element))
12259     {
12260       if (IS_ANIMATED(graphic))
12261         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12262
12263       continue;
12264     }
12265
12266     // this may take place after moving, so 'element' may have changed
12267     if (IS_CHANGING(x, y) &&
12268         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12269     {
12270       int page = element_info[element].event_page_nr[CE_DELAY];
12271
12272       HandleElementChange(x, y, page);
12273
12274       element = Tile[x][y];
12275       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12276     }
12277
12278     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12279     {
12280       StartMoving(x, y);
12281
12282       element = Tile[x][y];
12283       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12284
12285       if (IS_ANIMATED(graphic) &&
12286           !IS_MOVING(x, y) &&
12287           !Stop[x][y])
12288         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12289
12290       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12291         TEST_DrawTwinkleOnField(x, y);
12292     }
12293     else if (element == EL_ACID)
12294     {
12295       if (!Stop[x][y])
12296         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12297     }
12298     else if ((element == EL_EXIT_OPEN ||
12299               element == EL_EM_EXIT_OPEN ||
12300               element == EL_SP_EXIT_OPEN ||
12301               element == EL_STEEL_EXIT_OPEN ||
12302               element == EL_EM_STEEL_EXIT_OPEN ||
12303               element == EL_SP_TERMINAL ||
12304               element == EL_SP_TERMINAL_ACTIVE ||
12305               element == EL_EXTRA_TIME ||
12306               element == EL_SHIELD_NORMAL ||
12307               element == EL_SHIELD_DEADLY) &&
12308              IS_ANIMATED(graphic))
12309       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12310     else if (IS_MOVING(x, y))
12311       ContinueMoving(x, y);
12312     else if (IS_ACTIVE_BOMB(element))
12313       CheckDynamite(x, y);
12314     else if (element == EL_AMOEBA_GROWING)
12315       AmoebaGrowing(x, y);
12316     else if (element == EL_AMOEBA_SHRINKING)
12317       AmoebaShrinking(x, y);
12318
12319 #if !USE_NEW_AMOEBA_CODE
12320     else if (IS_AMOEBALIVE(element))
12321       AmoebaReproduce(x, y);
12322 #endif
12323
12324     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12325       Life(x, y);
12326     else if (element == EL_EXIT_CLOSED)
12327       CheckExit(x, y);
12328     else if (element == EL_EM_EXIT_CLOSED)
12329       CheckExitEM(x, y);
12330     else if (element == EL_STEEL_EXIT_CLOSED)
12331       CheckExitSteel(x, y);
12332     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12333       CheckExitSteelEM(x, y);
12334     else if (element == EL_SP_EXIT_CLOSED)
12335       CheckExitSP(x, y);
12336     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12337              element == EL_EXPANDABLE_STEELWALL_GROWING)
12338       MauerWaechst(x, y);
12339     else if (element == EL_EXPANDABLE_WALL ||
12340              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12341              element == EL_EXPANDABLE_WALL_VERTICAL ||
12342              element == EL_EXPANDABLE_WALL_ANY ||
12343              element == EL_BD_EXPANDABLE_WALL)
12344       MauerAbleger(x, y);
12345     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12346              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12347              element == EL_EXPANDABLE_STEELWALL_ANY)
12348       MauerAblegerStahl(x, y);
12349     else if (element == EL_FLAMES)
12350       CheckForDragon(x, y);
12351     else if (element == EL_EXPLOSION)
12352       ; // drawing of correct explosion animation is handled separately
12353     else if (element == EL_ELEMENT_SNAPPING ||
12354              element == EL_DIAGONAL_SHRINKING ||
12355              element == EL_DIAGONAL_GROWING)
12356     {
12357       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12358
12359       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12360     }
12361     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12362       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12363
12364     if (IS_BELT_ACTIVE(element))
12365       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12366
12367     if (game.magic_wall_active)
12368     {
12369       int jx = local_player->jx, jy = local_player->jy;
12370
12371       // play the element sound at the position nearest to the player
12372       if ((element == EL_MAGIC_WALL_FULL ||
12373            element == EL_MAGIC_WALL_ACTIVE ||
12374            element == EL_MAGIC_WALL_EMPTYING ||
12375            element == EL_BD_MAGIC_WALL_FULL ||
12376            element == EL_BD_MAGIC_WALL_ACTIVE ||
12377            element == EL_BD_MAGIC_WALL_EMPTYING ||
12378            element == EL_DC_MAGIC_WALL_FULL ||
12379            element == EL_DC_MAGIC_WALL_ACTIVE ||
12380            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12381           ABS(x - jx) + ABS(y - jy) <
12382           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12383       {
12384         magic_wall_x = x;
12385         magic_wall_y = y;
12386       }
12387     }
12388   }
12389
12390 #if USE_NEW_AMOEBA_CODE
12391   // new experimental amoeba growth stuff
12392   if (!(FrameCounter % 8))
12393   {
12394     static unsigned int random = 1684108901;
12395
12396     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12397     {
12398       x = RND(lev_fieldx);
12399       y = RND(lev_fieldy);
12400       element = Tile[x][y];
12401
12402       if (!IS_PLAYER(x,y) &&
12403           (element == EL_EMPTY ||
12404            CAN_GROW_INTO(element) ||
12405            element == EL_QUICKSAND_EMPTY ||
12406            element == EL_QUICKSAND_FAST_EMPTY ||
12407            element == EL_ACID_SPLASH_LEFT ||
12408            element == EL_ACID_SPLASH_RIGHT))
12409       {
12410         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12411             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12412             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12413             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12414           Tile[x][y] = EL_AMOEBA_DROP;
12415       }
12416
12417       random = random * 129 + 1;
12418     }
12419   }
12420 #endif
12421
12422   game.explosions_delayed = FALSE;
12423
12424   SCAN_PLAYFIELD(x, y)
12425   {
12426     element = Tile[x][y];
12427
12428     if (ExplodeField[x][y])
12429       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12430     else if (element == EL_EXPLOSION)
12431       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12432
12433     ExplodeField[x][y] = EX_TYPE_NONE;
12434   }
12435
12436   game.explosions_delayed = TRUE;
12437
12438   if (game.magic_wall_active)
12439   {
12440     if (!(game.magic_wall_time_left % 4))
12441     {
12442       int element = Tile[magic_wall_x][magic_wall_y];
12443
12444       if (element == EL_BD_MAGIC_WALL_FULL ||
12445           element == EL_BD_MAGIC_WALL_ACTIVE ||
12446           element == EL_BD_MAGIC_WALL_EMPTYING)
12447         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12448       else if (element == EL_DC_MAGIC_WALL_FULL ||
12449                element == EL_DC_MAGIC_WALL_ACTIVE ||
12450                element == EL_DC_MAGIC_WALL_EMPTYING)
12451         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12452       else
12453         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12454     }
12455
12456     if (game.magic_wall_time_left > 0)
12457     {
12458       game.magic_wall_time_left--;
12459
12460       if (!game.magic_wall_time_left)
12461       {
12462         SCAN_PLAYFIELD(x, y)
12463         {
12464           element = Tile[x][y];
12465
12466           if (element == EL_MAGIC_WALL_ACTIVE ||
12467               element == EL_MAGIC_WALL_FULL)
12468           {
12469             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12470             TEST_DrawLevelField(x, y);
12471           }
12472           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12473                    element == EL_BD_MAGIC_WALL_FULL)
12474           {
12475             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12476             TEST_DrawLevelField(x, y);
12477           }
12478           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12479                    element == EL_DC_MAGIC_WALL_FULL)
12480           {
12481             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12482             TEST_DrawLevelField(x, y);
12483           }
12484         }
12485
12486         game.magic_wall_active = FALSE;
12487       }
12488     }
12489   }
12490
12491   if (game.light_time_left > 0)
12492   {
12493     game.light_time_left--;
12494
12495     if (game.light_time_left == 0)
12496       RedrawAllLightSwitchesAndInvisibleElements();
12497   }
12498
12499   if (game.timegate_time_left > 0)
12500   {
12501     game.timegate_time_left--;
12502
12503     if (game.timegate_time_left == 0)
12504       CloseAllOpenTimegates();
12505   }
12506
12507   if (game.lenses_time_left > 0)
12508   {
12509     game.lenses_time_left--;
12510
12511     if (game.lenses_time_left == 0)
12512       RedrawAllInvisibleElementsForLenses();
12513   }
12514
12515   if (game.magnify_time_left > 0)
12516   {
12517     game.magnify_time_left--;
12518
12519     if (game.magnify_time_left == 0)
12520       RedrawAllInvisibleElementsForMagnifier();
12521   }
12522
12523   for (i = 0; i < MAX_PLAYERS; i++)
12524   {
12525     struct PlayerInfo *player = &stored_player[i];
12526
12527     if (SHIELD_ON(player))
12528     {
12529       if (player->shield_deadly_time_left)
12530         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12531       else if (player->shield_normal_time_left)
12532         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12533     }
12534   }
12535
12536 #if USE_DELAYED_GFX_REDRAW
12537   SCAN_PLAYFIELD(x, y)
12538   {
12539     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12540     {
12541       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12542          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12543
12544       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12545         DrawLevelField(x, y);
12546
12547       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12548         DrawLevelFieldCrumbled(x, y);
12549
12550       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12551         DrawLevelFieldCrumbledNeighbours(x, y);
12552
12553       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12554         DrawTwinkleOnField(x, y);
12555     }
12556
12557     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12558   }
12559 #endif
12560
12561   DrawAllPlayers();
12562   PlayAllPlayersSound();
12563
12564   for (i = 0; i < MAX_PLAYERS; i++)
12565   {
12566     struct PlayerInfo *player = &stored_player[i];
12567
12568     if (player->show_envelope != 0 && (!player->active ||
12569                                        player->MovPos == 0))
12570     {
12571       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12572
12573       player->show_envelope = 0;
12574     }
12575   }
12576
12577   // use random number generator in every frame to make it less predictable
12578   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12579     RND(1);
12580
12581   mouse_action_last = mouse_action;
12582 }
12583
12584 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12585 {
12586   int min_x = x, min_y = y, max_x = x, max_y = y;
12587   int scr_fieldx = getScreenFieldSizeX();
12588   int scr_fieldy = getScreenFieldSizeY();
12589   int i;
12590
12591   for (i = 0; i < MAX_PLAYERS; i++)
12592   {
12593     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12594
12595     if (!stored_player[i].active || &stored_player[i] == player)
12596       continue;
12597
12598     min_x = MIN(min_x, jx);
12599     min_y = MIN(min_y, jy);
12600     max_x = MAX(max_x, jx);
12601     max_y = MAX(max_y, jy);
12602   }
12603
12604   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12605 }
12606
12607 static boolean AllPlayersInVisibleScreen(void)
12608 {
12609   int i;
12610
12611   for (i = 0; i < MAX_PLAYERS; i++)
12612   {
12613     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12614
12615     if (!stored_player[i].active)
12616       continue;
12617
12618     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12619       return FALSE;
12620   }
12621
12622   return TRUE;
12623 }
12624
12625 void ScrollLevel(int dx, int dy)
12626 {
12627   int scroll_offset = 2 * TILEX_VAR;
12628   int x, y;
12629
12630   BlitBitmap(drawto_field, drawto_field,
12631              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12632              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12633              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12634              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12635              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12636              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12637
12638   if (dx != 0)
12639   {
12640     x = (dx == 1 ? BX1 : BX2);
12641     for (y = BY1; y <= BY2; y++)
12642       DrawScreenField(x, y);
12643   }
12644
12645   if (dy != 0)
12646   {
12647     y = (dy == 1 ? BY1 : BY2);
12648     for (x = BX1; x <= BX2; x++)
12649       DrawScreenField(x, y);
12650   }
12651
12652   redraw_mask |= REDRAW_FIELD;
12653 }
12654
12655 static boolean canFallDown(struct PlayerInfo *player)
12656 {
12657   int jx = player->jx, jy = player->jy;
12658
12659   return (IN_LEV_FIELD(jx, jy + 1) &&
12660           (IS_FREE(jx, jy + 1) ||
12661            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12662           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12663           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12664 }
12665
12666 static boolean canPassField(int x, int y, int move_dir)
12667 {
12668   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12669   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12670   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12671   int nextx = x + dx;
12672   int nexty = y + dy;
12673   int element = Tile[x][y];
12674
12675   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12676           !CAN_MOVE(element) &&
12677           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12678           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12679           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12680 }
12681
12682 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12683 {
12684   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12685   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12686   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12687   int newx = x + dx;
12688   int newy = y + dy;
12689
12690   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12691           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12692           (IS_DIGGABLE(Tile[newx][newy]) ||
12693            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12694            canPassField(newx, newy, move_dir)));
12695 }
12696
12697 static void CheckGravityMovement(struct PlayerInfo *player)
12698 {
12699   if (player->gravity && !player->programmed_action)
12700   {
12701     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12702     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12703     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12704     int jx = player->jx, jy = player->jy;
12705     boolean player_is_moving_to_valid_field =
12706       (!player_is_snapping &&
12707        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12708         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12709     boolean player_can_fall_down = canFallDown(player);
12710
12711     if (player_can_fall_down &&
12712         !player_is_moving_to_valid_field)
12713       player->programmed_action = MV_DOWN;
12714   }
12715 }
12716
12717 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12718 {
12719   return CheckGravityMovement(player);
12720
12721   if (player->gravity && !player->programmed_action)
12722   {
12723     int jx = player->jx, jy = player->jy;
12724     boolean field_under_player_is_free =
12725       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12726     boolean player_is_standing_on_valid_field =
12727       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12728        (IS_WALKABLE(Tile[jx][jy]) &&
12729         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12730
12731     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12732       player->programmed_action = MV_DOWN;
12733   }
12734 }
12735
12736 /*
12737   MovePlayerOneStep()
12738   -----------------------------------------------------------------------------
12739   dx, dy:               direction (non-diagonal) to try to move the player to
12740   real_dx, real_dy:     direction as read from input device (can be diagonal)
12741 */
12742
12743 boolean MovePlayerOneStep(struct PlayerInfo *player,
12744                           int dx, int dy, int real_dx, int real_dy)
12745 {
12746   int jx = player->jx, jy = player->jy;
12747   int new_jx = jx + dx, new_jy = jy + dy;
12748   int can_move;
12749   boolean player_can_move = !player->cannot_move;
12750
12751   if (!player->active || (!dx && !dy))
12752     return MP_NO_ACTION;
12753
12754   player->MovDir = (dx < 0 ? MV_LEFT :
12755                     dx > 0 ? MV_RIGHT :
12756                     dy < 0 ? MV_UP :
12757                     dy > 0 ? MV_DOWN :  MV_NONE);
12758
12759   if (!IN_LEV_FIELD(new_jx, new_jy))
12760     return MP_NO_ACTION;
12761
12762   if (!player_can_move)
12763   {
12764     if (player->MovPos == 0)
12765     {
12766       player->is_moving = FALSE;
12767       player->is_digging = FALSE;
12768       player->is_collecting = FALSE;
12769       player->is_snapping = FALSE;
12770       player->is_pushing = FALSE;
12771     }
12772   }
12773
12774   if (!network.enabled && game.centered_player_nr == -1 &&
12775       !AllPlayersInSight(player, new_jx, new_jy))
12776     return MP_NO_ACTION;
12777
12778   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12779   if (can_move != MP_MOVING)
12780     return can_move;
12781
12782   // check if DigField() has caused relocation of the player
12783   if (player->jx != jx || player->jy != jy)
12784     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12785
12786   StorePlayer[jx][jy] = 0;
12787   player->last_jx = jx;
12788   player->last_jy = jy;
12789   player->jx = new_jx;
12790   player->jy = new_jy;
12791   StorePlayer[new_jx][new_jy] = player->element_nr;
12792
12793   if (player->move_delay_value_next != -1)
12794   {
12795     player->move_delay_value = player->move_delay_value_next;
12796     player->move_delay_value_next = -1;
12797   }
12798
12799   player->MovPos =
12800     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12801
12802   player->step_counter++;
12803
12804   PlayerVisit[jx][jy] = FrameCounter;
12805
12806   player->is_moving = TRUE;
12807
12808 #if 1
12809   // should better be called in MovePlayer(), but this breaks some tapes
12810   ScrollPlayer(player, SCROLL_INIT);
12811 #endif
12812
12813   return MP_MOVING;
12814 }
12815
12816 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12817 {
12818   int jx = player->jx, jy = player->jy;
12819   int old_jx = jx, old_jy = jy;
12820   int moved = MP_NO_ACTION;
12821
12822   if (!player->active)
12823     return FALSE;
12824
12825   if (!dx && !dy)
12826   {
12827     if (player->MovPos == 0)
12828     {
12829       player->is_moving = FALSE;
12830       player->is_digging = FALSE;
12831       player->is_collecting = FALSE;
12832       player->is_snapping = FALSE;
12833       player->is_pushing = FALSE;
12834     }
12835
12836     return FALSE;
12837   }
12838
12839   if (player->move_delay > 0)
12840     return FALSE;
12841
12842   player->move_delay = -1;              // set to "uninitialized" value
12843
12844   // store if player is automatically moved to next field
12845   player->is_auto_moving = (player->programmed_action != MV_NONE);
12846
12847   // remove the last programmed player action
12848   player->programmed_action = 0;
12849
12850   if (player->MovPos)
12851   {
12852     // should only happen if pre-1.2 tape recordings are played
12853     // this is only for backward compatibility
12854
12855     int original_move_delay_value = player->move_delay_value;
12856
12857 #if DEBUG
12858     Debug("game:playing:MovePlayer",
12859           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12860           tape.counter);
12861 #endif
12862
12863     // scroll remaining steps with finest movement resolution
12864     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12865
12866     while (player->MovPos)
12867     {
12868       ScrollPlayer(player, SCROLL_GO_ON);
12869       ScrollScreen(NULL, SCROLL_GO_ON);
12870
12871       AdvanceFrameAndPlayerCounters(player->index_nr);
12872
12873       DrawAllPlayers();
12874       BackToFront_WithFrameDelay(0);
12875     }
12876
12877     player->move_delay_value = original_move_delay_value;
12878   }
12879
12880   player->is_active = FALSE;
12881
12882   if (player->last_move_dir & MV_HORIZONTAL)
12883   {
12884     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12885       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12886   }
12887   else
12888   {
12889     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12890       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12891   }
12892
12893   if (!moved && !player->is_active)
12894   {
12895     player->is_moving = FALSE;
12896     player->is_digging = FALSE;
12897     player->is_collecting = FALSE;
12898     player->is_snapping = FALSE;
12899     player->is_pushing = FALSE;
12900   }
12901
12902   jx = player->jx;
12903   jy = player->jy;
12904
12905   if (moved & MP_MOVING && !ScreenMovPos &&
12906       (player->index_nr == game.centered_player_nr ||
12907        game.centered_player_nr == -1))
12908   {
12909     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12910
12911     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12912     {
12913       // actual player has left the screen -- scroll in that direction
12914       if (jx != old_jx)         // player has moved horizontally
12915         scroll_x += (jx - old_jx);
12916       else                      // player has moved vertically
12917         scroll_y += (jy - old_jy);
12918     }
12919     else
12920     {
12921       int offset_raw = game.scroll_delay_value;
12922
12923       if (jx != old_jx)         // player has moved horizontally
12924       {
12925         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12926         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12927         int new_scroll_x = jx - MIDPOSX + offset_x;
12928
12929         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12930             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12931           scroll_x = new_scroll_x;
12932
12933         // don't scroll over playfield boundaries
12934         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12935
12936         // don't scroll more than one field at a time
12937         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12938
12939         // don't scroll against the player's moving direction
12940         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12941             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12942           scroll_x = old_scroll_x;
12943       }
12944       else                      // player has moved vertically
12945       {
12946         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12947         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12948         int new_scroll_y = jy - MIDPOSY + offset_y;
12949
12950         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12951             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12952           scroll_y = new_scroll_y;
12953
12954         // don't scroll over playfield boundaries
12955         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12956
12957         // don't scroll more than one field at a time
12958         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12959
12960         // don't scroll against the player's moving direction
12961         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12962             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12963           scroll_y = old_scroll_y;
12964       }
12965     }
12966
12967     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12968     {
12969       if (!network.enabled && game.centered_player_nr == -1 &&
12970           !AllPlayersInVisibleScreen())
12971       {
12972         scroll_x = old_scroll_x;
12973         scroll_y = old_scroll_y;
12974       }
12975       else
12976       {
12977         ScrollScreen(player, SCROLL_INIT);
12978         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12979       }
12980     }
12981   }
12982
12983   player->StepFrame = 0;
12984
12985   if (moved & MP_MOVING)
12986   {
12987     if (old_jx != jx && old_jy == jy)
12988       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12989     else if (old_jx == jx && old_jy != jy)
12990       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12991
12992     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12993
12994     player->last_move_dir = player->MovDir;
12995     player->is_moving = TRUE;
12996     player->is_snapping = FALSE;
12997     player->is_switching = FALSE;
12998     player->is_dropping = FALSE;
12999     player->is_dropping_pressed = FALSE;
13000     player->drop_pressed_delay = 0;
13001
13002 #if 0
13003     // should better be called here than above, but this breaks some tapes
13004     ScrollPlayer(player, SCROLL_INIT);
13005 #endif
13006   }
13007   else
13008   {
13009     CheckGravityMovementWhenNotMoving(player);
13010
13011     player->is_moving = FALSE;
13012
13013     /* at this point, the player is allowed to move, but cannot move right now
13014        (e.g. because of something blocking the way) -- ensure that the player
13015        is also allowed to move in the next frame (in old versions before 3.1.1,
13016        the player was forced to wait again for eight frames before next try) */
13017
13018     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13019       player->move_delay = 0;   // allow direct movement in the next frame
13020   }
13021
13022   if (player->move_delay == -1)         // not yet initialized by DigField()
13023     player->move_delay = player->move_delay_value;
13024
13025   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13026   {
13027     TestIfPlayerTouchesBadThing(jx, jy);
13028     TestIfPlayerTouchesCustomElement(jx, jy);
13029   }
13030
13031   if (!player->active)
13032     RemovePlayer(player);
13033
13034   return moved;
13035 }
13036
13037 void ScrollPlayer(struct PlayerInfo *player, int mode)
13038 {
13039   int jx = player->jx, jy = player->jy;
13040   int last_jx = player->last_jx, last_jy = player->last_jy;
13041   int move_stepsize = TILEX / player->move_delay_value;
13042
13043   if (!player->active)
13044     return;
13045
13046   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13047     return;
13048
13049   if (mode == SCROLL_INIT)
13050   {
13051     player->actual_frame_counter = FrameCounter;
13052     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13053
13054     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13055         Tile[last_jx][last_jy] == EL_EMPTY)
13056     {
13057       int last_field_block_delay = 0;   // start with no blocking at all
13058       int block_delay_adjustment = player->block_delay_adjustment;
13059
13060       // if player blocks last field, add delay for exactly one move
13061       if (player->block_last_field)
13062       {
13063         last_field_block_delay += player->move_delay_value;
13064
13065         // when blocking enabled, prevent moving up despite gravity
13066         if (player->gravity && player->MovDir == MV_UP)
13067           block_delay_adjustment = -1;
13068       }
13069
13070       // add block delay adjustment (also possible when not blocking)
13071       last_field_block_delay += block_delay_adjustment;
13072
13073       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13074       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13075     }
13076
13077     if (player->MovPos != 0)    // player has not yet reached destination
13078       return;
13079   }
13080   else if (!FrameReached(&player->actual_frame_counter, 1))
13081     return;
13082
13083   if (player->MovPos != 0)
13084   {
13085     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13086     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13087
13088     // before DrawPlayer() to draw correct player graphic for this case
13089     if (player->MovPos == 0)
13090       CheckGravityMovement(player);
13091   }
13092
13093   if (player->MovPos == 0)      // player reached destination field
13094   {
13095     if (player->move_delay_reset_counter > 0)
13096     {
13097       player->move_delay_reset_counter--;
13098
13099       if (player->move_delay_reset_counter == 0)
13100       {
13101         // continue with normal speed after quickly moving through gate
13102         HALVE_PLAYER_SPEED(player);
13103
13104         // be able to make the next move without delay
13105         player->move_delay = 0;
13106       }
13107     }
13108
13109     player->last_jx = jx;
13110     player->last_jy = jy;
13111
13112     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13113         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13114         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13115         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13116         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13117         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13118         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13119         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13120     {
13121       ExitPlayer(player);
13122
13123       if (game.players_still_needed == 0 &&
13124           (game.friends_still_needed == 0 ||
13125            IS_SP_ELEMENT(Tile[jx][jy])))
13126         LevelSolved();
13127     }
13128
13129     // this breaks one level: "machine", level 000
13130     {
13131       int move_direction = player->MovDir;
13132       int enter_side = MV_DIR_OPPOSITE(move_direction);
13133       int leave_side = move_direction;
13134       int old_jx = last_jx;
13135       int old_jy = last_jy;
13136       int old_element = Tile[old_jx][old_jy];
13137       int new_element = Tile[jx][jy];
13138
13139       if (IS_CUSTOM_ELEMENT(old_element))
13140         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13141                                    CE_LEFT_BY_PLAYER,
13142                                    player->index_bit, leave_side);
13143
13144       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13145                                           CE_PLAYER_LEAVES_X,
13146                                           player->index_bit, leave_side);
13147
13148       if (IS_CUSTOM_ELEMENT(new_element))
13149         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13150                                    player->index_bit, enter_side);
13151
13152       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13153                                           CE_PLAYER_ENTERS_X,
13154                                           player->index_bit, enter_side);
13155
13156       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13157                                         CE_MOVE_OF_X, move_direction);
13158     }
13159
13160     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13161     {
13162       TestIfPlayerTouchesBadThing(jx, jy);
13163       TestIfPlayerTouchesCustomElement(jx, jy);
13164
13165       /* needed because pushed element has not yet reached its destination,
13166          so it would trigger a change event at its previous field location */
13167       if (!player->is_pushing)
13168         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13169
13170       if (level.finish_dig_collect &&
13171           (player->is_digging || player->is_collecting))
13172       {
13173         int last_element = player->last_removed_element;
13174         int move_direction = player->MovDir;
13175         int enter_side = MV_DIR_OPPOSITE(move_direction);
13176         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13177                             CE_PLAYER_COLLECTS_X);
13178
13179         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13180                                             player->index_bit, enter_side);
13181
13182         player->last_removed_element = EL_UNDEFINED;
13183       }
13184
13185       if (!player->active)
13186         RemovePlayer(player);
13187     }
13188
13189     if (level.use_step_counter)
13190     {
13191       int i;
13192
13193       TimePlayed++;
13194
13195       if (TimeLeft > 0)
13196       {
13197         TimeLeft--;
13198
13199         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13200           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13201
13202         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13203
13204         DisplayGameControlValues();
13205
13206         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13207           for (i = 0; i < MAX_PLAYERS; i++)
13208             KillPlayer(&stored_player[i]);
13209       }
13210       else if (game.no_time_limit && !game.all_players_gone)
13211       {
13212         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13213
13214         DisplayGameControlValues();
13215       }
13216     }
13217
13218     if (tape.single_step && tape.recording && !tape.pausing &&
13219         !player->programmed_action)
13220       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13221
13222     if (!player->programmed_action)
13223       CheckSaveEngineSnapshot(player);
13224   }
13225 }
13226
13227 void ScrollScreen(struct PlayerInfo *player, int mode)
13228 {
13229   static unsigned int screen_frame_counter = 0;
13230
13231   if (mode == SCROLL_INIT)
13232   {
13233     // set scrolling step size according to actual player's moving speed
13234     ScrollStepSize = TILEX / player->move_delay_value;
13235
13236     screen_frame_counter = FrameCounter;
13237     ScreenMovDir = player->MovDir;
13238     ScreenMovPos = player->MovPos;
13239     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13240     return;
13241   }
13242   else if (!FrameReached(&screen_frame_counter, 1))
13243     return;
13244
13245   if (ScreenMovPos)
13246   {
13247     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13248     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13249     redraw_mask |= REDRAW_FIELD;
13250   }
13251   else
13252     ScreenMovDir = MV_NONE;
13253 }
13254
13255 void TestIfPlayerTouchesCustomElement(int x, int y)
13256 {
13257   static int xy[4][2] =
13258   {
13259     { 0, -1 },
13260     { -1, 0 },
13261     { +1, 0 },
13262     { 0, +1 }
13263   };
13264   static int trigger_sides[4][2] =
13265   {
13266     // center side       border side
13267     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13268     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13269     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13270     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13271   };
13272   static int touch_dir[4] =
13273   {
13274     MV_LEFT | MV_RIGHT,
13275     MV_UP   | MV_DOWN,
13276     MV_UP   | MV_DOWN,
13277     MV_LEFT | MV_RIGHT
13278   };
13279   int center_element = Tile[x][y];      // should always be non-moving!
13280   int i;
13281
13282   for (i = 0; i < NUM_DIRECTIONS; i++)
13283   {
13284     int xx = x + xy[i][0];
13285     int yy = y + xy[i][1];
13286     int center_side = trigger_sides[i][0];
13287     int border_side = trigger_sides[i][1];
13288     int border_element;
13289
13290     if (!IN_LEV_FIELD(xx, yy))
13291       continue;
13292
13293     if (IS_PLAYER(x, y))                // player found at center element
13294     {
13295       struct PlayerInfo *player = PLAYERINFO(x, y);
13296
13297       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13298         border_element = Tile[xx][yy];          // may be moving!
13299       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13300         border_element = Tile[xx][yy];
13301       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13302         border_element = MovingOrBlocked2Element(xx, yy);
13303       else
13304         continue;               // center and border element do not touch
13305
13306       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13307                                  player->index_bit, border_side);
13308       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13309                                           CE_PLAYER_TOUCHES_X,
13310                                           player->index_bit, border_side);
13311
13312       {
13313         /* use player element that is initially defined in the level playfield,
13314            not the player element that corresponds to the runtime player number
13315            (example: a level that contains EL_PLAYER_3 as the only player would
13316            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13317         int player_element = PLAYERINFO(x, y)->initial_element;
13318
13319         CheckElementChangeBySide(xx, yy, border_element, player_element,
13320                                  CE_TOUCHING_X, border_side);
13321       }
13322     }
13323     else if (IS_PLAYER(xx, yy))         // player found at border element
13324     {
13325       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13326
13327       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13328       {
13329         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13330           continue;             // center and border element do not touch
13331       }
13332
13333       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13334                                  player->index_bit, center_side);
13335       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13336                                           CE_PLAYER_TOUCHES_X,
13337                                           player->index_bit, center_side);
13338
13339       {
13340         /* use player element that is initially defined in the level playfield,
13341            not the player element that corresponds to the runtime player number
13342            (example: a level that contains EL_PLAYER_3 as the only player would
13343            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13344         int player_element = PLAYERINFO(xx, yy)->initial_element;
13345
13346         CheckElementChangeBySide(x, y, center_element, player_element,
13347                                  CE_TOUCHING_X, center_side);
13348       }
13349
13350       break;
13351     }
13352   }
13353 }
13354
13355 void TestIfElementTouchesCustomElement(int x, int y)
13356 {
13357   static int xy[4][2] =
13358   {
13359     { 0, -1 },
13360     { -1, 0 },
13361     { +1, 0 },
13362     { 0, +1 }
13363   };
13364   static int trigger_sides[4][2] =
13365   {
13366     // center side      border side
13367     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13368     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13369     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13370     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13371   };
13372   static int touch_dir[4] =
13373   {
13374     MV_LEFT | MV_RIGHT,
13375     MV_UP   | MV_DOWN,
13376     MV_UP   | MV_DOWN,
13377     MV_LEFT | MV_RIGHT
13378   };
13379   boolean change_center_element = FALSE;
13380   int center_element = Tile[x][y];      // should always be non-moving!
13381   int border_element_old[NUM_DIRECTIONS];
13382   int i;
13383
13384   for (i = 0; i < NUM_DIRECTIONS; i++)
13385   {
13386     int xx = x + xy[i][0];
13387     int yy = y + xy[i][1];
13388     int border_element;
13389
13390     border_element_old[i] = -1;
13391
13392     if (!IN_LEV_FIELD(xx, yy))
13393       continue;
13394
13395     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13396       border_element = Tile[xx][yy];    // may be moving!
13397     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13398       border_element = Tile[xx][yy];
13399     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13400       border_element = MovingOrBlocked2Element(xx, yy);
13401     else
13402       continue;                 // center and border element do not touch
13403
13404     border_element_old[i] = border_element;
13405   }
13406
13407   for (i = 0; i < NUM_DIRECTIONS; i++)
13408   {
13409     int xx = x + xy[i][0];
13410     int yy = y + xy[i][1];
13411     int center_side = trigger_sides[i][0];
13412     int border_element = border_element_old[i];
13413
13414     if (border_element == -1)
13415       continue;
13416
13417     // check for change of border element
13418     CheckElementChangeBySide(xx, yy, border_element, center_element,
13419                              CE_TOUCHING_X, center_side);
13420
13421     // (center element cannot be player, so we dont have to check this here)
13422   }
13423
13424   for (i = 0; i < NUM_DIRECTIONS; i++)
13425   {
13426     int xx = x + xy[i][0];
13427     int yy = y + xy[i][1];
13428     int border_side = trigger_sides[i][1];
13429     int border_element = border_element_old[i];
13430
13431     if (border_element == -1)
13432       continue;
13433
13434     // check for change of center element (but change it only once)
13435     if (!change_center_element)
13436       change_center_element =
13437         CheckElementChangeBySide(x, y, center_element, border_element,
13438                                  CE_TOUCHING_X, border_side);
13439
13440     if (IS_PLAYER(xx, yy))
13441     {
13442       /* use player element that is initially defined in the level playfield,
13443          not the player element that corresponds to the runtime player number
13444          (example: a level that contains EL_PLAYER_3 as the only player would
13445          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13446       int player_element = PLAYERINFO(xx, yy)->initial_element;
13447
13448       CheckElementChangeBySide(x, y, center_element, player_element,
13449                                CE_TOUCHING_X, border_side);
13450     }
13451   }
13452 }
13453
13454 void TestIfElementHitsCustomElement(int x, int y, int direction)
13455 {
13456   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13457   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13458   int hitx = x + dx, hity = y + dy;
13459   int hitting_element = Tile[x][y];
13460   int touched_element;
13461
13462   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13463     return;
13464
13465   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13466                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13467
13468   if (IN_LEV_FIELD(hitx, hity))
13469   {
13470     int opposite_direction = MV_DIR_OPPOSITE(direction);
13471     int hitting_side = direction;
13472     int touched_side = opposite_direction;
13473     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13474                           MovDir[hitx][hity] != direction ||
13475                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13476
13477     object_hit = TRUE;
13478
13479     if (object_hit)
13480     {
13481       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13482                                CE_HITTING_X, touched_side);
13483
13484       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13485                                CE_HIT_BY_X, hitting_side);
13486
13487       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13488                                CE_HIT_BY_SOMETHING, opposite_direction);
13489
13490       if (IS_PLAYER(hitx, hity))
13491       {
13492         /* use player element that is initially defined in the level playfield,
13493            not the player element that corresponds to the runtime player number
13494            (example: a level that contains EL_PLAYER_3 as the only player would
13495            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13496         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13497
13498         CheckElementChangeBySide(x, y, hitting_element, player_element,
13499                                  CE_HITTING_X, touched_side);
13500       }
13501     }
13502   }
13503
13504   // "hitting something" is also true when hitting the playfield border
13505   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13506                            CE_HITTING_SOMETHING, direction);
13507 }
13508
13509 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13510 {
13511   int i, kill_x = -1, kill_y = -1;
13512
13513   int bad_element = -1;
13514   static int test_xy[4][2] =
13515   {
13516     { 0, -1 },
13517     { -1, 0 },
13518     { +1, 0 },
13519     { 0, +1 }
13520   };
13521   static int test_dir[4] =
13522   {
13523     MV_UP,
13524     MV_LEFT,
13525     MV_RIGHT,
13526     MV_DOWN
13527   };
13528
13529   for (i = 0; i < NUM_DIRECTIONS; i++)
13530   {
13531     int test_x, test_y, test_move_dir, test_element;
13532
13533     test_x = good_x + test_xy[i][0];
13534     test_y = good_y + test_xy[i][1];
13535
13536     if (!IN_LEV_FIELD(test_x, test_y))
13537       continue;
13538
13539     test_move_dir =
13540       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13541
13542     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13543
13544     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13545        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13546     */
13547     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13548         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13549     {
13550       kill_x = test_x;
13551       kill_y = test_y;
13552       bad_element = test_element;
13553
13554       break;
13555     }
13556   }
13557
13558   if (kill_x != -1 || kill_y != -1)
13559   {
13560     if (IS_PLAYER(good_x, good_y))
13561     {
13562       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13563
13564       if (player->shield_deadly_time_left > 0 &&
13565           !IS_INDESTRUCTIBLE(bad_element))
13566         Bang(kill_x, kill_y);
13567       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13568         KillPlayer(player);
13569     }
13570     else
13571       Bang(good_x, good_y);
13572   }
13573 }
13574
13575 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13576 {
13577   int i, kill_x = -1, kill_y = -1;
13578   int bad_element = Tile[bad_x][bad_y];
13579   static int test_xy[4][2] =
13580   {
13581     { 0, -1 },
13582     { -1, 0 },
13583     { +1, 0 },
13584     { 0, +1 }
13585   };
13586   static int touch_dir[4] =
13587   {
13588     MV_LEFT | MV_RIGHT,
13589     MV_UP   | MV_DOWN,
13590     MV_UP   | MV_DOWN,
13591     MV_LEFT | MV_RIGHT
13592   };
13593   static int test_dir[4] =
13594   {
13595     MV_UP,
13596     MV_LEFT,
13597     MV_RIGHT,
13598     MV_DOWN
13599   };
13600
13601   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13602     return;
13603
13604   for (i = 0; i < NUM_DIRECTIONS; i++)
13605   {
13606     int test_x, test_y, test_move_dir, test_element;
13607
13608     test_x = bad_x + test_xy[i][0];
13609     test_y = bad_y + test_xy[i][1];
13610
13611     if (!IN_LEV_FIELD(test_x, test_y))
13612       continue;
13613
13614     test_move_dir =
13615       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13616
13617     test_element = Tile[test_x][test_y];
13618
13619     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13620        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13621     */
13622     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13623         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13624     {
13625       // good thing is player or penguin that does not move away
13626       if (IS_PLAYER(test_x, test_y))
13627       {
13628         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13629
13630         if (bad_element == EL_ROBOT && player->is_moving)
13631           continue;     // robot does not kill player if he is moving
13632
13633         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13634         {
13635           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13636             continue;           // center and border element do not touch
13637         }
13638
13639         kill_x = test_x;
13640         kill_y = test_y;
13641
13642         break;
13643       }
13644       else if (test_element == EL_PENGUIN)
13645       {
13646         kill_x = test_x;
13647         kill_y = test_y;
13648
13649         break;
13650       }
13651     }
13652   }
13653
13654   if (kill_x != -1 || kill_y != -1)
13655   {
13656     if (IS_PLAYER(kill_x, kill_y))
13657     {
13658       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13659
13660       if (player->shield_deadly_time_left > 0 &&
13661           !IS_INDESTRUCTIBLE(bad_element))
13662         Bang(bad_x, bad_y);
13663       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13664         KillPlayer(player);
13665     }
13666     else
13667       Bang(kill_x, kill_y);
13668   }
13669 }
13670
13671 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13672 {
13673   int bad_element = Tile[bad_x][bad_y];
13674   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13675   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13676   int test_x = bad_x + dx, test_y = bad_y + dy;
13677   int test_move_dir, test_element;
13678   int kill_x = -1, kill_y = -1;
13679
13680   if (!IN_LEV_FIELD(test_x, test_y))
13681     return;
13682
13683   test_move_dir =
13684     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13685
13686   test_element = Tile[test_x][test_y];
13687
13688   if (test_move_dir != bad_move_dir)
13689   {
13690     // good thing can be player or penguin that does not move away
13691     if (IS_PLAYER(test_x, test_y))
13692     {
13693       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13694
13695       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13696          player as being hit when he is moving towards the bad thing, because
13697          the "get hit by" condition would be lost after the player stops) */
13698       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13699         return;         // player moves away from bad thing
13700
13701       kill_x = test_x;
13702       kill_y = test_y;
13703     }
13704     else if (test_element == EL_PENGUIN)
13705     {
13706       kill_x = test_x;
13707       kill_y = test_y;
13708     }
13709   }
13710
13711   if (kill_x != -1 || kill_y != -1)
13712   {
13713     if (IS_PLAYER(kill_x, kill_y))
13714     {
13715       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13716
13717       if (player->shield_deadly_time_left > 0 &&
13718           !IS_INDESTRUCTIBLE(bad_element))
13719         Bang(bad_x, bad_y);
13720       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13721         KillPlayer(player);
13722     }
13723     else
13724       Bang(kill_x, kill_y);
13725   }
13726 }
13727
13728 void TestIfPlayerTouchesBadThing(int x, int y)
13729 {
13730   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13731 }
13732
13733 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13734 {
13735   TestIfGoodThingHitsBadThing(x, y, move_dir);
13736 }
13737
13738 void TestIfBadThingTouchesPlayer(int x, int y)
13739 {
13740   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13741 }
13742
13743 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13744 {
13745   TestIfBadThingHitsGoodThing(x, y, move_dir);
13746 }
13747
13748 void TestIfFriendTouchesBadThing(int x, int y)
13749 {
13750   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13751 }
13752
13753 void TestIfBadThingTouchesFriend(int x, int y)
13754 {
13755   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13756 }
13757
13758 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13759 {
13760   int i, kill_x = bad_x, kill_y = bad_y;
13761   static int xy[4][2] =
13762   {
13763     { 0, -1 },
13764     { -1, 0 },
13765     { +1, 0 },
13766     { 0, +1 }
13767   };
13768
13769   for (i = 0; i < NUM_DIRECTIONS; i++)
13770   {
13771     int x, y, element;
13772
13773     x = bad_x + xy[i][0];
13774     y = bad_y + xy[i][1];
13775     if (!IN_LEV_FIELD(x, y))
13776       continue;
13777
13778     element = Tile[x][y];
13779     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13780         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13781     {
13782       kill_x = x;
13783       kill_y = y;
13784       break;
13785     }
13786   }
13787
13788   if (kill_x != bad_x || kill_y != bad_y)
13789     Bang(bad_x, bad_y);
13790 }
13791
13792 void KillPlayer(struct PlayerInfo *player)
13793 {
13794   int jx = player->jx, jy = player->jy;
13795
13796   if (!player->active)
13797     return;
13798
13799 #if 0
13800   Debug("game:playing:KillPlayer",
13801         "0: killed == %d, active == %d, reanimated == %d",
13802         player->killed, player->active, player->reanimated);
13803 #endif
13804
13805   /* the following code was introduced to prevent an infinite loop when calling
13806      -> Bang()
13807      -> CheckTriggeredElementChangeExt()
13808      -> ExecuteCustomElementAction()
13809      -> KillPlayer()
13810      -> (infinitely repeating the above sequence of function calls)
13811      which occurs when killing the player while having a CE with the setting
13812      "kill player X when explosion of <player X>"; the solution using a new
13813      field "player->killed" was chosen for backwards compatibility, although
13814      clever use of the fields "player->active" etc. would probably also work */
13815 #if 1
13816   if (player->killed)
13817     return;
13818 #endif
13819
13820   player->killed = TRUE;
13821
13822   // remove accessible field at the player's position
13823   Tile[jx][jy] = EL_EMPTY;
13824
13825   // deactivate shield (else Bang()/Explode() would not work right)
13826   player->shield_normal_time_left = 0;
13827   player->shield_deadly_time_left = 0;
13828
13829 #if 0
13830   Debug("game:playing:KillPlayer",
13831         "1: killed == %d, active == %d, reanimated == %d",
13832         player->killed, player->active, player->reanimated);
13833 #endif
13834
13835   Bang(jx, jy);
13836
13837 #if 0
13838   Debug("game:playing:KillPlayer",
13839         "2: killed == %d, active == %d, reanimated == %d",
13840         player->killed, player->active, player->reanimated);
13841 #endif
13842
13843   if (player->reanimated)       // killed player may have been reanimated
13844     player->killed = player->reanimated = FALSE;
13845   else
13846     BuryPlayer(player);
13847 }
13848
13849 static void KillPlayerUnlessEnemyProtected(int x, int y)
13850 {
13851   if (!PLAYER_ENEMY_PROTECTED(x, y))
13852     KillPlayer(PLAYERINFO(x, y));
13853 }
13854
13855 static void KillPlayerUnlessExplosionProtected(int x, int y)
13856 {
13857   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13858     KillPlayer(PLAYERINFO(x, y));
13859 }
13860
13861 void BuryPlayer(struct PlayerInfo *player)
13862 {
13863   int jx = player->jx, jy = player->jy;
13864
13865   if (!player->active)
13866     return;
13867
13868   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13869   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13870
13871   RemovePlayer(player);
13872
13873   player->buried = TRUE;
13874
13875   if (game.all_players_gone)
13876     game.GameOver = TRUE;
13877 }
13878
13879 void RemovePlayer(struct PlayerInfo *player)
13880 {
13881   int jx = player->jx, jy = player->jy;
13882   int i, found = FALSE;
13883
13884   player->present = FALSE;
13885   player->active = FALSE;
13886
13887   // required for some CE actions (even if the player is not active anymore)
13888   player->MovPos = 0;
13889
13890   if (!ExplodeField[jx][jy])
13891     StorePlayer[jx][jy] = 0;
13892
13893   if (player->is_moving)
13894     TEST_DrawLevelField(player->last_jx, player->last_jy);
13895
13896   for (i = 0; i < MAX_PLAYERS; i++)
13897     if (stored_player[i].active)
13898       found = TRUE;
13899
13900   if (!found)
13901   {
13902     game.all_players_gone = TRUE;
13903     game.GameOver = TRUE;
13904   }
13905
13906   game.exit_x = game.robot_wheel_x = jx;
13907   game.exit_y = game.robot_wheel_y = jy;
13908 }
13909
13910 void ExitPlayer(struct PlayerInfo *player)
13911 {
13912   DrawPlayer(player);   // needed here only to cleanup last field
13913   RemovePlayer(player);
13914
13915   if (game.players_still_needed > 0)
13916     game.players_still_needed--;
13917 }
13918
13919 static void SetFieldForSnapping(int x, int y, int element, int direction,
13920                                 int player_index_bit)
13921 {
13922   struct ElementInfo *ei = &element_info[element];
13923   int direction_bit = MV_DIR_TO_BIT(direction);
13924   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13925   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13926                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13927
13928   Tile[x][y] = EL_ELEMENT_SNAPPING;
13929   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13930   MovDir[x][y] = direction;
13931   Store[x][y] = element;
13932   Store2[x][y] = player_index_bit;
13933
13934   ResetGfxAnimation(x, y);
13935
13936   GfxElement[x][y] = element;
13937   GfxAction[x][y] = action;
13938   GfxDir[x][y] = direction;
13939   GfxFrame[x][y] = -1;
13940 }
13941
13942 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13943                                    int player_index_bit)
13944 {
13945   TestIfElementTouchesCustomElement(x, y);      // for empty space
13946
13947   if (level.finish_dig_collect)
13948   {
13949     int dig_side = MV_DIR_OPPOSITE(direction);
13950
13951     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13952                                         player_index_bit, dig_side);
13953   }
13954 }
13955
13956 /*
13957   =============================================================================
13958   checkDiagonalPushing()
13959   -----------------------------------------------------------------------------
13960   check if diagonal input device direction results in pushing of object
13961   (by checking if the alternative direction is walkable, diggable, ...)
13962   =============================================================================
13963 */
13964
13965 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13966                                     int x, int y, int real_dx, int real_dy)
13967 {
13968   int jx, jy, dx, dy, xx, yy;
13969
13970   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13971     return TRUE;
13972
13973   // diagonal direction: check alternative direction
13974   jx = player->jx;
13975   jy = player->jy;
13976   dx = x - jx;
13977   dy = y - jy;
13978   xx = jx + (dx == 0 ? real_dx : 0);
13979   yy = jy + (dy == 0 ? real_dy : 0);
13980
13981   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13982 }
13983
13984 /*
13985   =============================================================================
13986   DigField()
13987   -----------------------------------------------------------------------------
13988   x, y:                 field next to player (non-diagonal) to try to dig to
13989   real_dx, real_dy:     direction as read from input device (can be diagonal)
13990   =============================================================================
13991 */
13992
13993 static int DigField(struct PlayerInfo *player,
13994                     int oldx, int oldy, int x, int y,
13995                     int real_dx, int real_dy, int mode)
13996 {
13997   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13998   boolean player_was_pushing = player->is_pushing;
13999   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14000   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14001   int jx = oldx, jy = oldy;
14002   int dx = x - jx, dy = y - jy;
14003   int nextx = x + dx, nexty = y + dy;
14004   int move_direction = (dx == -1 ? MV_LEFT  :
14005                         dx == +1 ? MV_RIGHT :
14006                         dy == -1 ? MV_UP    :
14007                         dy == +1 ? MV_DOWN  : MV_NONE);
14008   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14009   int dig_side = MV_DIR_OPPOSITE(move_direction);
14010   int old_element = Tile[jx][jy];
14011   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14012   int collect_count;
14013
14014   if (is_player)                // function can also be called by EL_PENGUIN
14015   {
14016     if (player->MovPos == 0)
14017     {
14018       player->is_digging = FALSE;
14019       player->is_collecting = FALSE;
14020     }
14021
14022     if (player->MovPos == 0)    // last pushing move finished
14023       player->is_pushing = FALSE;
14024
14025     if (mode == DF_NO_PUSH)     // player just stopped pushing
14026     {
14027       player->is_switching = FALSE;
14028       player->push_delay = -1;
14029
14030       return MP_NO_ACTION;
14031     }
14032   }
14033
14034   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14035     old_element = Back[jx][jy];
14036
14037   // in case of element dropped at player position, check background
14038   else if (Back[jx][jy] != EL_EMPTY &&
14039            game.engine_version >= VERSION_IDENT(2,2,0,0))
14040     old_element = Back[jx][jy];
14041
14042   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14043     return MP_NO_ACTION;        // field has no opening in this direction
14044
14045   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14046     return MP_NO_ACTION;        // field has no opening in this direction
14047
14048   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14049   {
14050     SplashAcid(x, y);
14051
14052     Tile[jx][jy] = player->artwork_element;
14053     InitMovingField(jx, jy, MV_DOWN);
14054     Store[jx][jy] = EL_ACID;
14055     ContinueMoving(jx, jy);
14056     BuryPlayer(player);
14057
14058     return MP_DONT_RUN_INTO;
14059   }
14060
14061   if (player_can_move && DONT_RUN_INTO(element))
14062   {
14063     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14064
14065     return MP_DONT_RUN_INTO;
14066   }
14067
14068   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14069     return MP_NO_ACTION;
14070
14071   collect_count = element_info[element].collect_count_initial;
14072
14073   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14074     return MP_NO_ACTION;
14075
14076   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14077     player_can_move = player_can_move_or_snap;
14078
14079   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14080       game.engine_version >= VERSION_IDENT(2,2,0,0))
14081   {
14082     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14083                                player->index_bit, dig_side);
14084     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14085                                         player->index_bit, dig_side);
14086
14087     if (element == EL_DC_LANDMINE)
14088       Bang(x, y);
14089
14090     if (Tile[x][y] != element)          // field changed by snapping
14091       return MP_ACTION;
14092
14093     return MP_NO_ACTION;
14094   }
14095
14096   if (player->gravity && is_player && !player->is_auto_moving &&
14097       canFallDown(player) && move_direction != MV_DOWN &&
14098       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14099     return MP_NO_ACTION;        // player cannot walk here due to gravity
14100
14101   if (player_can_move &&
14102       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14103   {
14104     int sound_element = SND_ELEMENT(element);
14105     int sound_action = ACTION_WALKING;
14106
14107     if (IS_RND_GATE(element))
14108     {
14109       if (!player->key[RND_GATE_NR(element)])
14110         return MP_NO_ACTION;
14111     }
14112     else if (IS_RND_GATE_GRAY(element))
14113     {
14114       if (!player->key[RND_GATE_GRAY_NR(element)])
14115         return MP_NO_ACTION;
14116     }
14117     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14118     {
14119       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14120         return MP_NO_ACTION;
14121     }
14122     else if (element == EL_EXIT_OPEN ||
14123              element == EL_EM_EXIT_OPEN ||
14124              element == EL_EM_EXIT_OPENING ||
14125              element == EL_STEEL_EXIT_OPEN ||
14126              element == EL_EM_STEEL_EXIT_OPEN ||
14127              element == EL_EM_STEEL_EXIT_OPENING ||
14128              element == EL_SP_EXIT_OPEN ||
14129              element == EL_SP_EXIT_OPENING)
14130     {
14131       sound_action = ACTION_PASSING;    // player is passing exit
14132     }
14133     else if (element == EL_EMPTY)
14134     {
14135       sound_action = ACTION_MOVING;             // nothing to walk on
14136     }
14137
14138     // play sound from background or player, whatever is available
14139     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14140       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14141     else
14142       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14143   }
14144   else if (player_can_move &&
14145            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14146   {
14147     if (!ACCESS_FROM(element, opposite_direction))
14148       return MP_NO_ACTION;      // field not accessible from this direction
14149
14150     if (CAN_MOVE(element))      // only fixed elements can be passed!
14151       return MP_NO_ACTION;
14152
14153     if (IS_EM_GATE(element))
14154     {
14155       if (!player->key[EM_GATE_NR(element)])
14156         return MP_NO_ACTION;
14157     }
14158     else if (IS_EM_GATE_GRAY(element))
14159     {
14160       if (!player->key[EM_GATE_GRAY_NR(element)])
14161         return MP_NO_ACTION;
14162     }
14163     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14164     {
14165       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14166         return MP_NO_ACTION;
14167     }
14168     else if (IS_EMC_GATE(element))
14169     {
14170       if (!player->key[EMC_GATE_NR(element)])
14171         return MP_NO_ACTION;
14172     }
14173     else if (IS_EMC_GATE_GRAY(element))
14174     {
14175       if (!player->key[EMC_GATE_GRAY_NR(element)])
14176         return MP_NO_ACTION;
14177     }
14178     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14179     {
14180       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14181         return MP_NO_ACTION;
14182     }
14183     else if (element == EL_DC_GATE_WHITE ||
14184              element == EL_DC_GATE_WHITE_GRAY ||
14185              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14186     {
14187       if (player->num_white_keys == 0)
14188         return MP_NO_ACTION;
14189
14190       player->num_white_keys--;
14191     }
14192     else if (IS_SP_PORT(element))
14193     {
14194       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14195           element == EL_SP_GRAVITY_PORT_RIGHT ||
14196           element == EL_SP_GRAVITY_PORT_UP ||
14197           element == EL_SP_GRAVITY_PORT_DOWN)
14198         player->gravity = !player->gravity;
14199       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14200                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14201                element == EL_SP_GRAVITY_ON_PORT_UP ||
14202                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14203         player->gravity = TRUE;
14204       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14205                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14206                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14207                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14208         player->gravity = FALSE;
14209     }
14210
14211     // automatically move to the next field with double speed
14212     player->programmed_action = move_direction;
14213
14214     if (player->move_delay_reset_counter == 0)
14215     {
14216       player->move_delay_reset_counter = 2;     // two double speed steps
14217
14218       DOUBLE_PLAYER_SPEED(player);
14219     }
14220
14221     PlayLevelSoundAction(x, y, ACTION_PASSING);
14222   }
14223   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14224   {
14225     RemoveField(x, y);
14226
14227     if (mode != DF_SNAP)
14228     {
14229       GfxElement[x][y] = GFX_ELEMENT(element);
14230       player->is_digging = TRUE;
14231     }
14232
14233     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14234
14235     // use old behaviour for old levels (digging)
14236     if (!level.finish_dig_collect)
14237     {
14238       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14239                                           player->index_bit, dig_side);
14240
14241       // if digging triggered player relocation, finish digging tile
14242       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14243         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14244     }
14245
14246     if (mode == DF_SNAP)
14247     {
14248       if (level.block_snap_field)
14249         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14250       else
14251         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14252
14253       // use old behaviour for old levels (snapping)
14254       if (!level.finish_dig_collect)
14255         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14256                                             player->index_bit, dig_side);
14257     }
14258   }
14259   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14260   {
14261     RemoveField(x, y);
14262
14263     if (is_player && mode != DF_SNAP)
14264     {
14265       GfxElement[x][y] = element;
14266       player->is_collecting = TRUE;
14267     }
14268
14269     if (element == EL_SPEED_PILL)
14270     {
14271       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14272     }
14273     else if (element == EL_EXTRA_TIME && level.time > 0)
14274     {
14275       TimeLeft += level.extra_time;
14276
14277       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14278
14279       DisplayGameControlValues();
14280     }
14281     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14282     {
14283       player->shield_normal_time_left += level.shield_normal_time;
14284       if (element == EL_SHIELD_DEADLY)
14285         player->shield_deadly_time_left += level.shield_deadly_time;
14286     }
14287     else if (element == EL_DYNAMITE ||
14288              element == EL_EM_DYNAMITE ||
14289              element == EL_SP_DISK_RED)
14290     {
14291       if (player->inventory_size < MAX_INVENTORY_SIZE)
14292         player->inventory_element[player->inventory_size++] = element;
14293
14294       DrawGameDoorValues();
14295     }
14296     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14297     {
14298       player->dynabomb_count++;
14299       player->dynabombs_left++;
14300     }
14301     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14302     {
14303       player->dynabomb_size++;
14304     }
14305     else if (element == EL_DYNABOMB_INCREASE_POWER)
14306     {
14307       player->dynabomb_xl = TRUE;
14308     }
14309     else if (IS_KEY(element))
14310     {
14311       player->key[KEY_NR(element)] = TRUE;
14312
14313       DrawGameDoorValues();
14314     }
14315     else if (element == EL_DC_KEY_WHITE)
14316     {
14317       player->num_white_keys++;
14318
14319       // display white keys?
14320       // DrawGameDoorValues();
14321     }
14322     else if (IS_ENVELOPE(element))
14323     {
14324       player->show_envelope = element;
14325     }
14326     else if (element == EL_EMC_LENSES)
14327     {
14328       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14329
14330       RedrawAllInvisibleElementsForLenses();
14331     }
14332     else if (element == EL_EMC_MAGNIFIER)
14333     {
14334       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14335
14336       RedrawAllInvisibleElementsForMagnifier();
14337     }
14338     else if (IS_DROPPABLE(element) ||
14339              IS_THROWABLE(element))     // can be collected and dropped
14340     {
14341       int i;
14342
14343       if (collect_count == 0)
14344         player->inventory_infinite_element = element;
14345       else
14346         for (i = 0; i < collect_count; i++)
14347           if (player->inventory_size < MAX_INVENTORY_SIZE)
14348             player->inventory_element[player->inventory_size++] = element;
14349
14350       DrawGameDoorValues();
14351     }
14352     else if (collect_count > 0)
14353     {
14354       game.gems_still_needed -= collect_count;
14355       if (game.gems_still_needed < 0)
14356         game.gems_still_needed = 0;
14357
14358       game.snapshot.collected_item = TRUE;
14359
14360       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14361
14362       DisplayGameControlValues();
14363     }
14364
14365     RaiseScoreElement(element);
14366     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14367
14368     // use old behaviour for old levels (collecting)
14369     if (!level.finish_dig_collect && is_player)
14370     {
14371       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14372                                           player->index_bit, dig_side);
14373
14374       // if collecting triggered player relocation, finish collecting tile
14375       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14376         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14377     }
14378
14379     if (mode == DF_SNAP)
14380     {
14381       if (level.block_snap_field)
14382         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14383       else
14384         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14385
14386       // use old behaviour for old levels (snapping)
14387       if (!level.finish_dig_collect)
14388         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14389                                             player->index_bit, dig_side);
14390     }
14391   }
14392   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14393   {
14394     if (mode == DF_SNAP && element != EL_BD_ROCK)
14395       return MP_NO_ACTION;
14396
14397     if (CAN_FALL(element) && dy)
14398       return MP_NO_ACTION;
14399
14400     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14401         !(element == EL_SPRING && level.use_spring_bug))
14402       return MP_NO_ACTION;
14403
14404     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14405         ((move_direction & MV_VERTICAL &&
14406           ((element_info[element].move_pattern & MV_LEFT &&
14407             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14408            (element_info[element].move_pattern & MV_RIGHT &&
14409             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14410          (move_direction & MV_HORIZONTAL &&
14411           ((element_info[element].move_pattern & MV_UP &&
14412             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14413            (element_info[element].move_pattern & MV_DOWN &&
14414             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14415       return MP_NO_ACTION;
14416
14417     // do not push elements already moving away faster than player
14418     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14419         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14420       return MP_NO_ACTION;
14421
14422     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14423     {
14424       if (player->push_delay_value == -1 || !player_was_pushing)
14425         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14426     }
14427     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14428     {
14429       if (player->push_delay_value == -1)
14430         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14431     }
14432     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14433     {
14434       if (!player->is_pushing)
14435         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14436     }
14437
14438     player->is_pushing = TRUE;
14439     player->is_active = TRUE;
14440
14441     if (!(IN_LEV_FIELD(nextx, nexty) &&
14442           (IS_FREE(nextx, nexty) ||
14443            (IS_SB_ELEMENT(element) &&
14444             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14445            (IS_CUSTOM_ELEMENT(element) &&
14446             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14447       return MP_NO_ACTION;
14448
14449     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14450       return MP_NO_ACTION;
14451
14452     if (player->push_delay == -1)       // new pushing; restart delay
14453       player->push_delay = 0;
14454
14455     if (player->push_delay < player->push_delay_value &&
14456         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14457         element != EL_SPRING && element != EL_BALLOON)
14458     {
14459       // make sure that there is no move delay before next try to push
14460       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14461         player->move_delay = 0;
14462
14463       return MP_NO_ACTION;
14464     }
14465
14466     if (IS_CUSTOM_ELEMENT(element) &&
14467         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14468     {
14469       if (!DigFieldByCE(nextx, nexty, element))
14470         return MP_NO_ACTION;
14471     }
14472
14473     if (IS_SB_ELEMENT(element))
14474     {
14475       boolean sokoban_task_solved = FALSE;
14476
14477       if (element == EL_SOKOBAN_FIELD_FULL)
14478       {
14479         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14480
14481         IncrementSokobanFieldsNeeded();
14482         IncrementSokobanObjectsNeeded();
14483       }
14484
14485       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14486       {
14487         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14488
14489         DecrementSokobanFieldsNeeded();
14490         DecrementSokobanObjectsNeeded();
14491
14492         // sokoban object was pushed from empty field to sokoban field
14493         if (Back[x][y] == EL_EMPTY)
14494           sokoban_task_solved = TRUE;
14495       }
14496
14497       Tile[x][y] = EL_SOKOBAN_OBJECT;
14498
14499       if (Back[x][y] == Back[nextx][nexty])
14500         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14501       else if (Back[x][y] != 0)
14502         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14503                                     ACTION_EMPTYING);
14504       else
14505         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14506                                     ACTION_FILLING);
14507
14508       if (sokoban_task_solved &&
14509           game.sokoban_fields_still_needed == 0 &&
14510           game.sokoban_objects_still_needed == 0 &&
14511           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14512       {
14513         game.players_still_needed = 0;
14514
14515         LevelSolved();
14516
14517         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14518       }
14519     }
14520     else
14521       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14522
14523     InitMovingField(x, y, move_direction);
14524     GfxAction[x][y] = ACTION_PUSHING;
14525
14526     if (mode == DF_SNAP)
14527       ContinueMoving(x, y);
14528     else
14529       MovPos[x][y] = (dx != 0 ? dx : dy);
14530
14531     Pushed[x][y] = TRUE;
14532     Pushed[nextx][nexty] = TRUE;
14533
14534     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14535       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14536     else
14537       player->push_delay_value = -1;    // get new value later
14538
14539     // check for element change _after_ element has been pushed
14540     if (game.use_change_when_pushing_bug)
14541     {
14542       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14543                                  player->index_bit, dig_side);
14544       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14545                                           player->index_bit, dig_side);
14546     }
14547   }
14548   else if (IS_SWITCHABLE(element))
14549   {
14550     if (PLAYER_SWITCHING(player, x, y))
14551     {
14552       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14553                                           player->index_bit, dig_side);
14554
14555       return MP_ACTION;
14556     }
14557
14558     player->is_switching = TRUE;
14559     player->switch_x = x;
14560     player->switch_y = y;
14561
14562     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14563
14564     if (element == EL_ROBOT_WHEEL)
14565     {
14566       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14567
14568       game.robot_wheel_x = x;
14569       game.robot_wheel_y = y;
14570       game.robot_wheel_active = TRUE;
14571
14572       TEST_DrawLevelField(x, y);
14573     }
14574     else if (element == EL_SP_TERMINAL)
14575     {
14576       int xx, yy;
14577
14578       SCAN_PLAYFIELD(xx, yy)
14579       {
14580         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14581         {
14582           Bang(xx, yy);
14583         }
14584         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14585         {
14586           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14587
14588           ResetGfxAnimation(xx, yy);
14589           TEST_DrawLevelField(xx, yy);
14590         }
14591       }
14592     }
14593     else if (IS_BELT_SWITCH(element))
14594     {
14595       ToggleBeltSwitch(x, y);
14596     }
14597     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14598              element == EL_SWITCHGATE_SWITCH_DOWN ||
14599              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14600              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14601     {
14602       ToggleSwitchgateSwitch(x, y);
14603     }
14604     else if (element == EL_LIGHT_SWITCH ||
14605              element == EL_LIGHT_SWITCH_ACTIVE)
14606     {
14607       ToggleLightSwitch(x, y);
14608     }
14609     else if (element == EL_TIMEGATE_SWITCH ||
14610              element == EL_DC_TIMEGATE_SWITCH)
14611     {
14612       ActivateTimegateSwitch(x, y);
14613     }
14614     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14615              element == EL_BALLOON_SWITCH_RIGHT ||
14616              element == EL_BALLOON_SWITCH_UP    ||
14617              element == EL_BALLOON_SWITCH_DOWN  ||
14618              element == EL_BALLOON_SWITCH_NONE  ||
14619              element == EL_BALLOON_SWITCH_ANY)
14620     {
14621       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14622                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14623                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14624                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14625                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14626                              move_direction);
14627     }
14628     else if (element == EL_LAMP)
14629     {
14630       Tile[x][y] = EL_LAMP_ACTIVE;
14631       game.lights_still_needed--;
14632
14633       ResetGfxAnimation(x, y);
14634       TEST_DrawLevelField(x, y);
14635     }
14636     else if (element == EL_TIME_ORB_FULL)
14637     {
14638       Tile[x][y] = EL_TIME_ORB_EMPTY;
14639
14640       if (level.time > 0 || level.use_time_orb_bug)
14641       {
14642         TimeLeft += level.time_orb_time;
14643         game.no_time_limit = FALSE;
14644
14645         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14646
14647         DisplayGameControlValues();
14648       }
14649
14650       ResetGfxAnimation(x, y);
14651       TEST_DrawLevelField(x, y);
14652     }
14653     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14654              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14655     {
14656       int xx, yy;
14657
14658       game.ball_active = !game.ball_active;
14659
14660       SCAN_PLAYFIELD(xx, yy)
14661       {
14662         int e = Tile[xx][yy];
14663
14664         if (game.ball_active)
14665         {
14666           if (e == EL_EMC_MAGIC_BALL)
14667             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14668           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14669             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14670         }
14671         else
14672         {
14673           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14674             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14675           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14676             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14677         }
14678       }
14679     }
14680
14681     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14682                                         player->index_bit, dig_side);
14683
14684     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14685                                         player->index_bit, dig_side);
14686
14687     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14688                                         player->index_bit, dig_side);
14689
14690     return MP_ACTION;
14691   }
14692   else
14693   {
14694     if (!PLAYER_SWITCHING(player, x, y))
14695     {
14696       player->is_switching = TRUE;
14697       player->switch_x = x;
14698       player->switch_y = y;
14699
14700       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14701                                  player->index_bit, dig_side);
14702       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14703                                           player->index_bit, dig_side);
14704
14705       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14706                                  player->index_bit, dig_side);
14707       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14708                                           player->index_bit, dig_side);
14709     }
14710
14711     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14712                                player->index_bit, dig_side);
14713     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14714                                         player->index_bit, dig_side);
14715
14716     return MP_NO_ACTION;
14717   }
14718
14719   player->push_delay = -1;
14720
14721   if (is_player)                // function can also be called by EL_PENGUIN
14722   {
14723     if (Tile[x][y] != element)          // really digged/collected something
14724     {
14725       player->is_collecting = !player->is_digging;
14726       player->is_active = TRUE;
14727
14728       player->last_removed_element = element;
14729     }
14730   }
14731
14732   return MP_MOVING;
14733 }
14734
14735 static boolean DigFieldByCE(int x, int y, int digging_element)
14736 {
14737   int element = Tile[x][y];
14738
14739   if (!IS_FREE(x, y))
14740   {
14741     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14742                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14743                   ACTION_BREAKING);
14744
14745     // no element can dig solid indestructible elements
14746     if (IS_INDESTRUCTIBLE(element) &&
14747         !IS_DIGGABLE(element) &&
14748         !IS_COLLECTIBLE(element))
14749       return FALSE;
14750
14751     if (AmoebaNr[x][y] &&
14752         (element == EL_AMOEBA_FULL ||
14753          element == EL_BD_AMOEBA ||
14754          element == EL_AMOEBA_GROWING))
14755     {
14756       AmoebaCnt[AmoebaNr[x][y]]--;
14757       AmoebaCnt2[AmoebaNr[x][y]]--;
14758     }
14759
14760     if (IS_MOVING(x, y))
14761       RemoveMovingField(x, y);
14762     else
14763     {
14764       RemoveField(x, y);
14765       TEST_DrawLevelField(x, y);
14766     }
14767
14768     // if digged element was about to explode, prevent the explosion
14769     ExplodeField[x][y] = EX_TYPE_NONE;
14770
14771     PlayLevelSoundAction(x, y, action);
14772   }
14773
14774   Store[x][y] = EL_EMPTY;
14775
14776   // this makes it possible to leave the removed element again
14777   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14778     Store[x][y] = element;
14779
14780   return TRUE;
14781 }
14782
14783 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14784 {
14785   int jx = player->jx, jy = player->jy;
14786   int x = jx + dx, y = jy + dy;
14787   int snap_direction = (dx == -1 ? MV_LEFT  :
14788                         dx == +1 ? MV_RIGHT :
14789                         dy == -1 ? MV_UP    :
14790                         dy == +1 ? MV_DOWN  : MV_NONE);
14791   boolean can_continue_snapping = (level.continuous_snapping &&
14792                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14793
14794   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14795     return FALSE;
14796
14797   if (!player->active || !IN_LEV_FIELD(x, y))
14798     return FALSE;
14799
14800   if (dx && dy)
14801     return FALSE;
14802
14803   if (!dx && !dy)
14804   {
14805     if (player->MovPos == 0)
14806       player->is_pushing = FALSE;
14807
14808     player->is_snapping = FALSE;
14809
14810     if (player->MovPos == 0)
14811     {
14812       player->is_moving = FALSE;
14813       player->is_digging = FALSE;
14814       player->is_collecting = FALSE;
14815     }
14816
14817     return FALSE;
14818   }
14819
14820   // prevent snapping with already pressed snap key when not allowed
14821   if (player->is_snapping && !can_continue_snapping)
14822     return FALSE;
14823
14824   player->MovDir = snap_direction;
14825
14826   if (player->MovPos == 0)
14827   {
14828     player->is_moving = FALSE;
14829     player->is_digging = FALSE;
14830     player->is_collecting = FALSE;
14831   }
14832
14833   player->is_dropping = FALSE;
14834   player->is_dropping_pressed = FALSE;
14835   player->drop_pressed_delay = 0;
14836
14837   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14838     return FALSE;
14839
14840   player->is_snapping = TRUE;
14841   player->is_active = TRUE;
14842
14843   if (player->MovPos == 0)
14844   {
14845     player->is_moving = FALSE;
14846     player->is_digging = FALSE;
14847     player->is_collecting = FALSE;
14848   }
14849
14850   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14851     TEST_DrawLevelField(player->last_jx, player->last_jy);
14852
14853   TEST_DrawLevelField(x, y);
14854
14855   return TRUE;
14856 }
14857
14858 static boolean DropElement(struct PlayerInfo *player)
14859 {
14860   int old_element, new_element;
14861   int dropx = player->jx, dropy = player->jy;
14862   int drop_direction = player->MovDir;
14863   int drop_side = drop_direction;
14864   int drop_element = get_next_dropped_element(player);
14865
14866   /* do not drop an element on top of another element; when holding drop key
14867      pressed without moving, dropped element must move away before the next
14868      element can be dropped (this is especially important if the next element
14869      is dynamite, which can be placed on background for historical reasons) */
14870   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14871     return MP_ACTION;
14872
14873   if (IS_THROWABLE(drop_element))
14874   {
14875     dropx += GET_DX_FROM_DIR(drop_direction);
14876     dropy += GET_DY_FROM_DIR(drop_direction);
14877
14878     if (!IN_LEV_FIELD(dropx, dropy))
14879       return FALSE;
14880   }
14881
14882   old_element = Tile[dropx][dropy];     // old element at dropping position
14883   new_element = drop_element;           // default: no change when dropping
14884
14885   // check if player is active, not moving and ready to drop
14886   if (!player->active || player->MovPos || player->drop_delay > 0)
14887     return FALSE;
14888
14889   // check if player has anything that can be dropped
14890   if (new_element == EL_UNDEFINED)
14891     return FALSE;
14892
14893   // only set if player has anything that can be dropped
14894   player->is_dropping_pressed = TRUE;
14895
14896   // check if drop key was pressed long enough for EM style dynamite
14897   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14898     return FALSE;
14899
14900   // check if anything can be dropped at the current position
14901   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14902     return FALSE;
14903
14904   // collected custom elements can only be dropped on empty fields
14905   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14906     return FALSE;
14907
14908   if (old_element != EL_EMPTY)
14909     Back[dropx][dropy] = old_element;   // store old element on this field
14910
14911   ResetGfxAnimation(dropx, dropy);
14912   ResetRandomAnimationValue(dropx, dropy);
14913
14914   if (player->inventory_size > 0 ||
14915       player->inventory_infinite_element != EL_UNDEFINED)
14916   {
14917     if (player->inventory_size > 0)
14918     {
14919       player->inventory_size--;
14920
14921       DrawGameDoorValues();
14922
14923       if (new_element == EL_DYNAMITE)
14924         new_element = EL_DYNAMITE_ACTIVE;
14925       else if (new_element == EL_EM_DYNAMITE)
14926         new_element = EL_EM_DYNAMITE_ACTIVE;
14927       else if (new_element == EL_SP_DISK_RED)
14928         new_element = EL_SP_DISK_RED_ACTIVE;
14929     }
14930
14931     Tile[dropx][dropy] = new_element;
14932
14933     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14934       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14935                           el2img(Tile[dropx][dropy]), 0);
14936
14937     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14938
14939     // needed if previous element just changed to "empty" in the last frame
14940     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14941
14942     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14943                                player->index_bit, drop_side);
14944     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14945                                         CE_PLAYER_DROPS_X,
14946                                         player->index_bit, drop_side);
14947
14948     TestIfElementTouchesCustomElement(dropx, dropy);
14949   }
14950   else          // player is dropping a dyna bomb
14951   {
14952     player->dynabombs_left--;
14953
14954     Tile[dropx][dropy] = new_element;
14955
14956     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14957       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14958                           el2img(Tile[dropx][dropy]), 0);
14959
14960     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14961   }
14962
14963   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14964     InitField_WithBug1(dropx, dropy, FALSE);
14965
14966   new_element = Tile[dropx][dropy];     // element might have changed
14967
14968   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14969       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14970   {
14971     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14972       MovDir[dropx][dropy] = drop_direction;
14973
14974     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14975
14976     // do not cause impact style collision by dropping elements that can fall
14977     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14978   }
14979
14980   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14981   player->is_dropping = TRUE;
14982
14983   player->drop_pressed_delay = 0;
14984   player->is_dropping_pressed = FALSE;
14985
14986   player->drop_x = dropx;
14987   player->drop_y = dropy;
14988
14989   return TRUE;
14990 }
14991
14992 // ----------------------------------------------------------------------------
14993 // game sound playing functions
14994 // ----------------------------------------------------------------------------
14995
14996 static int *loop_sound_frame = NULL;
14997 static int *loop_sound_volume = NULL;
14998
14999 void InitPlayLevelSound(void)
15000 {
15001   int num_sounds = getSoundListSize();
15002
15003   checked_free(loop_sound_frame);
15004   checked_free(loop_sound_volume);
15005
15006   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15007   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15008 }
15009
15010 static void PlayLevelSound(int x, int y, int nr)
15011 {
15012   int sx = SCREENX(x), sy = SCREENY(y);
15013   int volume, stereo_position;
15014   int max_distance = 8;
15015   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15016
15017   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15018       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15019     return;
15020
15021   if (!IN_LEV_FIELD(x, y) ||
15022       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15023       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15024     return;
15025
15026   volume = SOUND_MAX_VOLUME;
15027
15028   if (!IN_SCR_FIELD(sx, sy))
15029   {
15030     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15031     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15032
15033     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15034   }
15035
15036   stereo_position = (SOUND_MAX_LEFT +
15037                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15038                      (SCR_FIELDX + 2 * max_distance));
15039
15040   if (IS_LOOP_SOUND(nr))
15041   {
15042     /* This assures that quieter loop sounds do not overwrite louder ones,
15043        while restarting sound volume comparison with each new game frame. */
15044
15045     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15046       return;
15047
15048     loop_sound_volume[nr] = volume;
15049     loop_sound_frame[nr] = FrameCounter;
15050   }
15051
15052   PlaySoundExt(nr, volume, stereo_position, type);
15053 }
15054
15055 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15056 {
15057   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15058                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15059                  y < LEVELY(BY1) ? LEVELY(BY1) :
15060                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15061                  sound_action);
15062 }
15063
15064 static void PlayLevelSoundAction(int x, int y, int action)
15065 {
15066   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15067 }
15068
15069 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15070 {
15071   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15072
15073   if (sound_effect != SND_UNDEFINED)
15074     PlayLevelSound(x, y, sound_effect);
15075 }
15076
15077 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15078                                               int action)
15079 {
15080   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15081
15082   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15083     PlayLevelSound(x, y, sound_effect);
15084 }
15085
15086 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15087 {
15088   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15089
15090   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15091     PlayLevelSound(x, y, sound_effect);
15092 }
15093
15094 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15095 {
15096   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15097
15098   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15099     StopSound(sound_effect);
15100 }
15101
15102 static int getLevelMusicNr(void)
15103 {
15104   if (levelset.music[level_nr] != MUS_UNDEFINED)
15105     return levelset.music[level_nr];            // from config file
15106   else
15107     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15108 }
15109
15110 static void FadeLevelSounds(void)
15111 {
15112   FadeSounds();
15113 }
15114
15115 static void FadeLevelMusic(void)
15116 {
15117   int music_nr = getLevelMusicNr();
15118   char *curr_music = getCurrentlyPlayingMusicFilename();
15119   char *next_music = getMusicInfoEntryFilename(music_nr);
15120
15121   if (!strEqual(curr_music, next_music))
15122     FadeMusic();
15123 }
15124
15125 void FadeLevelSoundsAndMusic(void)
15126 {
15127   FadeLevelSounds();
15128   FadeLevelMusic();
15129 }
15130
15131 static void PlayLevelMusic(void)
15132 {
15133   int music_nr = getLevelMusicNr();
15134   char *curr_music = getCurrentlyPlayingMusicFilename();
15135   char *next_music = getMusicInfoEntryFilename(music_nr);
15136
15137   if (!strEqual(curr_music, next_music))
15138     PlayMusicLoop(music_nr);
15139 }
15140
15141 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15142 {
15143   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15144   int offset = 0;
15145   int x = xx - offset;
15146   int y = yy - offset;
15147
15148   switch (sample)
15149   {
15150     case SOUND_blank:
15151       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15152       break;
15153
15154     case SOUND_roll:
15155       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15156       break;
15157
15158     case SOUND_stone:
15159       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15160       break;
15161
15162     case SOUND_nut:
15163       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15164       break;
15165
15166     case SOUND_crack:
15167       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15168       break;
15169
15170     case SOUND_bug:
15171       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15172       break;
15173
15174     case SOUND_tank:
15175       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15176       break;
15177
15178     case SOUND_android_clone:
15179       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15180       break;
15181
15182     case SOUND_android_move:
15183       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15184       break;
15185
15186     case SOUND_spring:
15187       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15188       break;
15189
15190     case SOUND_slurp:
15191       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15192       break;
15193
15194     case SOUND_eater:
15195       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15196       break;
15197
15198     case SOUND_eater_eat:
15199       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15200       break;
15201
15202     case SOUND_alien:
15203       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15204       break;
15205
15206     case SOUND_collect:
15207       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15208       break;
15209
15210     case SOUND_diamond:
15211       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15212       break;
15213
15214     case SOUND_squash:
15215       // !!! CHECK THIS !!!
15216 #if 1
15217       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15218 #else
15219       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15220 #endif
15221       break;
15222
15223     case SOUND_wonderfall:
15224       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15225       break;
15226
15227     case SOUND_drip:
15228       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15229       break;
15230
15231     case SOUND_push:
15232       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15233       break;
15234
15235     case SOUND_dirt:
15236       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15237       break;
15238
15239     case SOUND_acid:
15240       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15241       break;
15242
15243     case SOUND_ball:
15244       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15245       break;
15246
15247     case SOUND_slide:
15248       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15249       break;
15250
15251     case SOUND_wonder:
15252       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15253       break;
15254
15255     case SOUND_door:
15256       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15257       break;
15258
15259     case SOUND_exit_open:
15260       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15261       break;
15262
15263     case SOUND_exit_leave:
15264       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15265       break;
15266
15267     case SOUND_dynamite:
15268       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15269       break;
15270
15271     case SOUND_tick:
15272       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15273       break;
15274
15275     case SOUND_press:
15276       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15277       break;
15278
15279     case SOUND_wheel:
15280       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15281       break;
15282
15283     case SOUND_boom:
15284       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15285       break;
15286
15287     case SOUND_die:
15288       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15289       break;
15290
15291     case SOUND_time:
15292       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15293       break;
15294
15295     default:
15296       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15297       break;
15298   }
15299 }
15300
15301 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15302 {
15303   int element = map_element_SP_to_RND(element_sp);
15304   int action = map_action_SP_to_RND(action_sp);
15305   int offset = (setup.sp_show_border_elements ? 0 : 1);
15306   int x = xx - offset;
15307   int y = yy - offset;
15308
15309   PlayLevelSoundElementAction(x, y, element, action);
15310 }
15311
15312 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15313 {
15314   int element = map_element_MM_to_RND(element_mm);
15315   int action = map_action_MM_to_RND(action_mm);
15316   int offset = 0;
15317   int x = xx - offset;
15318   int y = yy - offset;
15319
15320   if (!IS_MM_ELEMENT(element))
15321     element = EL_MM_DEFAULT;
15322
15323   PlayLevelSoundElementAction(x, y, element, action);
15324 }
15325
15326 void PlaySound_MM(int sound_mm)
15327 {
15328   int sound = map_sound_MM_to_RND(sound_mm);
15329
15330   if (sound == SND_UNDEFINED)
15331     return;
15332
15333   PlaySound(sound);
15334 }
15335
15336 void PlaySoundLoop_MM(int sound_mm)
15337 {
15338   int sound = map_sound_MM_to_RND(sound_mm);
15339
15340   if (sound == SND_UNDEFINED)
15341     return;
15342
15343   PlaySoundLoop(sound);
15344 }
15345
15346 void StopSound_MM(int sound_mm)
15347 {
15348   int sound = map_sound_MM_to_RND(sound_mm);
15349
15350   if (sound == SND_UNDEFINED)
15351     return;
15352
15353   StopSound(sound);
15354 }
15355
15356 void RaiseScore(int value)
15357 {
15358   game.score += value;
15359
15360   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15361
15362   DisplayGameControlValues();
15363 }
15364
15365 void RaiseScoreElement(int element)
15366 {
15367   switch (element)
15368   {
15369     case EL_EMERALD:
15370     case EL_BD_DIAMOND:
15371     case EL_EMERALD_YELLOW:
15372     case EL_EMERALD_RED:
15373     case EL_EMERALD_PURPLE:
15374     case EL_SP_INFOTRON:
15375       RaiseScore(level.score[SC_EMERALD]);
15376       break;
15377     case EL_DIAMOND:
15378       RaiseScore(level.score[SC_DIAMOND]);
15379       break;
15380     case EL_CRYSTAL:
15381       RaiseScore(level.score[SC_CRYSTAL]);
15382       break;
15383     case EL_PEARL:
15384       RaiseScore(level.score[SC_PEARL]);
15385       break;
15386     case EL_BUG:
15387     case EL_BD_BUTTERFLY:
15388     case EL_SP_ELECTRON:
15389       RaiseScore(level.score[SC_BUG]);
15390       break;
15391     case EL_SPACESHIP:
15392     case EL_BD_FIREFLY:
15393     case EL_SP_SNIKSNAK:
15394       RaiseScore(level.score[SC_SPACESHIP]);
15395       break;
15396     case EL_YAMYAM:
15397     case EL_DARK_YAMYAM:
15398       RaiseScore(level.score[SC_YAMYAM]);
15399       break;
15400     case EL_ROBOT:
15401       RaiseScore(level.score[SC_ROBOT]);
15402       break;
15403     case EL_PACMAN:
15404       RaiseScore(level.score[SC_PACMAN]);
15405       break;
15406     case EL_NUT:
15407       RaiseScore(level.score[SC_NUT]);
15408       break;
15409     case EL_DYNAMITE:
15410     case EL_EM_DYNAMITE:
15411     case EL_SP_DISK_RED:
15412     case EL_DYNABOMB_INCREASE_NUMBER:
15413     case EL_DYNABOMB_INCREASE_SIZE:
15414     case EL_DYNABOMB_INCREASE_POWER:
15415       RaiseScore(level.score[SC_DYNAMITE]);
15416       break;
15417     case EL_SHIELD_NORMAL:
15418     case EL_SHIELD_DEADLY:
15419       RaiseScore(level.score[SC_SHIELD]);
15420       break;
15421     case EL_EXTRA_TIME:
15422       RaiseScore(level.extra_time_score);
15423       break;
15424     case EL_KEY_1:
15425     case EL_KEY_2:
15426     case EL_KEY_3:
15427     case EL_KEY_4:
15428     case EL_EM_KEY_1:
15429     case EL_EM_KEY_2:
15430     case EL_EM_KEY_3:
15431     case EL_EM_KEY_4:
15432     case EL_EMC_KEY_5:
15433     case EL_EMC_KEY_6:
15434     case EL_EMC_KEY_7:
15435     case EL_EMC_KEY_8:
15436     case EL_DC_KEY_WHITE:
15437       RaiseScore(level.score[SC_KEY]);
15438       break;
15439     default:
15440       RaiseScore(element_info[element].collect_score);
15441       break;
15442   }
15443 }
15444
15445 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15446 {
15447   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15448   {
15449     if (!quick_quit)
15450     {
15451       // prevent short reactivation of overlay buttons while closing door
15452       SetOverlayActive(FALSE);
15453
15454       // door may still be open due to skipped or envelope style request
15455       CloseDoor(DOOR_CLOSE_1);
15456     }
15457
15458     if (network.enabled)
15459       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15460     else
15461     {
15462       if (quick_quit)
15463         FadeSkipNextFadeIn();
15464
15465       SetGameStatus(GAME_MODE_MAIN);
15466
15467       DrawMainMenu();
15468     }
15469   }
15470   else          // continue playing the game
15471   {
15472     if (tape.playing && tape.deactivate_display)
15473       TapeDeactivateDisplayOff(TRUE);
15474
15475     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15476
15477     if (tape.playing && tape.deactivate_display)
15478       TapeDeactivateDisplayOn();
15479   }
15480 }
15481
15482 void RequestQuitGame(boolean escape_key_pressed)
15483 {
15484   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15485   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15486                         level_editor_test_game);
15487   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15488                           quick_quit);
15489
15490   RequestQuitGameExt(skip_request, quick_quit,
15491                      "Do you really want to quit the game?");
15492 }
15493
15494 void RequestRestartGame(char *message)
15495 {
15496   game.restart_game_message = NULL;
15497
15498   boolean has_started_game = hasStartedNetworkGame();
15499   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15500
15501   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15502   {
15503     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15504   }
15505   else
15506   {
15507     // needed in case of envelope request to close game panel
15508     CloseDoor(DOOR_CLOSE_1);
15509
15510     SetGameStatus(GAME_MODE_MAIN);
15511
15512     DrawMainMenu();
15513   }
15514 }
15515
15516 void CheckGameOver(void)
15517 {
15518   static boolean last_game_over = FALSE;
15519   static int game_over_delay = 0;
15520   int game_over_delay_value = 50;
15521   boolean game_over = checkGameFailed();
15522
15523   // do not handle game over if request dialog is already active
15524   if (game.request_active)
15525     return;
15526
15527   // do not ask to play again if game was never actually played
15528   if (!game.GamePlayed)
15529     return;
15530
15531   if (!game_over)
15532   {
15533     last_game_over = FALSE;
15534     game_over_delay = game_over_delay_value;
15535
15536     return;
15537   }
15538
15539   if (game_over_delay > 0)
15540   {
15541     game_over_delay--;
15542
15543     return;
15544   }
15545
15546   if (last_game_over != game_over)
15547     game.restart_game_message = (hasStartedNetworkGame() ?
15548                                  "Game over! Play it again?" :
15549                                  "Game over!");
15550
15551   last_game_over = game_over;
15552 }
15553
15554 boolean checkGameSolved(void)
15555 {
15556   // set for all game engines if level was solved
15557   return game.LevelSolved_GameEnd;
15558 }
15559
15560 boolean checkGameFailed(void)
15561 {
15562   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15563     return (game_em.game_over && !game_em.level_solved);
15564   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15565     return (game_sp.game_over && !game_sp.level_solved);
15566   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15567     return (game_mm.game_over && !game_mm.level_solved);
15568   else                          // GAME_ENGINE_TYPE_RND
15569     return (game.GameOver && !game.LevelSolved);
15570 }
15571
15572 boolean checkGameEnded(void)
15573 {
15574   return (checkGameSolved() || checkGameFailed());
15575 }
15576
15577
15578 // ----------------------------------------------------------------------------
15579 // random generator functions
15580 // ----------------------------------------------------------------------------
15581
15582 unsigned int InitEngineRandom_RND(int seed)
15583 {
15584   game.num_random_calls = 0;
15585
15586   return InitEngineRandom(seed);
15587 }
15588
15589 unsigned int RND(int max)
15590 {
15591   if (max > 0)
15592   {
15593     game.num_random_calls++;
15594
15595     return GetEngineRandom(max);
15596   }
15597
15598   return 0;
15599 }
15600
15601
15602 // ----------------------------------------------------------------------------
15603 // game engine snapshot handling functions
15604 // ----------------------------------------------------------------------------
15605
15606 struct EngineSnapshotInfo
15607 {
15608   // runtime values for custom element collect score
15609   int collect_score[NUM_CUSTOM_ELEMENTS];
15610
15611   // runtime values for group element choice position
15612   int choice_pos[NUM_GROUP_ELEMENTS];
15613
15614   // runtime values for belt position animations
15615   int belt_graphic[4][NUM_BELT_PARTS];
15616   int belt_anim_mode[4][NUM_BELT_PARTS];
15617 };
15618
15619 static struct EngineSnapshotInfo engine_snapshot_rnd;
15620 static char *snapshot_level_identifier = NULL;
15621 static int snapshot_level_nr = -1;
15622
15623 static void SaveEngineSnapshotValues_RND(void)
15624 {
15625   static int belt_base_active_element[4] =
15626   {
15627     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15628     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15629     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15630     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15631   };
15632   int i, j;
15633
15634   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15635   {
15636     int element = EL_CUSTOM_START + i;
15637
15638     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15639   }
15640
15641   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15642   {
15643     int element = EL_GROUP_START + i;
15644
15645     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15646   }
15647
15648   for (i = 0; i < 4; i++)
15649   {
15650     for (j = 0; j < NUM_BELT_PARTS; j++)
15651     {
15652       int element = belt_base_active_element[i] + j;
15653       int graphic = el2img(element);
15654       int anim_mode = graphic_info[graphic].anim_mode;
15655
15656       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15657       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15658     }
15659   }
15660 }
15661
15662 static void LoadEngineSnapshotValues_RND(void)
15663 {
15664   unsigned int num_random_calls = game.num_random_calls;
15665   int i, j;
15666
15667   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15668   {
15669     int element = EL_CUSTOM_START + i;
15670
15671     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15672   }
15673
15674   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15675   {
15676     int element = EL_GROUP_START + i;
15677
15678     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15679   }
15680
15681   for (i = 0; i < 4; i++)
15682   {
15683     for (j = 0; j < NUM_BELT_PARTS; j++)
15684     {
15685       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15686       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15687
15688       graphic_info[graphic].anim_mode = anim_mode;
15689     }
15690   }
15691
15692   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15693   {
15694     InitRND(tape.random_seed);
15695     for (i = 0; i < num_random_calls; i++)
15696       RND(1);
15697   }
15698
15699   if (game.num_random_calls != num_random_calls)
15700   {
15701     Error("number of random calls out of sync");
15702     Error("number of random calls should be %d", num_random_calls);
15703     Error("number of random calls is %d", game.num_random_calls);
15704
15705     Fail("this should not happen -- please debug");
15706   }
15707 }
15708
15709 void FreeEngineSnapshotSingle(void)
15710 {
15711   FreeSnapshotSingle();
15712
15713   setString(&snapshot_level_identifier, NULL);
15714   snapshot_level_nr = -1;
15715 }
15716
15717 void FreeEngineSnapshotList(void)
15718 {
15719   FreeSnapshotList();
15720 }
15721
15722 static ListNode *SaveEngineSnapshotBuffers(void)
15723 {
15724   ListNode *buffers = NULL;
15725
15726   // copy some special values to a structure better suited for the snapshot
15727
15728   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15729     SaveEngineSnapshotValues_RND();
15730   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15731     SaveEngineSnapshotValues_EM();
15732   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15733     SaveEngineSnapshotValues_SP(&buffers);
15734   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15735     SaveEngineSnapshotValues_MM(&buffers);
15736
15737   // save values stored in special snapshot structure
15738
15739   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15740     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15741   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15742     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15743   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15744     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15745   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15746     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15747
15748   // save further RND engine values
15749
15750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15753
15754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15757   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15759
15760   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15761   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15763
15764   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15765
15766   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15767   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15768
15769   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15770   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15771   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15772   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15773   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15774   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15775   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15776   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15777   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15778   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15779   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15780   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15781   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15782   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15783   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15784   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15785   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15786   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15787
15788   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15789   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15790
15791   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15792   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15793   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15794
15795   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15796   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15797
15798   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15799   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15800   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15801   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15802   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15803
15804   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15805   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15806
15807 #if 0
15808   ListNode *node = engine_snapshot_list_rnd;
15809   int num_bytes = 0;
15810
15811   while (node != NULL)
15812   {
15813     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15814
15815     node = node->next;
15816   }
15817
15818   Debug("game:playing:SaveEngineSnapshotBuffers",
15819         "size of engine snapshot: %d bytes", num_bytes);
15820 #endif
15821
15822   return buffers;
15823 }
15824
15825 void SaveEngineSnapshotSingle(void)
15826 {
15827   ListNode *buffers = SaveEngineSnapshotBuffers();
15828
15829   // finally save all snapshot buffers to single snapshot
15830   SaveSnapshotSingle(buffers);
15831
15832   // save level identification information
15833   setString(&snapshot_level_identifier, leveldir_current->identifier);
15834   snapshot_level_nr = level_nr;
15835 }
15836
15837 boolean CheckSaveEngineSnapshotToList(void)
15838 {
15839   boolean save_snapshot =
15840     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15841      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15842       game.snapshot.changed_action) ||
15843      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15844       game.snapshot.collected_item));
15845
15846   game.snapshot.changed_action = FALSE;
15847   game.snapshot.collected_item = FALSE;
15848   game.snapshot.save_snapshot = save_snapshot;
15849
15850   return save_snapshot;
15851 }
15852
15853 void SaveEngineSnapshotToList(void)
15854 {
15855   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15856       tape.quick_resume)
15857     return;
15858
15859   ListNode *buffers = SaveEngineSnapshotBuffers();
15860
15861   // finally save all snapshot buffers to snapshot list
15862   SaveSnapshotToList(buffers);
15863 }
15864
15865 void SaveEngineSnapshotToListInitial(void)
15866 {
15867   FreeEngineSnapshotList();
15868
15869   SaveEngineSnapshotToList();
15870 }
15871
15872 static void LoadEngineSnapshotValues(void)
15873 {
15874   // restore special values from snapshot structure
15875
15876   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15877     LoadEngineSnapshotValues_RND();
15878   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15879     LoadEngineSnapshotValues_EM();
15880   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15881     LoadEngineSnapshotValues_SP();
15882   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15883     LoadEngineSnapshotValues_MM();
15884 }
15885
15886 void LoadEngineSnapshotSingle(void)
15887 {
15888   LoadSnapshotSingle();
15889
15890   LoadEngineSnapshotValues();
15891 }
15892
15893 static void LoadEngineSnapshot_Undo(int steps)
15894 {
15895   LoadSnapshotFromList_Older(steps);
15896
15897   LoadEngineSnapshotValues();
15898 }
15899
15900 static void LoadEngineSnapshot_Redo(int steps)
15901 {
15902   LoadSnapshotFromList_Newer(steps);
15903
15904   LoadEngineSnapshotValues();
15905 }
15906
15907 boolean CheckEngineSnapshotSingle(void)
15908 {
15909   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15910           snapshot_level_nr == level_nr);
15911 }
15912
15913 boolean CheckEngineSnapshotList(void)
15914 {
15915   return CheckSnapshotList();
15916 }
15917
15918
15919 // ---------- new game button stuff -------------------------------------------
15920
15921 static struct
15922 {
15923   int graphic;
15924   struct XY *pos;
15925   int gadget_id;
15926   boolean *setup_value;
15927   boolean allowed_on_tape;
15928   boolean is_touch_button;
15929   char *infotext;
15930 } gamebutton_info[NUM_GAME_BUTTONS] =
15931 {
15932   {
15933     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15934     GAME_CTRL_ID_STOP,                          NULL,
15935     TRUE, FALSE,                                "stop game"
15936   },
15937   {
15938     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15939     GAME_CTRL_ID_PAUSE,                         NULL,
15940     TRUE, FALSE,                                "pause game"
15941   },
15942   {
15943     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15944     GAME_CTRL_ID_PLAY,                          NULL,
15945     TRUE, FALSE,                                "play game"
15946   },
15947   {
15948     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15949     GAME_CTRL_ID_UNDO,                          NULL,
15950     TRUE, FALSE,                                "undo step"
15951   },
15952   {
15953     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15954     GAME_CTRL_ID_REDO,                          NULL,
15955     TRUE, FALSE,                                "redo step"
15956   },
15957   {
15958     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15959     GAME_CTRL_ID_SAVE,                          NULL,
15960     TRUE, FALSE,                                "save game"
15961   },
15962   {
15963     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15964     GAME_CTRL_ID_PAUSE2,                        NULL,
15965     TRUE, FALSE,                                "pause game"
15966   },
15967   {
15968     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15969     GAME_CTRL_ID_LOAD,                          NULL,
15970     TRUE, FALSE,                                "load game"
15971   },
15972   {
15973     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15974     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15975     FALSE, FALSE,                               "stop game"
15976   },
15977   {
15978     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15979     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15980     FALSE, FALSE,                               "pause game"
15981   },
15982   {
15983     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15984     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15985     FALSE, FALSE,                               "play game"
15986   },
15987   {
15988     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15989     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15990     FALSE, TRUE,                                "stop game"
15991   },
15992   {
15993     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15994     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15995     FALSE, TRUE,                                "pause game"
15996   },
15997   {
15998     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15999     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16000     TRUE, FALSE,                                "background music on/off"
16001   },
16002   {
16003     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16004     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16005     TRUE, FALSE,                                "sound loops on/off"
16006   },
16007   {
16008     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16009     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16010     TRUE, FALSE,                                "normal sounds on/off"
16011   },
16012   {
16013     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16014     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16015     FALSE, FALSE,                               "background music on/off"
16016   },
16017   {
16018     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16019     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16020     FALSE, FALSE,                               "sound loops on/off"
16021   },
16022   {
16023     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16024     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16025     FALSE, FALSE,                               "normal sounds on/off"
16026   }
16027 };
16028
16029 void CreateGameButtons(void)
16030 {
16031   int i;
16032
16033   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16034   {
16035     int graphic = gamebutton_info[i].graphic;
16036     struct GraphicInfo *gfx = &graphic_info[graphic];
16037     struct XY *pos = gamebutton_info[i].pos;
16038     struct GadgetInfo *gi;
16039     int button_type;
16040     boolean checked;
16041     unsigned int event_mask;
16042     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16043     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16044     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16045     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16046     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16047     int gd_x   = gfx->src_x;
16048     int gd_y   = gfx->src_y;
16049     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16050     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16051     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16052     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16053     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16054     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16055     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16056     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16057     int id = i;
16058
16059     if (gfx->bitmap == NULL)
16060     {
16061       game_gadget[id] = NULL;
16062
16063       continue;
16064     }
16065
16066     if (id == GAME_CTRL_ID_STOP ||
16067         id == GAME_CTRL_ID_PANEL_STOP ||
16068         id == GAME_CTRL_ID_TOUCH_STOP ||
16069         id == GAME_CTRL_ID_PLAY ||
16070         id == GAME_CTRL_ID_PANEL_PLAY ||
16071         id == GAME_CTRL_ID_SAVE ||
16072         id == GAME_CTRL_ID_LOAD)
16073     {
16074       button_type = GD_TYPE_NORMAL_BUTTON;
16075       checked = FALSE;
16076       event_mask = GD_EVENT_RELEASED;
16077     }
16078     else if (id == GAME_CTRL_ID_UNDO ||
16079              id == GAME_CTRL_ID_REDO)
16080     {
16081       button_type = GD_TYPE_NORMAL_BUTTON;
16082       checked = FALSE;
16083       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16084     }
16085     else
16086     {
16087       button_type = GD_TYPE_CHECK_BUTTON;
16088       checked = (gamebutton_info[i].setup_value != NULL ?
16089                  *gamebutton_info[i].setup_value : FALSE);
16090       event_mask = GD_EVENT_PRESSED;
16091     }
16092
16093     gi = CreateGadget(GDI_CUSTOM_ID, id,
16094                       GDI_IMAGE_ID, graphic,
16095                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16096                       GDI_X, base_x + x,
16097                       GDI_Y, base_y + y,
16098                       GDI_WIDTH, gfx->width,
16099                       GDI_HEIGHT, gfx->height,
16100                       GDI_TYPE, button_type,
16101                       GDI_STATE, GD_BUTTON_UNPRESSED,
16102                       GDI_CHECKED, checked,
16103                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16104                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16105                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16106                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16107                       GDI_DIRECT_DRAW, FALSE,
16108                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16109                       GDI_EVENT_MASK, event_mask,
16110                       GDI_CALLBACK_ACTION, HandleGameButtons,
16111                       GDI_END);
16112
16113     if (gi == NULL)
16114       Fail("cannot create gadget");
16115
16116     game_gadget[id] = gi;
16117   }
16118 }
16119
16120 void FreeGameButtons(void)
16121 {
16122   int i;
16123
16124   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16125     FreeGadget(game_gadget[i]);
16126 }
16127
16128 static void UnmapGameButtonsAtSamePosition(int id)
16129 {
16130   int i;
16131
16132   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16133     if (i != id &&
16134         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16135         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16136       UnmapGadget(game_gadget[i]);
16137 }
16138
16139 static void UnmapGameButtonsAtSamePosition_All(void)
16140 {
16141   if (setup.show_snapshot_buttons)
16142   {
16143     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16144     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16145     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16146   }
16147   else
16148   {
16149     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16150     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16151     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16152
16153     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16154     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16155     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16156   }
16157 }
16158
16159 static void MapGameButtonsAtSamePosition(int id)
16160 {
16161   int i;
16162
16163   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16164     if (i != id &&
16165         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16166         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16167       MapGadget(game_gadget[i]);
16168
16169   UnmapGameButtonsAtSamePosition_All();
16170 }
16171
16172 void MapUndoRedoButtons(void)
16173 {
16174   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16175   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16176
16177   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16178   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16179 }
16180
16181 void UnmapUndoRedoButtons(void)
16182 {
16183   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16184   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16185
16186   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16187   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16188 }
16189
16190 void ModifyPauseButtons(void)
16191 {
16192   static int ids[] =
16193   {
16194     GAME_CTRL_ID_PAUSE,
16195     GAME_CTRL_ID_PAUSE2,
16196     GAME_CTRL_ID_PANEL_PAUSE,
16197     GAME_CTRL_ID_TOUCH_PAUSE,
16198     -1
16199   };
16200   int i;
16201
16202   for (i = 0; ids[i] > -1; i++)
16203     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16204 }
16205
16206 static void MapGameButtonsExt(boolean on_tape)
16207 {
16208   int i;
16209
16210   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16211     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16212         i != GAME_CTRL_ID_UNDO &&
16213         i != GAME_CTRL_ID_REDO)
16214       MapGadget(game_gadget[i]);
16215
16216   UnmapGameButtonsAtSamePosition_All();
16217
16218   RedrawGameButtons();
16219 }
16220
16221 static void UnmapGameButtonsExt(boolean on_tape)
16222 {
16223   int i;
16224
16225   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16226     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16227       UnmapGadget(game_gadget[i]);
16228 }
16229
16230 static void RedrawGameButtonsExt(boolean on_tape)
16231 {
16232   int i;
16233
16234   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16235     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16236       RedrawGadget(game_gadget[i]);
16237 }
16238
16239 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16240 {
16241   if (gi == NULL)
16242     return;
16243
16244   gi->checked = state;
16245 }
16246
16247 static void RedrawSoundButtonGadget(int id)
16248 {
16249   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16250              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16251              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16252              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16253              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16254              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16255              id);
16256
16257   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16258   RedrawGadget(game_gadget[id2]);
16259 }
16260
16261 void MapGameButtons(void)
16262 {
16263   MapGameButtonsExt(FALSE);
16264 }
16265
16266 void UnmapGameButtons(void)
16267 {
16268   UnmapGameButtonsExt(FALSE);
16269 }
16270
16271 void RedrawGameButtons(void)
16272 {
16273   RedrawGameButtonsExt(FALSE);
16274 }
16275
16276 void MapGameButtonsOnTape(void)
16277 {
16278   MapGameButtonsExt(TRUE);
16279 }
16280
16281 void UnmapGameButtonsOnTape(void)
16282 {
16283   UnmapGameButtonsExt(TRUE);
16284 }
16285
16286 void RedrawGameButtonsOnTape(void)
16287 {
16288   RedrawGameButtonsExt(TRUE);
16289 }
16290
16291 static void GameUndoRedoExt(void)
16292 {
16293   ClearPlayerAction();
16294
16295   tape.pausing = TRUE;
16296
16297   RedrawPlayfield();
16298   UpdateAndDisplayGameControlValues();
16299
16300   DrawCompleteVideoDisplay();
16301   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16302   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16303   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16304
16305   BackToFront();
16306 }
16307
16308 static void GameUndo(int steps)
16309 {
16310   if (!CheckEngineSnapshotList())
16311     return;
16312
16313   LoadEngineSnapshot_Undo(steps);
16314
16315   GameUndoRedoExt();
16316 }
16317
16318 static void GameRedo(int steps)
16319 {
16320   if (!CheckEngineSnapshotList())
16321     return;
16322
16323   LoadEngineSnapshot_Redo(steps);
16324
16325   GameUndoRedoExt();
16326 }
16327
16328 static void HandleGameButtonsExt(int id, int button)
16329 {
16330   static boolean game_undo_executed = FALSE;
16331   int steps = BUTTON_STEPSIZE(button);
16332   boolean handle_game_buttons =
16333     (game_status == GAME_MODE_PLAYING ||
16334      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16335
16336   if (!handle_game_buttons)
16337     return;
16338
16339   switch (id)
16340   {
16341     case GAME_CTRL_ID_STOP:
16342     case GAME_CTRL_ID_PANEL_STOP:
16343     case GAME_CTRL_ID_TOUCH_STOP:
16344       if (game_status == GAME_MODE_MAIN)
16345         break;
16346
16347       if (tape.playing)
16348         TapeStop();
16349       else
16350         RequestQuitGame(FALSE);
16351
16352       break;
16353
16354     case GAME_CTRL_ID_PAUSE:
16355     case GAME_CTRL_ID_PAUSE2:
16356     case GAME_CTRL_ID_PANEL_PAUSE:
16357     case GAME_CTRL_ID_TOUCH_PAUSE:
16358       if (network.enabled && game_status == GAME_MODE_PLAYING)
16359       {
16360         if (tape.pausing)
16361           SendToServer_ContinuePlaying();
16362         else
16363           SendToServer_PausePlaying();
16364       }
16365       else
16366         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16367
16368       game_undo_executed = FALSE;
16369
16370       break;
16371
16372     case GAME_CTRL_ID_PLAY:
16373     case GAME_CTRL_ID_PANEL_PLAY:
16374       if (game_status == GAME_MODE_MAIN)
16375       {
16376         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16377       }
16378       else if (tape.pausing)
16379       {
16380         if (network.enabled)
16381           SendToServer_ContinuePlaying();
16382         else
16383           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16384       }
16385       break;
16386
16387     case GAME_CTRL_ID_UNDO:
16388       // Important: When using "save snapshot when collecting an item" mode,
16389       // load last (current) snapshot for first "undo" after pressing "pause"
16390       // (else the last-but-one snapshot would be loaded, because the snapshot
16391       // pointer already points to the last snapshot when pressing "pause",
16392       // which is fine for "every step/move" mode, but not for "every collect")
16393       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16394           !game_undo_executed)
16395         steps--;
16396
16397       game_undo_executed = TRUE;
16398
16399       GameUndo(steps);
16400       break;
16401
16402     case GAME_CTRL_ID_REDO:
16403       GameRedo(steps);
16404       break;
16405
16406     case GAME_CTRL_ID_SAVE:
16407       TapeQuickSave();
16408       break;
16409
16410     case GAME_CTRL_ID_LOAD:
16411       TapeQuickLoad();
16412       break;
16413
16414     case SOUND_CTRL_ID_MUSIC:
16415     case SOUND_CTRL_ID_PANEL_MUSIC:
16416       if (setup.sound_music)
16417       { 
16418         setup.sound_music = FALSE;
16419
16420         FadeMusic();
16421       }
16422       else if (audio.music_available)
16423       { 
16424         setup.sound = setup.sound_music = TRUE;
16425
16426         SetAudioMode(setup.sound);
16427
16428         if (game_status == GAME_MODE_PLAYING)
16429           PlayLevelMusic();
16430       }
16431
16432       RedrawSoundButtonGadget(id);
16433
16434       break;
16435
16436     case SOUND_CTRL_ID_LOOPS:
16437     case SOUND_CTRL_ID_PANEL_LOOPS:
16438       if (setup.sound_loops)
16439         setup.sound_loops = FALSE;
16440       else if (audio.loops_available)
16441       {
16442         setup.sound = setup.sound_loops = TRUE;
16443
16444         SetAudioMode(setup.sound);
16445       }
16446
16447       RedrawSoundButtonGadget(id);
16448
16449       break;
16450
16451     case SOUND_CTRL_ID_SIMPLE:
16452     case SOUND_CTRL_ID_PANEL_SIMPLE:
16453       if (setup.sound_simple)
16454         setup.sound_simple = FALSE;
16455       else if (audio.sound_available)
16456       {
16457         setup.sound = setup.sound_simple = TRUE;
16458
16459         SetAudioMode(setup.sound);
16460       }
16461
16462       RedrawSoundButtonGadget(id);
16463
16464       break;
16465
16466     default:
16467       break;
16468   }
16469 }
16470
16471 static void HandleGameButtons(struct GadgetInfo *gi)
16472 {
16473   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16474 }
16475
16476 void HandleSoundButtonKeys(Key key)
16477 {
16478   if (key == setup.shortcut.sound_simple)
16479     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16480   else if (key == setup.shortcut.sound_loops)
16481     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16482   else if (key == setup.shortcut.sound_music)
16483     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16484 }