d3d7f513034d9f75dcc5a68d8d9819ef7073dda9
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame : INIT_GFX_RANDOM());
2570
2571         if (gpc->value != gpc->last_value)
2572         {
2573           gpc->gfx_frame = 0;
2574           gpc->gfx_random = init_gfx_random;
2575         }
2576         else
2577         {
2578           gpc->gfx_frame++;
2579
2580           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2581               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2582             gpc->gfx_random = init_gfx_random;
2583         }
2584
2585         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2586           gfx.anim_random_frame = gpc->gfx_random;
2587
2588         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2589           gpc->gfx_frame = element_info[element].collect_score;
2590
2591         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2592
2593         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2594           gfx.anim_random_frame = last_anim_random_frame;
2595       }
2596     }
2597     else if (gpc->type == TYPE_GRAPHIC)
2598     {
2599       if (gpc->graphic != IMG_UNDEFINED)
2600       {
2601         int last_anim_random_frame = gfx.anim_random_frame;
2602         int graphic = gpc->graphic;
2603         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2604                                sync_random_frame : INIT_GFX_RANDOM());
2605
2606         if (gpc->value != gpc->last_value)
2607         {
2608           gpc->gfx_frame = 0;
2609           gpc->gfx_random = init_gfx_random;
2610         }
2611         else
2612         {
2613           gpc->gfx_frame++;
2614
2615           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2616               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2617             gpc->gfx_random = init_gfx_random;
2618         }
2619
2620         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2621           gfx.anim_random_frame = gpc->gfx_random;
2622
2623         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2624
2625         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2626           gfx.anim_random_frame = last_anim_random_frame;
2627       }
2628     }
2629   }
2630 }
2631
2632 static void DisplayGameControlValues(void)
2633 {
2634   boolean redraw_panel = FALSE;
2635   int i;
2636
2637   for (i = 0; game_panel_controls[i].nr != -1; i++)
2638   {
2639     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2640
2641     if (PANEL_DEACTIVATED(gpc->pos))
2642       continue;
2643
2644     if (gpc->value == gpc->last_value &&
2645         gpc->frame == gpc->last_frame)
2646       continue;
2647
2648     redraw_panel = TRUE;
2649   }
2650
2651   if (!redraw_panel)
2652     return;
2653
2654   // copy default game door content to main double buffer
2655
2656   // !!! CHECK AGAIN !!!
2657   SetPanelBackground();
2658   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2659   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2660
2661   // redraw game control buttons
2662   RedrawGameButtons();
2663
2664   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2665
2666   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2667   {
2668     int nr = game_panel_order[i].nr;
2669     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2670     struct TextPosInfo *pos = gpc->pos;
2671     int type = gpc->type;
2672     int value = gpc->value;
2673     int frame = gpc->frame;
2674     int size = pos->size;
2675     int font = pos->font;
2676     boolean draw_masked = pos->draw_masked;
2677     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2678
2679     if (PANEL_DEACTIVATED(pos))
2680       continue;
2681
2682     if (pos->class == get_hash_from_key("extra_panel_items") &&
2683         !setup.prefer_extra_panel_items)
2684       continue;
2685
2686     gpc->last_value = value;
2687     gpc->last_frame = frame;
2688
2689     if (type == TYPE_INTEGER)
2690     {
2691       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2692           nr == GAME_PANEL_INVENTORY_COUNT ||
2693           nr == GAME_PANEL_SCORE ||
2694           nr == GAME_PANEL_HIGHSCORE ||
2695           nr == GAME_PANEL_TIME)
2696       {
2697         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2698
2699         if (use_dynamic_size)           // use dynamic number of digits
2700         {
2701           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2702                               nr == GAME_PANEL_INVENTORY_COUNT ||
2703                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2704           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2705                           nr == GAME_PANEL_INVENTORY_COUNT ||
2706                           nr == GAME_PANEL_TIME ? 1 : 2);
2707           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2708                        nr == GAME_PANEL_INVENTORY_COUNT ||
2709                        nr == GAME_PANEL_TIME ? 3 : 5);
2710           int size2 = size1 + size_add;
2711           int font1 = pos->font;
2712           int font2 = pos->font_alt;
2713
2714           size = (value < value_change ? size1 : size2);
2715           font = (value < value_change ? font1 : font2);
2716         }
2717       }
2718
2719       // correct text size if "digits" is zero or less
2720       if (size <= 0)
2721         size = strlen(int2str(value, size));
2722
2723       // dynamically correct text alignment
2724       pos->width = size * getFontWidth(font);
2725
2726       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2727                   int2str(value, size), font, mask_mode);
2728     }
2729     else if (type == TYPE_ELEMENT)
2730     {
2731       int element, graphic;
2732       Bitmap *src_bitmap;
2733       int src_x, src_y;
2734       int width, height;
2735       int dst_x = PANEL_XPOS(pos);
2736       int dst_y = PANEL_YPOS(pos);
2737
2738       if (value != EL_UNDEFINED && value != EL_EMPTY)
2739       {
2740         element = value;
2741         graphic = el2panelimg(value);
2742
2743 #if 0
2744         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2745               element, EL_NAME(element), size);
2746 #endif
2747
2748         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2749           size = TILESIZE;
2750
2751         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2752                               &src_x, &src_y);
2753
2754         width  = graphic_info[graphic].width  * size / TILESIZE;
2755         height = graphic_info[graphic].height * size / TILESIZE;
2756
2757         if (draw_masked)
2758           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2759                            dst_x, dst_y);
2760         else
2761           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2762                      dst_x, dst_y);
2763       }
2764     }
2765     else if (type == TYPE_GRAPHIC)
2766     {
2767       int graphic        = gpc->graphic;
2768       int graphic_active = gpc->graphic_active;
2769       Bitmap *src_bitmap;
2770       int src_x, src_y;
2771       int width, height;
2772       int dst_x = PANEL_XPOS(pos);
2773       int dst_y = PANEL_YPOS(pos);
2774       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2775                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2776
2777       if (graphic != IMG_UNDEFINED && !skip)
2778       {
2779         if (pos->style == STYLE_REVERSE)
2780           value = 100 - value;
2781
2782         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2783
2784         if (pos->direction & MV_HORIZONTAL)
2785         {
2786           width  = graphic_info[graphic_active].width * value / 100;
2787           height = graphic_info[graphic_active].height;
2788
2789           if (pos->direction == MV_LEFT)
2790           {
2791             src_x += graphic_info[graphic_active].width - width;
2792             dst_x += graphic_info[graphic_active].width - width;
2793           }
2794         }
2795         else
2796         {
2797           width  = graphic_info[graphic_active].width;
2798           height = graphic_info[graphic_active].height * value / 100;
2799
2800           if (pos->direction == MV_UP)
2801           {
2802             src_y += graphic_info[graphic_active].height - height;
2803             dst_y += graphic_info[graphic_active].height - height;
2804           }
2805         }
2806
2807         if (draw_masked)
2808           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2809                            dst_x, dst_y);
2810         else
2811           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2812                      dst_x, dst_y);
2813
2814         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2815
2816         if (pos->direction & MV_HORIZONTAL)
2817         {
2818           if (pos->direction == MV_RIGHT)
2819           {
2820             src_x += width;
2821             dst_x += width;
2822           }
2823           else
2824           {
2825             dst_x = PANEL_XPOS(pos);
2826           }
2827
2828           width = graphic_info[graphic].width - width;
2829         }
2830         else
2831         {
2832           if (pos->direction == MV_DOWN)
2833           {
2834             src_y += height;
2835             dst_y += height;
2836           }
2837           else
2838           {
2839             dst_y = PANEL_YPOS(pos);
2840           }
2841
2842           height = graphic_info[graphic].height - height;
2843         }
2844
2845         if (draw_masked)
2846           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2847                            dst_x, dst_y);
2848         else
2849           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2850                      dst_x, dst_y);
2851       }
2852     }
2853     else if (type == TYPE_STRING)
2854     {
2855       boolean active = (value != 0);
2856       char *state_normal = "off";
2857       char *state_active = "on";
2858       char *state = (active ? state_active : state_normal);
2859       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2860                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2861                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2862                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2863
2864       if (nr == GAME_PANEL_GRAVITY_STATE)
2865       {
2866         int font1 = pos->font;          // (used for normal state)
2867         int font2 = pos->font_alt;      // (used for active state)
2868
2869         font = (active ? font2 : font1);
2870       }
2871
2872       if (s != NULL)
2873       {
2874         char *s_cut;
2875
2876         if (size <= 0)
2877         {
2878           // don't truncate output if "chars" is zero or less
2879           size = strlen(s);
2880
2881           // dynamically correct text alignment
2882           pos->width = size * getFontWidth(font);
2883         }
2884
2885         s_cut = getStringCopyN(s, size);
2886
2887         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2888                     s_cut, font, mask_mode);
2889
2890         free(s_cut);
2891       }
2892     }
2893
2894     redraw_mask |= REDRAW_DOOR_1;
2895   }
2896
2897   SetGameStatus(GAME_MODE_PLAYING);
2898 }
2899
2900 void UpdateAndDisplayGameControlValues(void)
2901 {
2902   if (tape.deactivate_display)
2903     return;
2904
2905   UpdateGameControlValues();
2906   DisplayGameControlValues();
2907 }
2908
2909 void UpdateGameDoorValues(void)
2910 {
2911   UpdateGameControlValues();
2912 }
2913
2914 void DrawGameDoorValues(void)
2915 {
2916   DisplayGameControlValues();
2917 }
2918
2919
2920 // ============================================================================
2921 // InitGameEngine()
2922 // ----------------------------------------------------------------------------
2923 // initialize game engine due to level / tape version number
2924 // ============================================================================
2925
2926 static void InitGameEngine(void)
2927 {
2928   int i, j, k, l, x, y;
2929
2930   // set game engine from tape file when re-playing, else from level file
2931   game.engine_version = (tape.playing ? tape.engine_version :
2932                          level.game_version);
2933
2934   // set single or multi-player game mode (needed for re-playing tapes)
2935   game.team_mode = setup.team_mode;
2936
2937   if (tape.playing)
2938   {
2939     int num_players = 0;
2940
2941     for (i = 0; i < MAX_PLAYERS; i++)
2942       if (tape.player_participates[i])
2943         num_players++;
2944
2945     // multi-player tapes contain input data for more than one player
2946     game.team_mode = (num_players > 1);
2947   }
2948
2949 #if 0
2950   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2951         level.game_version);
2952   Debug("game:init:level", "          tape.file_version   == %06d",
2953         tape.file_version);
2954   Debug("game:init:level", "          tape.game_version   == %06d",
2955         tape.game_version);
2956   Debug("game:init:level", "          tape.engine_version == %06d",
2957         tape.engine_version);
2958   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2959         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2960 #endif
2961
2962   // --------------------------------------------------------------------------
2963   // set flags for bugs and changes according to active game engine version
2964   // --------------------------------------------------------------------------
2965
2966   /*
2967     Summary of bugfix:
2968     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2969
2970     Bug was introduced in version:
2971     2.0.1
2972
2973     Bug was fixed in version:
2974     4.2.0.0
2975
2976     Description:
2977     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2978     but the property "can fall" was missing, which caused some levels to be
2979     unsolvable. This was fixed in version 4.2.0.0.
2980
2981     Affected levels/tapes:
2982     An example for a tape that was fixed by this bugfix is tape 029 from the
2983     level set "rnd_sam_bateman".
2984     The wrong behaviour will still be used for all levels or tapes that were
2985     created/recorded with it. An example for this is tape 023 from the level
2986     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2987   */
2988
2989   boolean use_amoeba_dropping_cannot_fall_bug =
2990     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2991       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2992      (tape.playing &&
2993       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2994       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2995
2996   /*
2997     Summary of bugfix/change:
2998     Fixed move speed of elements entering or leaving magic wall.
2999
3000     Fixed/changed in version:
3001     2.0.1
3002
3003     Description:
3004     Before 2.0.1, move speed of elements entering or leaving magic wall was
3005     twice as fast as it is now.
3006     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3007
3008     Affected levels/tapes:
3009     The first condition is generally needed for all levels/tapes before version
3010     2.0.1, which might use the old behaviour before it was changed; known tapes
3011     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3012     The second condition is an exception from the above case and is needed for
3013     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3014     above, but before it was known that this change would break tapes like the
3015     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3016     although the engine version while recording maybe was before 2.0.1. There
3017     are a lot of tapes that are affected by this exception, like tape 006 from
3018     the level set "rnd_conor_mancone".
3019   */
3020
3021   boolean use_old_move_stepsize_for_magic_wall =
3022     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3023      !(tape.playing &&
3024        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3025        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3026
3027   /*
3028     Summary of bugfix/change:
3029     Fixed handling for custom elements that change when pushed by the player.
3030
3031     Fixed/changed in version:
3032     3.1.0
3033
3034     Description:
3035     Before 3.1.0, custom elements that "change when pushing" changed directly
3036     after the player started pushing them (until then handled in "DigField()").
3037     Since 3.1.0, these custom elements are not changed until the "pushing"
3038     move of the element is finished (now handled in "ContinueMoving()").
3039
3040     Affected levels/tapes:
3041     The first condition is generally needed for all levels/tapes before version
3042     3.1.0, which might use the old behaviour before it was changed; known tapes
3043     that are affected are some tapes from the level set "Walpurgis Gardens" by
3044     Jamie Cullen.
3045     The second condition is an exception from the above case and is needed for
3046     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3047     above (including some development versions of 3.1.0), but before it was
3048     known that this change would break tapes like the above and was fixed in
3049     3.1.1, so that the changed behaviour was active although the engine version
3050     while recording maybe was before 3.1.0. There is at least one tape that is
3051     affected by this exception, which is the tape for the one-level set "Bug
3052     Machine" by Juergen Bonhagen.
3053   */
3054
3055   game.use_change_when_pushing_bug =
3056     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3057      !(tape.playing &&
3058        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3059        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3060
3061   /*
3062     Summary of bugfix/change:
3063     Fixed handling for blocking the field the player leaves when moving.
3064
3065     Fixed/changed in version:
3066     3.1.1
3067
3068     Description:
3069     Before 3.1.1, when "block last field when moving" was enabled, the field
3070     the player is leaving when moving was blocked for the time of the move,
3071     and was directly unblocked afterwards. This resulted in the last field
3072     being blocked for exactly one less than the number of frames of one player
3073     move. Additionally, even when blocking was disabled, the last field was
3074     blocked for exactly one frame.
3075     Since 3.1.1, due to changes in player movement handling, the last field
3076     is not blocked at all when blocking is disabled. When blocking is enabled,
3077     the last field is blocked for exactly the number of frames of one player
3078     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3079     last field is blocked for exactly one more than the number of frames of
3080     one player move.
3081
3082     Affected levels/tapes:
3083     (!!! yet to be determined -- probably many !!!)
3084   */
3085
3086   game.use_block_last_field_bug =
3087     (game.engine_version < VERSION_IDENT(3,1,1,0));
3088
3089   /* various special flags and settings for native Emerald Mine game engine */
3090
3091   game_em.use_single_button =
3092     (game.engine_version > VERSION_IDENT(4,0,0,2));
3093
3094   game_em.use_snap_key_bug =
3095     (game.engine_version < VERSION_IDENT(4,0,1,0));
3096
3097   game_em.use_random_bug =
3098     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3099
3100   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3101
3102   game_em.use_old_explosions            = use_old_em_engine;
3103   game_em.use_old_android               = use_old_em_engine;
3104   game_em.use_old_push_elements         = use_old_em_engine;
3105   game_em.use_old_push_into_acid        = use_old_em_engine;
3106
3107   game_em.use_wrap_around               = !use_old_em_engine;
3108
3109   // --------------------------------------------------------------------------
3110
3111   // set maximal allowed number of custom element changes per game frame
3112   game.max_num_changes_per_frame = 1;
3113
3114   // default scan direction: scan playfield from top/left to bottom/right
3115   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3116
3117   // dynamically adjust element properties according to game engine version
3118   InitElementPropertiesEngine(game.engine_version);
3119
3120   // ---------- initialize special element properties -------------------------
3121
3122   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3123   if (use_amoeba_dropping_cannot_fall_bug)
3124     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3125
3126   // ---------- initialize player's initial move delay ------------------------
3127
3128   // dynamically adjust player properties according to level information
3129   for (i = 0; i < MAX_PLAYERS; i++)
3130     game.initial_move_delay_value[i] =
3131       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3132
3133   // dynamically adjust player properties according to game engine version
3134   for (i = 0; i < MAX_PLAYERS; i++)
3135     game.initial_move_delay[i] =
3136       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3137        game.initial_move_delay_value[i] : 0);
3138
3139   // ---------- initialize player's initial push delay ------------------------
3140
3141   // dynamically adjust player properties according to game engine version
3142   game.initial_push_delay_value =
3143     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3144
3145   // ---------- initialize changing elements ----------------------------------
3146
3147   // initialize changing elements information
3148   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3149   {
3150     struct ElementInfo *ei = &element_info[i];
3151
3152     // this pointer might have been changed in the level editor
3153     ei->change = &ei->change_page[0];
3154
3155     if (!IS_CUSTOM_ELEMENT(i))
3156     {
3157       ei->change->target_element = EL_EMPTY_SPACE;
3158       ei->change->delay_fixed = 0;
3159       ei->change->delay_random = 0;
3160       ei->change->delay_frames = 1;
3161     }
3162
3163     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3164     {
3165       ei->has_change_event[j] = FALSE;
3166
3167       ei->event_page_nr[j] = 0;
3168       ei->event_page[j] = &ei->change_page[0];
3169     }
3170   }
3171
3172   // add changing elements from pre-defined list
3173   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3174   {
3175     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3176     struct ElementInfo *ei = &element_info[ch_delay->element];
3177
3178     ei->change->target_element       = ch_delay->target_element;
3179     ei->change->delay_fixed          = ch_delay->change_delay;
3180
3181     ei->change->pre_change_function  = ch_delay->pre_change_function;
3182     ei->change->change_function      = ch_delay->change_function;
3183     ei->change->post_change_function = ch_delay->post_change_function;
3184
3185     ei->change->can_change = TRUE;
3186     ei->change->can_change_or_has_action = TRUE;
3187
3188     ei->has_change_event[CE_DELAY] = TRUE;
3189
3190     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3191     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3192   }
3193
3194   // ---------- initialize internal run-time variables ------------------------
3195
3196   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3197   {
3198     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3199
3200     for (j = 0; j < ei->num_change_pages; j++)
3201     {
3202       ei->change_page[j].can_change_or_has_action =
3203         (ei->change_page[j].can_change |
3204          ei->change_page[j].has_action);
3205     }
3206   }
3207
3208   // add change events from custom element configuration
3209   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3210   {
3211     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3212
3213     for (j = 0; j < ei->num_change_pages; j++)
3214     {
3215       if (!ei->change_page[j].can_change_or_has_action)
3216         continue;
3217
3218       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3219       {
3220         // only add event page for the first page found with this event
3221         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3222         {
3223           ei->has_change_event[k] = TRUE;
3224
3225           ei->event_page_nr[k] = j;
3226           ei->event_page[k] = &ei->change_page[j];
3227         }
3228       }
3229     }
3230   }
3231
3232   // ---------- initialize reference elements in change conditions ------------
3233
3234   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3235   {
3236     int element = EL_CUSTOM_START + i;
3237     struct ElementInfo *ei = &element_info[element];
3238
3239     for (j = 0; j < ei->num_change_pages; j++)
3240     {
3241       int trigger_element = ei->change_page[j].initial_trigger_element;
3242
3243       if (trigger_element >= EL_PREV_CE_8 &&
3244           trigger_element <= EL_NEXT_CE_8)
3245         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3246
3247       ei->change_page[j].trigger_element = trigger_element;
3248     }
3249   }
3250
3251   // ---------- initialize run-time trigger player and element ----------------
3252
3253   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3254   {
3255     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3256
3257     for (j = 0; j < ei->num_change_pages; j++)
3258     {
3259       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3260       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3261       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3262       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3263       ei->change_page[j].actual_trigger_ce_value = 0;
3264       ei->change_page[j].actual_trigger_ce_score = 0;
3265     }
3266   }
3267
3268   // ---------- initialize trigger events -------------------------------------
3269
3270   // initialize trigger events information
3271   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3272     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3273       trigger_events[i][j] = FALSE;
3274
3275   // add trigger events from element change event properties
3276   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3277   {
3278     struct ElementInfo *ei = &element_info[i];
3279
3280     for (j = 0; j < ei->num_change_pages; j++)
3281     {
3282       if (!ei->change_page[j].can_change_or_has_action)
3283         continue;
3284
3285       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3286       {
3287         int trigger_element = ei->change_page[j].trigger_element;
3288
3289         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3290         {
3291           if (ei->change_page[j].has_event[k])
3292           {
3293             if (IS_GROUP_ELEMENT(trigger_element))
3294             {
3295               struct ElementGroupInfo *group =
3296                 element_info[trigger_element].group;
3297
3298               for (l = 0; l < group->num_elements_resolved; l++)
3299                 trigger_events[group->element_resolved[l]][k] = TRUE;
3300             }
3301             else if (trigger_element == EL_ANY_ELEMENT)
3302               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3303                 trigger_events[l][k] = TRUE;
3304             else
3305               trigger_events[trigger_element][k] = TRUE;
3306           }
3307         }
3308       }
3309     }
3310   }
3311
3312   // ---------- initialize push delay -----------------------------------------
3313
3314   // initialize push delay values to default
3315   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3316   {
3317     if (!IS_CUSTOM_ELEMENT(i))
3318     {
3319       // set default push delay values (corrected since version 3.0.7-1)
3320       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3321       {
3322         element_info[i].push_delay_fixed = 2;
3323         element_info[i].push_delay_random = 8;
3324       }
3325       else
3326       {
3327         element_info[i].push_delay_fixed = 8;
3328         element_info[i].push_delay_random = 8;
3329       }
3330     }
3331   }
3332
3333   // set push delay value for certain elements from pre-defined list
3334   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3335   {
3336     int e = push_delay_list[i].element;
3337
3338     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3339     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3340   }
3341
3342   // set push delay value for Supaplex elements for newer engine versions
3343   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3344   {
3345     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3346     {
3347       if (IS_SP_ELEMENT(i))
3348       {
3349         // set SP push delay to just enough to push under a falling zonk
3350         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3351
3352         element_info[i].push_delay_fixed  = delay;
3353         element_info[i].push_delay_random = 0;
3354       }
3355     }
3356   }
3357
3358   // ---------- initialize move stepsize --------------------------------------
3359
3360   // initialize move stepsize values to default
3361   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3362     if (!IS_CUSTOM_ELEMENT(i))
3363       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3364
3365   // set move stepsize value for certain elements from pre-defined list
3366   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3367   {
3368     int e = move_stepsize_list[i].element;
3369
3370     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3371
3372     // set move stepsize value for certain elements for older engine versions
3373     if (use_old_move_stepsize_for_magic_wall)
3374     {
3375       if (e == EL_MAGIC_WALL_FILLING ||
3376           e == EL_MAGIC_WALL_EMPTYING ||
3377           e == EL_BD_MAGIC_WALL_FILLING ||
3378           e == EL_BD_MAGIC_WALL_EMPTYING)
3379         element_info[e].move_stepsize *= 2;
3380     }
3381   }
3382
3383   // ---------- initialize collect score --------------------------------------
3384
3385   // initialize collect score values for custom elements from initial value
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387     if (IS_CUSTOM_ELEMENT(i))
3388       element_info[i].collect_score = element_info[i].collect_score_initial;
3389
3390   // ---------- initialize collect count --------------------------------------
3391
3392   // initialize collect count values for non-custom elements
3393   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3394     if (!IS_CUSTOM_ELEMENT(i))
3395       element_info[i].collect_count_initial = 0;
3396
3397   // add collect count values for all elements from pre-defined list
3398   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3399     element_info[collect_count_list[i].element].collect_count_initial =
3400       collect_count_list[i].count;
3401
3402   // ---------- initialize access direction -----------------------------------
3403
3404   // initialize access direction values to default (access from every side)
3405   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3406     if (!IS_CUSTOM_ELEMENT(i))
3407       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3408
3409   // set access direction value for certain elements from pre-defined list
3410   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3411     element_info[access_direction_list[i].element].access_direction =
3412       access_direction_list[i].direction;
3413
3414   // ---------- initialize explosion content ----------------------------------
3415   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3416   {
3417     if (IS_CUSTOM_ELEMENT(i))
3418       continue;
3419
3420     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3421     {
3422       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3423
3424       element_info[i].content.e[x][y] =
3425         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3426          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3427          i == EL_PLAYER_3 ? EL_EMERALD :
3428          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3429          i == EL_MOLE ? EL_EMERALD_RED :
3430          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3431          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3432          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3433          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3434          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3435          i == EL_WALL_EMERALD ? EL_EMERALD :
3436          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3437          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3438          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3439          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3440          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3441          i == EL_WALL_PEARL ? EL_PEARL :
3442          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3443          EL_EMPTY);
3444     }
3445   }
3446
3447   // ---------- initialize recursion detection --------------------------------
3448   recursion_loop_depth = 0;
3449   recursion_loop_detected = FALSE;
3450   recursion_loop_element = EL_UNDEFINED;
3451
3452   // ---------- initialize graphics engine ------------------------------------
3453   game.scroll_delay_value =
3454     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3455      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3456      !setup.forced_scroll_delay           ? 0 :
3457      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3458   game.scroll_delay_value =
3459     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3460
3461   // ---------- initialize game engine snapshots ------------------------------
3462   for (i = 0; i < MAX_PLAYERS; i++)
3463     game.snapshot.last_action[i] = 0;
3464   game.snapshot.changed_action = FALSE;
3465   game.snapshot.collected_item = FALSE;
3466   game.snapshot.mode =
3467     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3468      SNAPSHOT_MODE_EVERY_STEP :
3469      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3470      SNAPSHOT_MODE_EVERY_MOVE :
3471      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3472      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3473   game.snapshot.save_snapshot = FALSE;
3474
3475   // ---------- initialize level time for Supaplex engine ---------------------
3476   // Supaplex levels with time limit currently unsupported -- should be added
3477   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3478     level.time = 0;
3479
3480   // ---------- initialize flags for handling game actions --------------------
3481
3482   // set flags for game actions to default values
3483   game.use_key_actions = TRUE;
3484   game.use_mouse_actions = FALSE;
3485
3486   // when using Mirror Magic game engine, handle mouse events only
3487   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3488   {
3489     game.use_key_actions = FALSE;
3490     game.use_mouse_actions = TRUE;
3491   }
3492
3493   // check for custom elements with mouse click events
3494   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3495   {
3496     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3497     {
3498       int element = EL_CUSTOM_START + i;
3499
3500       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3501           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3502           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3503           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3504         game.use_mouse_actions = TRUE;
3505     }
3506   }
3507 }
3508
3509 static int get_num_special_action(int element, int action_first,
3510                                   int action_last)
3511 {
3512   int num_special_action = 0;
3513   int i, j;
3514
3515   for (i = action_first; i <= action_last; i++)
3516   {
3517     boolean found = FALSE;
3518
3519     for (j = 0; j < NUM_DIRECTIONS; j++)
3520       if (el_act_dir2img(element, i, j) !=
3521           el_act_dir2img(element, ACTION_DEFAULT, j))
3522         found = TRUE;
3523
3524     if (found)
3525       num_special_action++;
3526     else
3527       break;
3528   }
3529
3530   return num_special_action;
3531 }
3532
3533
3534 // ============================================================================
3535 // InitGame()
3536 // ----------------------------------------------------------------------------
3537 // initialize and start new game
3538 // ============================================================================
3539
3540 #if DEBUG_INIT_PLAYER
3541 static void DebugPrintPlayerStatus(char *message)
3542 {
3543   int i;
3544
3545   if (!options.debug)
3546     return;
3547
3548   Debug("game:init:player", "%s:", message);
3549
3550   for (i = 0; i < MAX_PLAYERS; i++)
3551   {
3552     struct PlayerInfo *player = &stored_player[i];
3553
3554     Debug("game:init:player",
3555           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3556           i + 1,
3557           player->present,
3558           player->connected,
3559           player->connected_locally,
3560           player->connected_network,
3561           player->active,
3562           (local_player == player ? " (local player)" : ""));
3563   }
3564 }
3565 #endif
3566
3567 void InitGame(void)
3568 {
3569   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3570   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3571   int fade_mask = REDRAW_FIELD;
3572
3573   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3574   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3575   int initial_move_dir = MV_DOWN;
3576   int i, j, x, y;
3577
3578   // required here to update video display before fading (FIX THIS)
3579   DrawMaskedBorder(REDRAW_DOOR_2);
3580
3581   if (!game.restart_level)
3582     CloseDoor(DOOR_CLOSE_1);
3583
3584   SetGameStatus(GAME_MODE_PLAYING);
3585
3586   if (level_editor_test_game)
3587     FadeSkipNextFadeOut();
3588   else
3589     FadeSetEnterScreen();
3590
3591   if (CheckFadeAll())
3592     fade_mask = REDRAW_ALL;
3593
3594   FadeLevelSoundsAndMusic();
3595
3596   ExpireSoundLoops(TRUE);
3597
3598   FadeOut(fade_mask);
3599
3600   if (level_editor_test_game)
3601     FadeSkipNextFadeIn();
3602
3603   // needed if different viewport properties defined for playing
3604   ChangeViewportPropertiesIfNeeded();
3605
3606   ClearField();
3607
3608   DrawCompleteVideoDisplay();
3609
3610   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3611
3612   InitGameEngine();
3613   InitGameControlValues();
3614
3615   if (tape.recording)
3616   {
3617     // initialize tape actions from game when recording tape
3618     tape.use_key_actions   = game.use_key_actions;
3619     tape.use_mouse_actions = game.use_mouse_actions;
3620
3621     // initialize visible playfield size when recording tape (for team mode)
3622     tape.scr_fieldx = SCR_FIELDX;
3623     tape.scr_fieldy = SCR_FIELDY;
3624   }
3625
3626   // don't play tapes over network
3627   network_playing = (network.enabled && !tape.playing);
3628
3629   for (i = 0; i < MAX_PLAYERS; i++)
3630   {
3631     struct PlayerInfo *player = &stored_player[i];
3632
3633     player->index_nr = i;
3634     player->index_bit = (1 << i);
3635     player->element_nr = EL_PLAYER_1 + i;
3636
3637     player->present = FALSE;
3638     player->active = FALSE;
3639     player->mapped = FALSE;
3640
3641     player->killed = FALSE;
3642     player->reanimated = FALSE;
3643     player->buried = FALSE;
3644
3645     player->action = 0;
3646     player->effective_action = 0;
3647     player->programmed_action = 0;
3648     player->snap_action = 0;
3649
3650     player->mouse_action.lx = 0;
3651     player->mouse_action.ly = 0;
3652     player->mouse_action.button = 0;
3653     player->mouse_action.button_hint = 0;
3654
3655     player->effective_mouse_action.lx = 0;
3656     player->effective_mouse_action.ly = 0;
3657     player->effective_mouse_action.button = 0;
3658     player->effective_mouse_action.button_hint = 0;
3659
3660     for (j = 0; j < MAX_NUM_KEYS; j++)
3661       player->key[j] = FALSE;
3662
3663     player->num_white_keys = 0;
3664
3665     player->dynabomb_count = 0;
3666     player->dynabomb_size = 1;
3667     player->dynabombs_left = 0;
3668     player->dynabomb_xl = FALSE;
3669
3670     player->MovDir = initial_move_dir;
3671     player->MovPos = 0;
3672     player->GfxPos = 0;
3673     player->GfxDir = initial_move_dir;
3674     player->GfxAction = ACTION_DEFAULT;
3675     player->Frame = 0;
3676     player->StepFrame = 0;
3677
3678     player->initial_element = player->element_nr;
3679     player->artwork_element =
3680       (level.use_artwork_element[i] ? level.artwork_element[i] :
3681        player->element_nr);
3682     player->use_murphy = FALSE;
3683
3684     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3685     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3686
3687     player->gravity = level.initial_player_gravity[i];
3688
3689     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3690
3691     player->actual_frame_counter.count = 0;
3692     player->actual_frame_counter.value = 1;
3693
3694     player->step_counter = 0;
3695
3696     player->last_move_dir = initial_move_dir;
3697
3698     player->is_active = FALSE;
3699
3700     player->is_waiting = FALSE;
3701     player->is_moving = FALSE;
3702     player->is_auto_moving = FALSE;
3703     player->is_digging = FALSE;
3704     player->is_snapping = FALSE;
3705     player->is_collecting = FALSE;
3706     player->is_pushing = FALSE;
3707     player->is_switching = FALSE;
3708     player->is_dropping = FALSE;
3709     player->is_dropping_pressed = FALSE;
3710
3711     player->is_bored = FALSE;
3712     player->is_sleeping = FALSE;
3713
3714     player->was_waiting = TRUE;
3715     player->was_moving = FALSE;
3716     player->was_snapping = FALSE;
3717     player->was_dropping = FALSE;
3718
3719     player->force_dropping = FALSE;
3720
3721     player->frame_counter_bored = -1;
3722     player->frame_counter_sleeping = -1;
3723
3724     player->anim_delay_counter = 0;
3725     player->post_delay_counter = 0;
3726
3727     player->dir_waiting = initial_move_dir;
3728     player->action_waiting = ACTION_DEFAULT;
3729     player->last_action_waiting = ACTION_DEFAULT;
3730     player->special_action_bored = ACTION_DEFAULT;
3731     player->special_action_sleeping = ACTION_DEFAULT;
3732
3733     player->switch_x = -1;
3734     player->switch_y = -1;
3735
3736     player->drop_x = -1;
3737     player->drop_y = -1;
3738
3739     player->show_envelope = 0;
3740
3741     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3742
3743     player->push_delay       = -1;      // initialized when pushing starts
3744     player->push_delay_value = game.initial_push_delay_value;
3745
3746     player->drop_delay = 0;
3747     player->drop_pressed_delay = 0;
3748
3749     player->last_jx = -1;
3750     player->last_jy = -1;
3751     player->jx = -1;
3752     player->jy = -1;
3753
3754     player->shield_normal_time_left = 0;
3755     player->shield_deadly_time_left = 0;
3756
3757     player->last_removed_element = EL_UNDEFINED;
3758
3759     player->inventory_infinite_element = EL_UNDEFINED;
3760     player->inventory_size = 0;
3761
3762     if (level.use_initial_inventory[i])
3763     {
3764       for (j = 0; j < level.initial_inventory_size[i]; j++)
3765       {
3766         int element = level.initial_inventory_content[i][j];
3767         int collect_count = element_info[element].collect_count_initial;
3768         int k;
3769
3770         if (!IS_CUSTOM_ELEMENT(element))
3771           collect_count = 1;
3772
3773         if (collect_count == 0)
3774           player->inventory_infinite_element = element;
3775         else
3776           for (k = 0; k < collect_count; k++)
3777             if (player->inventory_size < MAX_INVENTORY_SIZE)
3778               player->inventory_element[player->inventory_size++] = element;
3779       }
3780     }
3781
3782     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3783     SnapField(player, 0, 0);
3784
3785     map_player_action[i] = i;
3786   }
3787
3788   network_player_action_received = FALSE;
3789
3790   // initial null action
3791   if (network_playing)
3792     SendToServer_MovePlayer(MV_NONE);
3793
3794   FrameCounter = 0;
3795   TimeFrames = 0;
3796   TimePlayed = 0;
3797   TimeLeft = level.time;
3798   TapeTime = 0;
3799
3800   ScreenMovDir = MV_NONE;
3801   ScreenMovPos = 0;
3802   ScreenGfxPos = 0;
3803
3804   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3805
3806   game.robot_wheel_x = -1;
3807   game.robot_wheel_y = -1;
3808
3809   game.exit_x = -1;
3810   game.exit_y = -1;
3811
3812   game.all_players_gone = FALSE;
3813
3814   game.LevelSolved = FALSE;
3815   game.GameOver = FALSE;
3816
3817   game.GamePlayed = !tape.playing;
3818
3819   game.LevelSolved_GameWon = FALSE;
3820   game.LevelSolved_GameEnd = FALSE;
3821   game.LevelSolved_SaveTape = FALSE;
3822   game.LevelSolved_SaveScore = FALSE;
3823
3824   game.LevelSolved_CountingTime = 0;
3825   game.LevelSolved_CountingScore = 0;
3826   game.LevelSolved_CountingHealth = 0;
3827
3828   game.panel.active = TRUE;
3829
3830   game.no_level_time_limit = (level.time == 0);
3831   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3832
3833   game.yamyam_content_nr = 0;
3834   game.robot_wheel_active = FALSE;
3835   game.magic_wall_active = FALSE;
3836   game.magic_wall_time_left = 0;
3837   game.light_time_left = 0;
3838   game.timegate_time_left = 0;
3839   game.switchgate_pos = 0;
3840   game.wind_direction = level.wind_direction_initial;
3841
3842   game.time_final = 0;
3843   game.score_time_final = 0;
3844
3845   game.score = 0;
3846   game.score_final = 0;
3847
3848   game.health = MAX_HEALTH;
3849   game.health_final = MAX_HEALTH;
3850
3851   game.gems_still_needed = level.gems_needed;
3852   game.sokoban_fields_still_needed = 0;
3853   game.sokoban_objects_still_needed = 0;
3854   game.lights_still_needed = 0;
3855   game.players_still_needed = 0;
3856   game.friends_still_needed = 0;
3857
3858   game.lenses_time_left = 0;
3859   game.magnify_time_left = 0;
3860
3861   game.ball_active = level.ball_active_initial;
3862   game.ball_content_nr = 0;
3863
3864   game.explosions_delayed = TRUE;
3865
3866   game.envelope_active = FALSE;
3867
3868   // special case: set custom artwork setting to initial value
3869   game.use_masked_elements = game.use_masked_elements_initial;
3870
3871   for (i = 0; i < NUM_BELTS; i++)
3872   {
3873     game.belt_dir[i] = MV_NONE;
3874     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3875   }
3876
3877   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3878     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3879
3880 #if DEBUG_INIT_PLAYER
3881   DebugPrintPlayerStatus("Player status at level initialization");
3882 #endif
3883
3884   SCAN_PLAYFIELD(x, y)
3885   {
3886     Tile[x][y] = Last[x][y] = level.field[x][y];
3887     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3888     ChangeDelay[x][y] = 0;
3889     ChangePage[x][y] = -1;
3890     CustomValue[x][y] = 0;              // initialized in InitField()
3891     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3892     AmoebaNr[x][y] = 0;
3893     WasJustMoving[x][y] = 0;
3894     WasJustFalling[x][y] = 0;
3895     CheckCollision[x][y] = 0;
3896     CheckImpact[x][y] = 0;
3897     Stop[x][y] = FALSE;
3898     Pushed[x][y] = FALSE;
3899
3900     ChangeCount[x][y] = 0;
3901     ChangeEvent[x][y] = -1;
3902
3903     ExplodePhase[x][y] = 0;
3904     ExplodeDelay[x][y] = 0;
3905     ExplodeField[x][y] = EX_TYPE_NONE;
3906
3907     RunnerVisit[x][y] = 0;
3908     PlayerVisit[x][y] = 0;
3909
3910     GfxFrame[x][y] = 0;
3911     GfxRandom[x][y] = INIT_GFX_RANDOM();
3912     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3913     GfxElement[x][y] = EL_UNDEFINED;
3914     GfxElementEmpty[x][y] = EL_EMPTY;
3915     GfxAction[x][y] = ACTION_DEFAULT;
3916     GfxDir[x][y] = MV_NONE;
3917     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3918   }
3919
3920   SCAN_PLAYFIELD(x, y)
3921   {
3922     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3923       emulate_bd = FALSE;
3924     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3925       emulate_sp = FALSE;
3926
3927     InitField(x, y, TRUE);
3928
3929     ResetGfxAnimation(x, y);
3930   }
3931
3932   InitBeltMovement();
3933
3934   for (i = 0; i < MAX_PLAYERS; i++)
3935   {
3936     struct PlayerInfo *player = &stored_player[i];
3937
3938     // set number of special actions for bored and sleeping animation
3939     player->num_special_action_bored =
3940       get_num_special_action(player->artwork_element,
3941                              ACTION_BORING_1, ACTION_BORING_LAST);
3942     player->num_special_action_sleeping =
3943       get_num_special_action(player->artwork_element,
3944                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3945   }
3946
3947   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3948                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3949
3950   // initialize type of slippery elements
3951   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3952   {
3953     if (!IS_CUSTOM_ELEMENT(i))
3954     {
3955       // default: elements slip down either to the left or right randomly
3956       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3957
3958       // SP style elements prefer to slip down on the left side
3959       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3960         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3961
3962       // BD style elements prefer to slip down on the left side
3963       if (game.emulation == EMU_BOULDERDASH)
3964         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3965     }
3966   }
3967
3968   // initialize explosion and ignition delay
3969   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3970   {
3971     if (!IS_CUSTOM_ELEMENT(i))
3972     {
3973       int num_phase = 8;
3974       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3975                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3976                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3977       int last_phase = (num_phase + 1) * delay;
3978       int half_phase = (num_phase / 2) * delay;
3979
3980       element_info[i].explosion_delay = last_phase - 1;
3981       element_info[i].ignition_delay = half_phase;
3982
3983       if (i == EL_BLACK_ORB)
3984         element_info[i].ignition_delay = 1;
3985     }
3986   }
3987
3988   // correct non-moving belts to start moving left
3989   for (i = 0; i < NUM_BELTS; i++)
3990     if (game.belt_dir[i] == MV_NONE)
3991       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3992
3993 #if USE_NEW_PLAYER_ASSIGNMENTS
3994   // use preferred player also in local single-player mode
3995   if (!network.enabled && !game.team_mode)
3996   {
3997     int new_index_nr = setup.network_player_nr;
3998
3999     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4000     {
4001       for (i = 0; i < MAX_PLAYERS; i++)
4002         stored_player[i].connected_locally = FALSE;
4003
4004       stored_player[new_index_nr].connected_locally = TRUE;
4005     }
4006   }
4007
4008   for (i = 0; i < MAX_PLAYERS; i++)
4009   {
4010     stored_player[i].connected = FALSE;
4011
4012     // in network game mode, the local player might not be the first player
4013     if (stored_player[i].connected_locally)
4014       local_player = &stored_player[i];
4015   }
4016
4017   if (!network.enabled)
4018     local_player->connected = TRUE;
4019
4020   if (tape.playing)
4021   {
4022     for (i = 0; i < MAX_PLAYERS; i++)
4023       stored_player[i].connected = tape.player_participates[i];
4024   }
4025   else if (network.enabled)
4026   {
4027     // add team mode players connected over the network (needed for correct
4028     // assignment of player figures from level to locally playing players)
4029
4030     for (i = 0; i < MAX_PLAYERS; i++)
4031       if (stored_player[i].connected_network)
4032         stored_player[i].connected = TRUE;
4033   }
4034   else if (game.team_mode)
4035   {
4036     // try to guess locally connected team mode players (needed for correct
4037     // assignment of player figures from level to locally playing players)
4038
4039     for (i = 0; i < MAX_PLAYERS; i++)
4040       if (setup.input[i].use_joystick ||
4041           setup.input[i].key.left != KSYM_UNDEFINED)
4042         stored_player[i].connected = TRUE;
4043   }
4044
4045 #if DEBUG_INIT_PLAYER
4046   DebugPrintPlayerStatus("Player status after level initialization");
4047 #endif
4048
4049 #if DEBUG_INIT_PLAYER
4050   Debug("game:init:player", "Reassigning players ...");
4051 #endif
4052
4053   // check if any connected player was not found in playfield
4054   for (i = 0; i < MAX_PLAYERS; i++)
4055   {
4056     struct PlayerInfo *player = &stored_player[i];
4057
4058     if (player->connected && !player->present)
4059     {
4060       struct PlayerInfo *field_player = NULL;
4061
4062 #if DEBUG_INIT_PLAYER
4063       Debug("game:init:player",
4064             "- looking for field player for player %d ...", i + 1);
4065 #endif
4066
4067       // assign first free player found that is present in the playfield
4068
4069       // first try: look for unmapped playfield player that is not connected
4070       for (j = 0; j < MAX_PLAYERS; j++)
4071         if (field_player == NULL &&
4072             stored_player[j].present &&
4073             !stored_player[j].mapped &&
4074             !stored_player[j].connected)
4075           field_player = &stored_player[j];
4076
4077       // second try: look for *any* unmapped playfield player
4078       for (j = 0; j < MAX_PLAYERS; j++)
4079         if (field_player == NULL &&
4080             stored_player[j].present &&
4081             !stored_player[j].mapped)
4082           field_player = &stored_player[j];
4083
4084       if (field_player != NULL)
4085       {
4086         int jx = field_player->jx, jy = field_player->jy;
4087
4088 #if DEBUG_INIT_PLAYER
4089         Debug("game:init:player", "- found player %d",
4090               field_player->index_nr + 1);
4091 #endif
4092
4093         player->present = FALSE;
4094         player->active = FALSE;
4095
4096         field_player->present = TRUE;
4097         field_player->active = TRUE;
4098
4099         /*
4100         player->initial_element = field_player->initial_element;
4101         player->artwork_element = field_player->artwork_element;
4102
4103         player->block_last_field       = field_player->block_last_field;
4104         player->block_delay_adjustment = field_player->block_delay_adjustment;
4105         */
4106
4107         StorePlayer[jx][jy] = field_player->element_nr;
4108
4109         field_player->jx = field_player->last_jx = jx;
4110         field_player->jy = field_player->last_jy = jy;
4111
4112         if (local_player == player)
4113           local_player = field_player;
4114
4115         map_player_action[field_player->index_nr] = i;
4116
4117         field_player->mapped = TRUE;
4118
4119 #if DEBUG_INIT_PLAYER
4120         Debug("game:init:player", "- map_player_action[%d] == %d",
4121               field_player->index_nr + 1, i + 1);
4122 #endif
4123       }
4124     }
4125
4126     if (player->connected && player->present)
4127       player->mapped = TRUE;
4128   }
4129
4130 #if DEBUG_INIT_PLAYER
4131   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4132 #endif
4133
4134 #else
4135
4136   // check if any connected player was not found in playfield
4137   for (i = 0; i < MAX_PLAYERS; i++)
4138   {
4139     struct PlayerInfo *player = &stored_player[i];
4140
4141     if (player->connected && !player->present)
4142     {
4143       for (j = 0; j < MAX_PLAYERS; j++)
4144       {
4145         struct PlayerInfo *field_player = &stored_player[j];
4146         int jx = field_player->jx, jy = field_player->jy;
4147
4148         // assign first free player found that is present in the playfield
4149         if (field_player->present && !field_player->connected)
4150         {
4151           player->present = TRUE;
4152           player->active = TRUE;
4153
4154           field_player->present = FALSE;
4155           field_player->active = FALSE;
4156
4157           player->initial_element = field_player->initial_element;
4158           player->artwork_element = field_player->artwork_element;
4159
4160           player->block_last_field       = field_player->block_last_field;
4161           player->block_delay_adjustment = field_player->block_delay_adjustment;
4162
4163           StorePlayer[jx][jy] = player->element_nr;
4164
4165           player->jx = player->last_jx = jx;
4166           player->jy = player->last_jy = jy;
4167
4168           break;
4169         }
4170       }
4171     }
4172   }
4173 #endif
4174
4175 #if 0
4176   Debug("game:init:player", "local_player->present == %d",
4177         local_player->present);
4178 #endif
4179
4180   // set focus to local player for network games, else to all players
4181   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4182   game.centered_player_nr_next = game.centered_player_nr;
4183   game.set_centered_player = FALSE;
4184   game.set_centered_player_wrap = FALSE;
4185
4186   if (network_playing && tape.recording)
4187   {
4188     // store client dependent player focus when recording network games
4189     tape.centered_player_nr_next = game.centered_player_nr_next;
4190     tape.set_centered_player = TRUE;
4191   }
4192
4193   if (tape.playing)
4194   {
4195     // when playing a tape, eliminate all players who do not participate
4196
4197 #if USE_NEW_PLAYER_ASSIGNMENTS
4198
4199     if (!game.team_mode)
4200     {
4201       for (i = 0; i < MAX_PLAYERS; i++)
4202       {
4203         if (stored_player[i].active &&
4204             !tape.player_participates[map_player_action[i]])
4205         {
4206           struct PlayerInfo *player = &stored_player[i];
4207           int jx = player->jx, jy = player->jy;
4208
4209 #if DEBUG_INIT_PLAYER
4210           Debug("game:init:player", "Removing player %d at (%d, %d)",
4211                 i + 1, jx, jy);
4212 #endif
4213
4214           player->active = FALSE;
4215           StorePlayer[jx][jy] = 0;
4216           Tile[jx][jy] = EL_EMPTY;
4217         }
4218       }
4219     }
4220
4221 #else
4222
4223     for (i = 0; i < MAX_PLAYERS; i++)
4224     {
4225       if (stored_player[i].active &&
4226           !tape.player_participates[i])
4227       {
4228         struct PlayerInfo *player = &stored_player[i];
4229         int jx = player->jx, jy = player->jy;
4230
4231         player->active = FALSE;
4232         StorePlayer[jx][jy] = 0;
4233         Tile[jx][jy] = EL_EMPTY;
4234       }
4235     }
4236 #endif
4237   }
4238   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4239   {
4240     // when in single player mode, eliminate all but the local player
4241
4242     for (i = 0; i < MAX_PLAYERS; i++)
4243     {
4244       struct PlayerInfo *player = &stored_player[i];
4245
4246       if (player->active && player != local_player)
4247       {
4248         int jx = player->jx, jy = player->jy;
4249
4250         player->active = FALSE;
4251         player->present = FALSE;
4252
4253         StorePlayer[jx][jy] = 0;
4254         Tile[jx][jy] = EL_EMPTY;
4255       }
4256     }
4257   }
4258
4259   for (i = 0; i < MAX_PLAYERS; i++)
4260     if (stored_player[i].active)
4261       game.players_still_needed++;
4262
4263   if (level.solved_by_one_player)
4264     game.players_still_needed = 1;
4265
4266   // when recording the game, store which players take part in the game
4267   if (tape.recording)
4268   {
4269 #if USE_NEW_PLAYER_ASSIGNMENTS
4270     for (i = 0; i < MAX_PLAYERS; i++)
4271       if (stored_player[i].connected)
4272         tape.player_participates[i] = TRUE;
4273 #else
4274     for (i = 0; i < MAX_PLAYERS; i++)
4275       if (stored_player[i].active)
4276         tape.player_participates[i] = TRUE;
4277 #endif
4278   }
4279
4280 #if DEBUG_INIT_PLAYER
4281   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4282 #endif
4283
4284   if (BorderElement == EL_EMPTY)
4285   {
4286     SBX_Left = 0;
4287     SBX_Right = lev_fieldx - SCR_FIELDX;
4288     SBY_Upper = 0;
4289     SBY_Lower = lev_fieldy - SCR_FIELDY;
4290   }
4291   else
4292   {
4293     SBX_Left = -1;
4294     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4295     SBY_Upper = -1;
4296     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4297   }
4298
4299   if (full_lev_fieldx <= SCR_FIELDX)
4300     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4301   if (full_lev_fieldy <= SCR_FIELDY)
4302     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4303
4304   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4305     SBX_Left--;
4306   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4307     SBY_Upper--;
4308
4309   // if local player not found, look for custom element that might create
4310   // the player (make some assumptions about the right custom element)
4311   if (!local_player->present)
4312   {
4313     int start_x = 0, start_y = 0;
4314     int found_rating = 0;
4315     int found_element = EL_UNDEFINED;
4316     int player_nr = local_player->index_nr;
4317
4318     SCAN_PLAYFIELD(x, y)
4319     {
4320       int element = Tile[x][y];
4321       int content;
4322       int xx, yy;
4323       boolean is_player;
4324
4325       if (level.use_start_element[player_nr] &&
4326           level.start_element[player_nr] == element &&
4327           found_rating < 4)
4328       {
4329         start_x = x;
4330         start_y = y;
4331
4332         found_rating = 4;
4333         found_element = element;
4334       }
4335
4336       if (!IS_CUSTOM_ELEMENT(element))
4337         continue;
4338
4339       if (CAN_CHANGE(element))
4340       {
4341         for (i = 0; i < element_info[element].num_change_pages; i++)
4342         {
4343           // check for player created from custom element as single target
4344           content = element_info[element].change_page[i].target_element;
4345           is_player = IS_PLAYER_ELEMENT(content);
4346
4347           if (is_player && (found_rating < 3 ||
4348                             (found_rating == 3 && element < found_element)))
4349           {
4350             start_x = x;
4351             start_y = y;
4352
4353             found_rating = 3;
4354             found_element = element;
4355           }
4356         }
4357       }
4358
4359       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4360       {
4361         // check for player created from custom element as explosion content
4362         content = element_info[element].content.e[xx][yy];
4363         is_player = IS_PLAYER_ELEMENT(content);
4364
4365         if (is_player && (found_rating < 2 ||
4366                           (found_rating == 2 && element < found_element)))
4367         {
4368           start_x = x + xx - 1;
4369           start_y = y + yy - 1;
4370
4371           found_rating = 2;
4372           found_element = element;
4373         }
4374
4375         if (!CAN_CHANGE(element))
4376           continue;
4377
4378         for (i = 0; i < element_info[element].num_change_pages; i++)
4379         {
4380           // check for player created from custom element as extended target
4381           content =
4382             element_info[element].change_page[i].target_content.e[xx][yy];
4383
4384           is_player = IS_PLAYER_ELEMENT(content);
4385
4386           if (is_player && (found_rating < 1 ||
4387                             (found_rating == 1 && element < found_element)))
4388           {
4389             start_x = x + xx - 1;
4390             start_y = y + yy - 1;
4391
4392             found_rating = 1;
4393             found_element = element;
4394           }
4395         }
4396       }
4397     }
4398
4399     scroll_x = SCROLL_POSITION_X(start_x);
4400     scroll_y = SCROLL_POSITION_Y(start_y);
4401   }
4402   else
4403   {
4404     scroll_x = SCROLL_POSITION_X(local_player->jx);
4405     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4406   }
4407
4408   // !!! FIX THIS (START) !!!
4409   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4410   {
4411     InitGameEngine_EM();
4412   }
4413   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4414   {
4415     InitGameEngine_SP();
4416   }
4417   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4418   {
4419     InitGameEngine_MM();
4420   }
4421   else
4422   {
4423     DrawLevel(REDRAW_FIELD);
4424     DrawAllPlayers();
4425
4426     // after drawing the level, correct some elements
4427     if (game.timegate_time_left == 0)
4428       CloseAllOpenTimegates();
4429   }
4430
4431   // blit playfield from scroll buffer to normal back buffer for fading in
4432   BlitScreenToBitmap(backbuffer);
4433   // !!! FIX THIS (END) !!!
4434
4435   DrawMaskedBorder(fade_mask);
4436
4437   FadeIn(fade_mask);
4438
4439 #if 1
4440   // full screen redraw is required at this point in the following cases:
4441   // - special editor door undrawn when game was started from level editor
4442   // - drawing area (playfield) was changed and has to be removed completely
4443   redraw_mask = REDRAW_ALL;
4444   BackToFront();
4445 #endif
4446
4447   if (!game.restart_level)
4448   {
4449     // copy default game door content to main double buffer
4450
4451     // !!! CHECK AGAIN !!!
4452     SetPanelBackground();
4453     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4454     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4455   }
4456
4457   SetPanelBackground();
4458   SetDrawBackgroundMask(REDRAW_DOOR_1);
4459
4460   UpdateAndDisplayGameControlValues();
4461
4462   if (!game.restart_level)
4463   {
4464     UnmapGameButtons();
4465     UnmapTapeButtons();
4466
4467     FreeGameButtons();
4468     CreateGameButtons();
4469
4470     MapGameButtons();
4471     MapTapeButtons();
4472
4473     // copy actual game door content to door double buffer for OpenDoor()
4474     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4475
4476     OpenDoor(DOOR_OPEN_ALL);
4477
4478     KeyboardAutoRepeatOffUnlessAutoplay();
4479
4480 #if DEBUG_INIT_PLAYER
4481     DebugPrintPlayerStatus("Player status (final)");
4482 #endif
4483   }
4484
4485   UnmapAllGadgets();
4486
4487   MapGameButtons();
4488   MapTapeButtons();
4489
4490   if (!game.restart_level && !tape.playing)
4491   {
4492     LevelStats_incPlayed(level_nr);
4493
4494     SaveLevelSetup_SeriesInfo();
4495   }
4496
4497   game.restart_level = FALSE;
4498   game.restart_game_message = NULL;
4499
4500   game.request_active = FALSE;
4501   game.request_active_or_moving = FALSE;
4502
4503   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4504     InitGameActions_MM();
4505
4506   SaveEngineSnapshotToListInitial();
4507
4508   if (!game.restart_level)
4509   {
4510     PlaySound(SND_GAME_STARTING);
4511
4512     if (setup.sound_music)
4513       PlayLevelMusic();
4514   }
4515
4516   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4517 }
4518
4519 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4520                         int actual_player_x, int actual_player_y)
4521 {
4522   // this is used for non-R'n'D game engines to update certain engine values
4523
4524   // needed to determine if sounds are played within the visible screen area
4525   scroll_x = actual_scroll_x;
4526   scroll_y = actual_scroll_y;
4527
4528   // needed to get player position for "follow finger" playing input method
4529   local_player->jx = actual_player_x;
4530   local_player->jy = actual_player_y;
4531 }
4532
4533 void InitMovDir(int x, int y)
4534 {
4535   int i, element = Tile[x][y];
4536   static int xy[4][2] =
4537   {
4538     {  0, +1 },
4539     { +1,  0 },
4540     {  0, -1 },
4541     { -1,  0 }
4542   };
4543   static int direction[3][4] =
4544   {
4545     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4546     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4547     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4548   };
4549
4550   switch (element)
4551   {
4552     case EL_BUG_RIGHT:
4553     case EL_BUG_UP:
4554     case EL_BUG_LEFT:
4555     case EL_BUG_DOWN:
4556       Tile[x][y] = EL_BUG;
4557       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4558       break;
4559
4560     case EL_SPACESHIP_RIGHT:
4561     case EL_SPACESHIP_UP:
4562     case EL_SPACESHIP_LEFT:
4563     case EL_SPACESHIP_DOWN:
4564       Tile[x][y] = EL_SPACESHIP;
4565       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4566       break;
4567
4568     case EL_BD_BUTTERFLY_RIGHT:
4569     case EL_BD_BUTTERFLY_UP:
4570     case EL_BD_BUTTERFLY_LEFT:
4571     case EL_BD_BUTTERFLY_DOWN:
4572       Tile[x][y] = EL_BD_BUTTERFLY;
4573       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4574       break;
4575
4576     case EL_BD_FIREFLY_RIGHT:
4577     case EL_BD_FIREFLY_UP:
4578     case EL_BD_FIREFLY_LEFT:
4579     case EL_BD_FIREFLY_DOWN:
4580       Tile[x][y] = EL_BD_FIREFLY;
4581       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4582       break;
4583
4584     case EL_PACMAN_RIGHT:
4585     case EL_PACMAN_UP:
4586     case EL_PACMAN_LEFT:
4587     case EL_PACMAN_DOWN:
4588       Tile[x][y] = EL_PACMAN;
4589       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4590       break;
4591
4592     case EL_YAMYAM_LEFT:
4593     case EL_YAMYAM_RIGHT:
4594     case EL_YAMYAM_UP:
4595     case EL_YAMYAM_DOWN:
4596       Tile[x][y] = EL_YAMYAM;
4597       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4598       break;
4599
4600     case EL_SP_SNIKSNAK:
4601       MovDir[x][y] = MV_UP;
4602       break;
4603
4604     case EL_SP_ELECTRON:
4605       MovDir[x][y] = MV_LEFT;
4606       break;
4607
4608     case EL_MOLE_LEFT:
4609     case EL_MOLE_RIGHT:
4610     case EL_MOLE_UP:
4611     case EL_MOLE_DOWN:
4612       Tile[x][y] = EL_MOLE;
4613       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4614       break;
4615
4616     case EL_SPRING_LEFT:
4617     case EL_SPRING_RIGHT:
4618       Tile[x][y] = EL_SPRING;
4619       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4620       break;
4621
4622     default:
4623       if (IS_CUSTOM_ELEMENT(element))
4624       {
4625         struct ElementInfo *ei = &element_info[element];
4626         int move_direction_initial = ei->move_direction_initial;
4627         int move_pattern = ei->move_pattern;
4628
4629         if (move_direction_initial == MV_START_PREVIOUS)
4630         {
4631           if (MovDir[x][y] != MV_NONE)
4632             return;
4633
4634           move_direction_initial = MV_START_AUTOMATIC;
4635         }
4636
4637         if (move_direction_initial == MV_START_RANDOM)
4638           MovDir[x][y] = 1 << RND(4);
4639         else if (move_direction_initial & MV_ANY_DIRECTION)
4640           MovDir[x][y] = move_direction_initial;
4641         else if (move_pattern == MV_ALL_DIRECTIONS ||
4642                  move_pattern == MV_TURNING_LEFT ||
4643                  move_pattern == MV_TURNING_RIGHT ||
4644                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4645                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4646                  move_pattern == MV_TURNING_RANDOM)
4647           MovDir[x][y] = 1 << RND(4);
4648         else if (move_pattern == MV_HORIZONTAL)
4649           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4650         else if (move_pattern == MV_VERTICAL)
4651           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4652         else if (move_pattern & MV_ANY_DIRECTION)
4653           MovDir[x][y] = element_info[element].move_pattern;
4654         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4655                  move_pattern == MV_ALONG_RIGHT_SIDE)
4656         {
4657           // use random direction as default start direction
4658           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4659             MovDir[x][y] = 1 << RND(4);
4660
4661           for (i = 0; i < NUM_DIRECTIONS; i++)
4662           {
4663             int x1 = x + xy[i][0];
4664             int y1 = y + xy[i][1];
4665
4666             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4667             {
4668               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4669                 MovDir[x][y] = direction[0][i];
4670               else
4671                 MovDir[x][y] = direction[1][i];
4672
4673               break;
4674             }
4675           }
4676         }                
4677       }
4678       else
4679       {
4680         MovDir[x][y] = 1 << RND(4);
4681
4682         if (element != EL_BUG &&
4683             element != EL_SPACESHIP &&
4684             element != EL_BD_BUTTERFLY &&
4685             element != EL_BD_FIREFLY)
4686           break;
4687
4688         for (i = 0; i < NUM_DIRECTIONS; i++)
4689         {
4690           int x1 = x + xy[i][0];
4691           int y1 = y + xy[i][1];
4692
4693           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4694           {
4695             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4696             {
4697               MovDir[x][y] = direction[0][i];
4698               break;
4699             }
4700             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4701                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4702             {
4703               MovDir[x][y] = direction[1][i];
4704               break;
4705             }
4706           }
4707         }
4708       }
4709       break;
4710   }
4711
4712   GfxDir[x][y] = MovDir[x][y];
4713 }
4714
4715 void InitAmoebaNr(int x, int y)
4716 {
4717   int i;
4718   int group_nr = AmoebaNeighbourNr(x, y);
4719
4720   if (group_nr == 0)
4721   {
4722     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4723     {
4724       if (AmoebaCnt[i] == 0)
4725       {
4726         group_nr = i;
4727         break;
4728       }
4729     }
4730   }
4731
4732   AmoebaNr[x][y] = group_nr;
4733   AmoebaCnt[group_nr]++;
4734   AmoebaCnt2[group_nr]++;
4735 }
4736
4737 static void LevelSolved_SetFinalGameValues(void)
4738 {
4739   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4740   game.score_time_final = (level.use_step_counter ? TimePlayed :
4741                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4742
4743   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4744                       game_em.lev->score :
4745                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4746                       game_mm.score :
4747                       game.score);
4748
4749   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4750                        MM_HEALTH(game_mm.laser_overload_value) :
4751                        game.health);
4752
4753   game.LevelSolved_CountingTime = game.time_final;
4754   game.LevelSolved_CountingScore = game.score_final;
4755   game.LevelSolved_CountingHealth = game.health_final;
4756 }
4757
4758 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4759 {
4760   game.LevelSolved_CountingTime = time;
4761   game.LevelSolved_CountingScore = score;
4762   game.LevelSolved_CountingHealth = health;
4763
4764   game_panel_controls[GAME_PANEL_TIME].value = time;
4765   game_panel_controls[GAME_PANEL_SCORE].value = score;
4766   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4767
4768   DisplayGameControlValues();
4769 }
4770
4771 static void LevelSolved(void)
4772 {
4773   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4774       game.players_still_needed > 0)
4775     return;
4776
4777   game.LevelSolved = TRUE;
4778   game.GameOver = TRUE;
4779
4780   tape.solved = TRUE;
4781
4782   // needed here to display correct panel values while player walks into exit
4783   LevelSolved_SetFinalGameValues();
4784 }
4785
4786 void GameWon(void)
4787 {
4788   static int time_count_steps;
4789   static int time, time_final;
4790   static float score, score_final; // needed for time score < 10 for 10 seconds
4791   static int health, health_final;
4792   static int game_over_delay_1 = 0;
4793   static int game_over_delay_2 = 0;
4794   static int game_over_delay_3 = 0;
4795   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4796   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4797
4798   if (!game.LevelSolved_GameWon)
4799   {
4800     int i;
4801
4802     // do not start end game actions before the player stops moving (to exit)
4803     if (local_player->active && local_player->MovPos)
4804       return;
4805
4806     // calculate final game values after player finished walking into exit
4807     LevelSolved_SetFinalGameValues();
4808
4809     game.LevelSolved_GameWon = TRUE;
4810     game.LevelSolved_SaveTape = tape.recording;
4811     game.LevelSolved_SaveScore = !tape.playing;
4812
4813     if (!tape.playing)
4814     {
4815       LevelStats_incSolved(level_nr);
4816
4817       SaveLevelSetup_SeriesInfo();
4818     }
4819
4820     if (tape.auto_play)         // tape might already be stopped here
4821       tape.auto_play_level_solved = TRUE;
4822
4823     TapeStop();
4824
4825     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4826     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4827     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4828
4829     time = time_final = game.time_final;
4830     score = score_final = game.score_final;
4831     health = health_final = game.health_final;
4832
4833     // update game panel values before (delayed) counting of score (if any)
4834     LevelSolved_DisplayFinalGameValues(time, score, health);
4835
4836     // if level has time score defined, calculate new final game values
4837     if (time_score > 0)
4838     {
4839       int time_final_max = 999;
4840       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4841       int time_frames = 0;
4842       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4843       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4844
4845       if (TimeLeft > 0)
4846       {
4847         time_final = 0;
4848         time_frames = time_frames_left;
4849       }
4850       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4851       {
4852         time_final = time_final_max;
4853         time_frames = time_frames_final_max - time_frames_played;
4854       }
4855
4856       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4857
4858       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4859
4860       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4861       {
4862         health_final = 0;
4863         score_final += health * time_score;
4864       }
4865
4866       game.score_final = score_final;
4867       game.health_final = health_final;
4868     }
4869
4870     // if not counting score after game, immediately update game panel values
4871     if (level_editor_test_game || !setup.count_score_after_game)
4872     {
4873       time = time_final;
4874       score = score_final;
4875
4876       LevelSolved_DisplayFinalGameValues(time, score, health);
4877     }
4878
4879     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4880     {
4881       // check if last player has left the level
4882       if (game.exit_x >= 0 &&
4883           game.exit_y >= 0)
4884       {
4885         int x = game.exit_x;
4886         int y = game.exit_y;
4887         int element = Tile[x][y];
4888
4889         // close exit door after last player
4890         if ((game.all_players_gone &&
4891              (element == EL_EXIT_OPEN ||
4892               element == EL_SP_EXIT_OPEN ||
4893               element == EL_STEEL_EXIT_OPEN)) ||
4894             element == EL_EM_EXIT_OPEN ||
4895             element == EL_EM_STEEL_EXIT_OPEN)
4896         {
4897
4898           Tile[x][y] =
4899             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4900              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4901              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4902              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4903              EL_EM_STEEL_EXIT_CLOSING);
4904
4905           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4906         }
4907
4908         // player disappears
4909         DrawLevelField(x, y);
4910       }
4911
4912       for (i = 0; i < MAX_PLAYERS; i++)
4913       {
4914         struct PlayerInfo *player = &stored_player[i];
4915
4916         if (player->present)
4917         {
4918           RemovePlayer(player);
4919
4920           // player disappears
4921           DrawLevelField(player->jx, player->jy);
4922         }
4923       }
4924     }
4925
4926     PlaySound(SND_GAME_WINNING);
4927   }
4928
4929   if (setup.count_score_after_game)
4930   {
4931     if (time != time_final)
4932     {
4933       if (game_over_delay_1 > 0)
4934       {
4935         game_over_delay_1--;
4936
4937         return;
4938       }
4939
4940       int time_to_go = ABS(time_final - time);
4941       int time_count_dir = (time < time_final ? +1 : -1);
4942
4943       if (time_to_go < time_count_steps)
4944         time_count_steps = 1;
4945
4946       time  += time_count_steps * time_count_dir;
4947       score += time_count_steps * time_score;
4948
4949       // set final score to correct rounding differences after counting score
4950       if (time == time_final)
4951         score = score_final;
4952
4953       LevelSolved_DisplayFinalGameValues(time, score, health);
4954
4955       if (time == time_final)
4956         StopSound(SND_GAME_LEVELTIME_BONUS);
4957       else if (setup.sound_loops)
4958         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4959       else
4960         PlaySound(SND_GAME_LEVELTIME_BONUS);
4961
4962       return;
4963     }
4964
4965     if (health != health_final)
4966     {
4967       if (game_over_delay_2 > 0)
4968       {
4969         game_over_delay_2--;
4970
4971         return;
4972       }
4973
4974       int health_count_dir = (health < health_final ? +1 : -1);
4975
4976       health += health_count_dir;
4977       score  += time_score;
4978
4979       LevelSolved_DisplayFinalGameValues(time, score, health);
4980
4981       if (health == health_final)
4982         StopSound(SND_GAME_LEVELTIME_BONUS);
4983       else if (setup.sound_loops)
4984         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4985       else
4986         PlaySound(SND_GAME_LEVELTIME_BONUS);
4987
4988       return;
4989     }
4990   }
4991
4992   game.panel.active = FALSE;
4993
4994   if (game_over_delay_3 > 0)
4995   {
4996     game_over_delay_3--;
4997
4998     return;
4999   }
5000
5001   GameEnd();
5002 }
5003
5004 void GameEnd(void)
5005 {
5006   // used instead of "level_nr" (needed for network games)
5007   int last_level_nr = levelset.level_nr;
5008   boolean tape_saved = FALSE;
5009
5010   game.LevelSolved_GameEnd = TRUE;
5011
5012   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5013   {
5014     // make sure that request dialog to save tape does not open door again
5015     if (!global.use_envelope_request)
5016       CloseDoor(DOOR_CLOSE_1);
5017
5018     // ask to save tape
5019     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5020
5021     // set unique basename for score tape (also saved in high score table)
5022     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5023   }
5024
5025   // if no tape is to be saved, close both doors simultaneously
5026   CloseDoor(DOOR_CLOSE_ALL);
5027
5028   if (level_editor_test_game || score_info_tape_play)
5029   {
5030     SetGameStatus(GAME_MODE_MAIN);
5031
5032     DrawMainMenu();
5033
5034     return;
5035   }
5036
5037   if (!game.LevelSolved_SaveScore)
5038   {
5039     SetGameStatus(GAME_MODE_MAIN);
5040
5041     DrawMainMenu();
5042
5043     return;
5044   }
5045
5046   if (level_nr == leveldir_current->handicap_level)
5047   {
5048     leveldir_current->handicap_level++;
5049
5050     SaveLevelSetup_SeriesInfo();
5051   }
5052
5053   // save score and score tape before potentially erasing tape below
5054   NewHighScore(last_level_nr, tape_saved);
5055
5056   if (setup.increment_levels &&
5057       level_nr < leveldir_current->last_level &&
5058       !network_playing)
5059   {
5060     level_nr++;         // advance to next level
5061     TapeErase();        // start with empty tape
5062
5063     if (setup.auto_play_next_level)
5064     {
5065       scores.continue_playing = TRUE;
5066       scores.next_level_nr = level_nr;
5067
5068       LoadLevel(level_nr);
5069
5070       SaveLevelSetup_SeriesInfo();
5071     }
5072   }
5073
5074   if (scores.last_added >= 0 && setup.show_scores_after_game)
5075   {
5076     SetGameStatus(GAME_MODE_SCORES);
5077
5078     DrawHallOfFame(last_level_nr);
5079   }
5080   else if (scores.continue_playing)
5081   {
5082     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5083   }
5084   else
5085   {
5086     SetGameStatus(GAME_MODE_MAIN);
5087
5088     DrawMainMenu();
5089   }
5090 }
5091
5092 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5093                          boolean one_score_entry_per_name)
5094 {
5095   int i;
5096
5097   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5098     return -1;
5099
5100   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5101   {
5102     struct ScoreEntry *entry = &list->entry[i];
5103     boolean score_is_better = (new_entry->score >  entry->score);
5104     boolean score_is_equal  = (new_entry->score == entry->score);
5105     boolean time_is_better  = (new_entry->time  <  entry->time);
5106     boolean time_is_equal   = (new_entry->time  == entry->time);
5107     boolean better_by_score = (score_is_better ||
5108                                (score_is_equal && time_is_better));
5109     boolean better_by_time  = (time_is_better ||
5110                                (time_is_equal && score_is_better));
5111     boolean is_better = (level.rate_time_over_score ? better_by_time :
5112                          better_by_score);
5113     boolean entry_is_empty = (entry->score == 0 &&
5114                               entry->time == 0);
5115
5116     // prevent adding server score entries if also existing in local score file
5117     // (special case: historic score entries have an empty tape basename entry)
5118     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5119         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5120     {
5121       // add fields from server score entry not stored in local score entry
5122       // (currently, this means setting platform, version and country fields;
5123       // in rare cases, this may also correct an invalid score value, as
5124       // historic scores might have been truncated to 16-bit values locally)
5125       *entry = *new_entry;
5126
5127       return -1;
5128     }
5129
5130     if (is_better || entry_is_empty)
5131     {
5132       // player has made it to the hall of fame
5133
5134       if (i < MAX_SCORE_ENTRIES - 1)
5135       {
5136         int m = MAX_SCORE_ENTRIES - 1;
5137         int l;
5138
5139         if (one_score_entry_per_name)
5140         {
5141           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5142             if (strEqual(list->entry[l].name, new_entry->name))
5143               m = l;
5144
5145           if (m == i)   // player's new highscore overwrites his old one
5146             goto put_into_list;
5147         }
5148
5149         for (l = m; l > i; l--)
5150           list->entry[l] = list->entry[l - 1];
5151       }
5152
5153       put_into_list:
5154
5155       *entry = *new_entry;
5156
5157       return i;
5158     }
5159     else if (one_score_entry_per_name &&
5160              strEqual(entry->name, new_entry->name))
5161     {
5162       // player already in high score list with better score or time
5163
5164       return -1;
5165     }
5166   }
5167
5168   // special case: new score is beyond the last high score list position
5169   return MAX_SCORE_ENTRIES;
5170 }
5171
5172 void NewHighScore(int level_nr, boolean tape_saved)
5173 {
5174   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5175   boolean one_per_name = FALSE;
5176
5177   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5178   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5179
5180   new_entry.score = game.score_final;
5181   new_entry.time = game.score_time_final;
5182
5183   LoadScore(level_nr);
5184
5185   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5186
5187   if (scores.last_added >= MAX_SCORE_ENTRIES)
5188   {
5189     scores.last_added = MAX_SCORE_ENTRIES - 1;
5190     scores.force_last_added = TRUE;
5191
5192     scores.entry[scores.last_added] = new_entry;
5193
5194     // store last added local score entry (before merging server scores)
5195     scores.last_added_local = scores.last_added;
5196
5197     return;
5198   }
5199
5200   if (scores.last_added < 0)
5201     return;
5202
5203   SaveScore(level_nr);
5204
5205   // store last added local score entry (before merging server scores)
5206   scores.last_added_local = scores.last_added;
5207
5208   if (!game.LevelSolved_SaveTape)
5209     return;
5210
5211   SaveScoreTape(level_nr);
5212
5213   if (setup.ask_for_using_api_server)
5214   {
5215     setup.use_api_server =
5216       Request("Upload your score and tape to the high score server?", REQ_ASK);
5217
5218     if (!setup.use_api_server)
5219       Request("Not using high score server! Use setup menu to enable again!",
5220               REQ_CONFIRM);
5221
5222     runtime.use_api_server = setup.use_api_server;
5223
5224     // after asking for using API server once, do not ask again
5225     setup.ask_for_using_api_server = FALSE;
5226
5227     SaveSetup_ServerSetup();
5228   }
5229
5230   SaveServerScore(level_nr, tape_saved);
5231 }
5232
5233 void MergeServerScore(void)
5234 {
5235   struct ScoreEntry last_added_entry;
5236   boolean one_per_name = FALSE;
5237   int i;
5238
5239   if (scores.last_added >= 0)
5240     last_added_entry = scores.entry[scores.last_added];
5241
5242   for (i = 0; i < server_scores.num_entries; i++)
5243   {
5244     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5245
5246     if (pos >= 0 && pos <= scores.last_added)
5247       scores.last_added++;
5248   }
5249
5250   if (scores.last_added >= MAX_SCORE_ENTRIES)
5251   {
5252     scores.last_added = MAX_SCORE_ENTRIES - 1;
5253     scores.force_last_added = TRUE;
5254
5255     scores.entry[scores.last_added] = last_added_entry;
5256   }
5257 }
5258
5259 static int getElementMoveStepsizeExt(int x, int y, int direction)
5260 {
5261   int element = Tile[x][y];
5262   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5263   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5264   int horiz_move = (dx != 0);
5265   int sign = (horiz_move ? dx : dy);
5266   int step = sign * element_info[element].move_stepsize;
5267
5268   // special values for move stepsize for spring and things on conveyor belt
5269   if (horiz_move)
5270   {
5271     if (CAN_FALL(element) &&
5272         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5273       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5274     else if (element == EL_SPRING)
5275       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5276   }
5277
5278   return step;
5279 }
5280
5281 static int getElementMoveStepsize(int x, int y)
5282 {
5283   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5284 }
5285
5286 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5287 {
5288   if (player->GfxAction != action || player->GfxDir != dir)
5289   {
5290     player->GfxAction = action;
5291     player->GfxDir = dir;
5292     player->Frame = 0;
5293     player->StepFrame = 0;
5294   }
5295 }
5296
5297 static void ResetGfxFrame(int x, int y)
5298 {
5299   // profiling showed that "autotest" spends 10~20% of its time in this function
5300   if (DrawingDeactivatedField())
5301     return;
5302
5303   int element = Tile[x][y];
5304   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5305
5306   if (graphic_info[graphic].anim_global_sync)
5307     GfxFrame[x][y] = FrameCounter;
5308   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5309     GfxFrame[x][y] = CustomValue[x][y];
5310   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5311     GfxFrame[x][y] = element_info[element].collect_score;
5312   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5313     GfxFrame[x][y] = ChangeDelay[x][y];
5314 }
5315
5316 static void ResetGfxAnimation(int x, int y)
5317 {
5318   GfxAction[x][y] = ACTION_DEFAULT;
5319   GfxDir[x][y] = MovDir[x][y];
5320   GfxFrame[x][y] = 0;
5321
5322   ResetGfxFrame(x, y);
5323 }
5324
5325 static void ResetRandomAnimationValue(int x, int y)
5326 {
5327   GfxRandom[x][y] = INIT_GFX_RANDOM();
5328 }
5329
5330 static void InitMovingField(int x, int y, int direction)
5331 {
5332   int element = Tile[x][y];
5333   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5334   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5335   int newx = x + dx;
5336   int newy = y + dy;
5337   boolean is_moving_before, is_moving_after;
5338
5339   // check if element was/is moving or being moved before/after mode change
5340   is_moving_before = (WasJustMoving[x][y] != 0);
5341   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5342
5343   // reset animation only for moving elements which change direction of moving
5344   // or which just started or stopped moving
5345   // (else CEs with property "can move" / "not moving" are reset each frame)
5346   if (is_moving_before != is_moving_after ||
5347       direction != MovDir[x][y])
5348     ResetGfxAnimation(x, y);
5349
5350   MovDir[x][y] = direction;
5351   GfxDir[x][y] = direction;
5352
5353   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5354                      direction == MV_DOWN && CAN_FALL(element) ?
5355                      ACTION_FALLING : ACTION_MOVING);
5356
5357   // this is needed for CEs with property "can move" / "not moving"
5358
5359   if (is_moving_after)
5360   {
5361     if (Tile[newx][newy] == EL_EMPTY)
5362       Tile[newx][newy] = EL_BLOCKED;
5363
5364     MovDir[newx][newy] = MovDir[x][y];
5365
5366     CustomValue[newx][newy] = CustomValue[x][y];
5367
5368     GfxFrame[newx][newy] = GfxFrame[x][y];
5369     GfxRandom[newx][newy] = GfxRandom[x][y];
5370     GfxAction[newx][newy] = GfxAction[x][y];
5371     GfxDir[newx][newy] = GfxDir[x][y];
5372   }
5373 }
5374
5375 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5376 {
5377   int direction = MovDir[x][y];
5378   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5379   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5380
5381   *goes_to_x = newx;
5382   *goes_to_y = newy;
5383 }
5384
5385 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5386 {
5387   int oldx = x, oldy = y;
5388   int direction = MovDir[x][y];
5389
5390   if (direction == MV_LEFT)
5391     oldx++;
5392   else if (direction == MV_RIGHT)
5393     oldx--;
5394   else if (direction == MV_UP)
5395     oldy++;
5396   else if (direction == MV_DOWN)
5397     oldy--;
5398
5399   *comes_from_x = oldx;
5400   *comes_from_y = oldy;
5401 }
5402
5403 static int MovingOrBlocked2Element(int x, int y)
5404 {
5405   int element = Tile[x][y];
5406
5407   if (element == EL_BLOCKED)
5408   {
5409     int oldx, oldy;
5410
5411     Blocked2Moving(x, y, &oldx, &oldy);
5412     return Tile[oldx][oldy];
5413   }
5414   else
5415     return element;
5416 }
5417
5418 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5419 {
5420   // like MovingOrBlocked2Element(), but if element is moving
5421   // and (x,y) is the field the moving element is just leaving,
5422   // return EL_BLOCKED instead of the element value
5423   int element = Tile[x][y];
5424
5425   if (IS_MOVING(x, y))
5426   {
5427     if (element == EL_BLOCKED)
5428     {
5429       int oldx, oldy;
5430
5431       Blocked2Moving(x, y, &oldx, &oldy);
5432       return Tile[oldx][oldy];
5433     }
5434     else
5435       return EL_BLOCKED;
5436   }
5437   else
5438     return element;
5439 }
5440
5441 static void RemoveField(int x, int y)
5442 {
5443   Tile[x][y] = EL_EMPTY;
5444
5445   MovPos[x][y] = 0;
5446   MovDir[x][y] = 0;
5447   MovDelay[x][y] = 0;
5448
5449   CustomValue[x][y] = 0;
5450
5451   AmoebaNr[x][y] = 0;
5452   ChangeDelay[x][y] = 0;
5453   ChangePage[x][y] = -1;
5454   Pushed[x][y] = FALSE;
5455
5456   GfxElement[x][y] = EL_UNDEFINED;
5457   GfxAction[x][y] = ACTION_DEFAULT;
5458   GfxDir[x][y] = MV_NONE;
5459 }
5460
5461 static void RemoveMovingField(int x, int y)
5462 {
5463   int oldx = x, oldy = y, newx = x, newy = y;
5464   int element = Tile[x][y];
5465   int next_element = EL_UNDEFINED;
5466
5467   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5468     return;
5469
5470   if (IS_MOVING(x, y))
5471   {
5472     Moving2Blocked(x, y, &newx, &newy);
5473
5474     if (Tile[newx][newy] != EL_BLOCKED)
5475     {
5476       // element is moving, but target field is not free (blocked), but
5477       // already occupied by something different (example: acid pool);
5478       // in this case, only remove the moving field, but not the target
5479
5480       RemoveField(oldx, oldy);
5481
5482       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5483
5484       TEST_DrawLevelField(oldx, oldy);
5485
5486       return;
5487     }
5488   }
5489   else if (element == EL_BLOCKED)
5490   {
5491     Blocked2Moving(x, y, &oldx, &oldy);
5492     if (!IS_MOVING(oldx, oldy))
5493       return;
5494   }
5495
5496   if (element == EL_BLOCKED &&
5497       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5498        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5499        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5500        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5501        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5502        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5503     next_element = get_next_element(Tile[oldx][oldy]);
5504
5505   RemoveField(oldx, oldy);
5506   RemoveField(newx, newy);
5507
5508   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5509
5510   if (next_element != EL_UNDEFINED)
5511     Tile[oldx][oldy] = next_element;
5512
5513   TEST_DrawLevelField(oldx, oldy);
5514   TEST_DrawLevelField(newx, newy);
5515 }
5516
5517 void DrawDynamite(int x, int y)
5518 {
5519   int sx = SCREENX(x), sy = SCREENY(y);
5520   int graphic = el2img(Tile[x][y]);
5521   int frame;
5522
5523   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5524     return;
5525
5526   if (IS_WALKABLE_INSIDE(Back[x][y]))
5527     return;
5528
5529   if (Back[x][y])
5530     DrawLevelElement(x, y, Back[x][y]);
5531   else if (Store[x][y])
5532     DrawLevelElement(x, y, Store[x][y]);
5533   else if (game.use_masked_elements)
5534     DrawLevelElement(x, y, EL_EMPTY);
5535
5536   frame = getGraphicAnimationFrameXY(graphic, x, y);
5537
5538   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5539     DrawGraphicThruMask(sx, sy, graphic, frame);
5540   else
5541     DrawGraphic(sx, sy, graphic, frame);
5542 }
5543
5544 static void CheckDynamite(int x, int y)
5545 {
5546   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5547   {
5548     MovDelay[x][y]--;
5549
5550     if (MovDelay[x][y] != 0)
5551     {
5552       DrawDynamite(x, y);
5553       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5554
5555       return;
5556     }
5557   }
5558
5559   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5560
5561   Bang(x, y);
5562 }
5563
5564 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5565 {
5566   boolean num_checked_players = 0;
5567   int i;
5568
5569   for (i = 0; i < MAX_PLAYERS; i++)
5570   {
5571     if (stored_player[i].active)
5572     {
5573       int sx = stored_player[i].jx;
5574       int sy = stored_player[i].jy;
5575
5576       if (num_checked_players == 0)
5577       {
5578         *sx1 = *sx2 = sx;
5579         *sy1 = *sy2 = sy;
5580       }
5581       else
5582       {
5583         *sx1 = MIN(*sx1, sx);
5584         *sy1 = MIN(*sy1, sy);
5585         *sx2 = MAX(*sx2, sx);
5586         *sy2 = MAX(*sy2, sy);
5587       }
5588
5589       num_checked_players++;
5590     }
5591   }
5592 }
5593
5594 static boolean checkIfAllPlayersFitToScreen_RND(void)
5595 {
5596   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5597
5598   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5599
5600   return (sx2 - sx1 < SCR_FIELDX &&
5601           sy2 - sy1 < SCR_FIELDY);
5602 }
5603
5604 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5605 {
5606   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5607
5608   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5609
5610   *sx = (sx1 + sx2) / 2;
5611   *sy = (sy1 + sy2) / 2;
5612 }
5613
5614 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5615                                boolean center_screen, boolean quick_relocation)
5616 {
5617   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5618   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5619   boolean no_delay = (tape.warp_forward);
5620   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5621   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5622   int new_scroll_x, new_scroll_y;
5623
5624   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5625   {
5626     // case 1: quick relocation inside visible screen (without scrolling)
5627
5628     RedrawPlayfield();
5629
5630     return;
5631   }
5632
5633   if (!level.shifted_relocation || center_screen)
5634   {
5635     // relocation _with_ centering of screen
5636
5637     new_scroll_x = SCROLL_POSITION_X(x);
5638     new_scroll_y = SCROLL_POSITION_Y(y);
5639   }
5640   else
5641   {
5642     // relocation _without_ centering of screen
5643
5644     int center_scroll_x = SCROLL_POSITION_X(old_x);
5645     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5646     int offset_x = x + (scroll_x - center_scroll_x);
5647     int offset_y = y + (scroll_y - center_scroll_y);
5648
5649     // for new screen position, apply previous offset to center position
5650     new_scroll_x = SCROLL_POSITION_X(offset_x);
5651     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5652   }
5653
5654   if (quick_relocation)
5655   {
5656     // case 2: quick relocation (redraw without visible scrolling)
5657
5658     scroll_x = new_scroll_x;
5659     scroll_y = new_scroll_y;
5660
5661     RedrawPlayfield();
5662
5663     return;
5664   }
5665
5666   // case 3: visible relocation (with scrolling to new position)
5667
5668   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5669
5670   SetVideoFrameDelay(wait_delay_value);
5671
5672   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5673   {
5674     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5675     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5676
5677     if (dx == 0 && dy == 0)             // no scrolling needed at all
5678       break;
5679
5680     scroll_x -= dx;
5681     scroll_y -= dy;
5682
5683     // set values for horizontal/vertical screen scrolling (half tile size)
5684     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5685     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5686     int pos_x = dx * TILEX / 2;
5687     int pos_y = dy * TILEY / 2;
5688     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5689     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5690
5691     ScrollLevel(dx, dy);
5692     DrawAllPlayers();
5693
5694     // scroll in two steps of half tile size to make things smoother
5695     BlitScreenToBitmapExt_RND(window, fx, fy);
5696
5697     // scroll second step to align at full tile size
5698     BlitScreenToBitmap(window);
5699   }
5700
5701   DrawAllPlayers();
5702   BackToFront();
5703
5704   SetVideoFrameDelay(frame_delay_value_old);
5705 }
5706
5707 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5708 {
5709   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5710   int player_nr = GET_PLAYER_NR(el_player);
5711   struct PlayerInfo *player = &stored_player[player_nr];
5712   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5713   boolean no_delay = (tape.warp_forward);
5714   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5715   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5716   int old_jx = player->jx;
5717   int old_jy = player->jy;
5718   int old_element = Tile[old_jx][old_jy];
5719   int element = Tile[jx][jy];
5720   boolean player_relocated = (old_jx != jx || old_jy != jy);
5721
5722   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5723   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5724   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5725   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5726   int leave_side_horiz = move_dir_horiz;
5727   int leave_side_vert  = move_dir_vert;
5728   int enter_side = enter_side_horiz | enter_side_vert;
5729   int leave_side = leave_side_horiz | leave_side_vert;
5730
5731   if (player->buried)           // do not reanimate dead player
5732     return;
5733
5734   if (!player_relocated)        // no need to relocate the player
5735     return;
5736
5737   if (IS_PLAYER(jx, jy))        // player already placed at new position
5738   {
5739     RemoveField(jx, jy);        // temporarily remove newly placed player
5740     DrawLevelField(jx, jy);
5741   }
5742
5743   if (player->present)
5744   {
5745     while (player->MovPos)
5746     {
5747       ScrollPlayer(player, SCROLL_GO_ON);
5748       ScrollScreen(NULL, SCROLL_GO_ON);
5749
5750       AdvanceFrameAndPlayerCounters(player->index_nr);
5751
5752       DrawPlayer(player);
5753
5754       BackToFront_WithFrameDelay(wait_delay_value);
5755     }
5756
5757     DrawPlayer(player);         // needed here only to cleanup last field
5758     DrawLevelField(player->jx, player->jy);     // remove player graphic
5759
5760     player->is_moving = FALSE;
5761   }
5762
5763   if (IS_CUSTOM_ELEMENT(old_element))
5764     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5765                                CE_LEFT_BY_PLAYER,
5766                                player->index_bit, leave_side);
5767
5768   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5769                                       CE_PLAYER_LEAVES_X,
5770                                       player->index_bit, leave_side);
5771
5772   Tile[jx][jy] = el_player;
5773   InitPlayerField(jx, jy, el_player, TRUE);
5774
5775   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5776      possible that the relocation target field did not contain a player element,
5777      but a walkable element, to which the new player was relocated -- in this
5778      case, restore that (already initialized!) element on the player field */
5779   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5780   {
5781     Tile[jx][jy] = element;     // restore previously existing element
5782   }
5783
5784   // only visually relocate centered player
5785   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5786                      FALSE, level.instant_relocation);
5787
5788   TestIfPlayerTouchesBadThing(jx, jy);
5789   TestIfPlayerTouchesCustomElement(jx, jy);
5790
5791   if (IS_CUSTOM_ELEMENT(element))
5792     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5793                                player->index_bit, enter_side);
5794
5795   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5796                                       player->index_bit, enter_side);
5797
5798   if (player->is_switching)
5799   {
5800     /* ensure that relocation while still switching an element does not cause
5801        a new element to be treated as also switched directly after relocation
5802        (this is important for teleporter switches that teleport the player to
5803        a place where another teleporter switch is in the same direction, which
5804        would then incorrectly be treated as immediately switched before the
5805        direction key that caused the switch was released) */
5806
5807     player->switch_x += jx - old_jx;
5808     player->switch_y += jy - old_jy;
5809   }
5810 }
5811
5812 static void Explode(int ex, int ey, int phase, int mode)
5813 {
5814   int x, y;
5815   int last_phase;
5816   int border_element;
5817
5818   // !!! eliminate this variable !!!
5819   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5820
5821   if (game.explosions_delayed)
5822   {
5823     ExplodeField[ex][ey] = mode;
5824     return;
5825   }
5826
5827   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5828   {
5829     int center_element = Tile[ex][ey];
5830     int artwork_element, explosion_element;     // set these values later
5831
5832     // remove things displayed in background while burning dynamite
5833     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5834       Back[ex][ey] = 0;
5835
5836     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5837     {
5838       // put moving element to center field (and let it explode there)
5839       center_element = MovingOrBlocked2Element(ex, ey);
5840       RemoveMovingField(ex, ey);
5841       Tile[ex][ey] = center_element;
5842     }
5843
5844     // now "center_element" is finally determined -- set related values now
5845     artwork_element = center_element;           // for custom player artwork
5846     explosion_element = center_element;         // for custom player artwork
5847
5848     if (IS_PLAYER(ex, ey))
5849     {
5850       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5851
5852       artwork_element = stored_player[player_nr].artwork_element;
5853
5854       if (level.use_explosion_element[player_nr])
5855       {
5856         explosion_element = level.explosion_element[player_nr];
5857         artwork_element = explosion_element;
5858       }
5859     }
5860
5861     if (mode == EX_TYPE_NORMAL ||
5862         mode == EX_TYPE_CENTER ||
5863         mode == EX_TYPE_CROSS)
5864       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5865
5866     last_phase = element_info[explosion_element].explosion_delay + 1;
5867
5868     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5869     {
5870       int xx = x - ex + 1;
5871       int yy = y - ey + 1;
5872       int element;
5873
5874       if (!IN_LEV_FIELD(x, y) ||
5875           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5876           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5877         continue;
5878
5879       element = Tile[x][y];
5880
5881       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5882       {
5883         element = MovingOrBlocked2Element(x, y);
5884
5885         if (!IS_EXPLOSION_PROOF(element))
5886           RemoveMovingField(x, y);
5887       }
5888
5889       // indestructible elements can only explode in center (but not flames)
5890       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5891                                            mode == EX_TYPE_BORDER)) ||
5892           element == EL_FLAMES)
5893         continue;
5894
5895       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5896          behaviour, for example when touching a yamyam that explodes to rocks
5897          with active deadly shield, a rock is created under the player !!! */
5898       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5899 #if 0
5900       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5901           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5902            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5903 #else
5904       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5905 #endif
5906       {
5907         if (IS_ACTIVE_BOMB(element))
5908         {
5909           // re-activate things under the bomb like gate or penguin
5910           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5911           Back[x][y] = 0;
5912         }
5913
5914         continue;
5915       }
5916
5917       // save walkable background elements while explosion on same tile
5918       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5919           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5920         Back[x][y] = element;
5921
5922       // ignite explodable elements reached by other explosion
5923       if (element == EL_EXPLOSION)
5924         element = Store2[x][y];
5925
5926       if (AmoebaNr[x][y] &&
5927           (element == EL_AMOEBA_FULL ||
5928            element == EL_BD_AMOEBA ||
5929            element == EL_AMOEBA_GROWING))
5930       {
5931         AmoebaCnt[AmoebaNr[x][y]]--;
5932         AmoebaCnt2[AmoebaNr[x][y]]--;
5933       }
5934
5935       RemoveField(x, y);
5936
5937       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5938       {
5939         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5940
5941         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5942
5943         if (PLAYERINFO(ex, ey)->use_murphy)
5944           Store[x][y] = EL_EMPTY;
5945       }
5946
5947       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5948       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5949       else if (IS_PLAYER_ELEMENT(center_element))
5950         Store[x][y] = EL_EMPTY;
5951       else if (center_element == EL_YAMYAM)
5952         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5953       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5954         Store[x][y] = element_info[center_element].content.e[xx][yy];
5955 #if 1
5956       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5957       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5958       // otherwise) -- FIX THIS !!!
5959       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5960         Store[x][y] = element_info[element].content.e[1][1];
5961 #else
5962       else if (!CAN_EXPLODE(element))
5963         Store[x][y] = element_info[element].content.e[1][1];
5964 #endif
5965       else
5966         Store[x][y] = EL_EMPTY;
5967
5968       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5969           center_element == EL_AMOEBA_TO_DIAMOND)
5970         Store2[x][y] = element;
5971
5972       Tile[x][y] = EL_EXPLOSION;
5973       GfxElement[x][y] = artwork_element;
5974
5975       ExplodePhase[x][y] = 1;
5976       ExplodeDelay[x][y] = last_phase;
5977
5978       Stop[x][y] = TRUE;
5979     }
5980
5981     if (center_element == EL_YAMYAM)
5982       game.yamyam_content_nr =
5983         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5984
5985     return;
5986   }
5987
5988   if (Stop[ex][ey])
5989     return;
5990
5991   x = ex;
5992   y = ey;
5993
5994   if (phase == 1)
5995     GfxFrame[x][y] = 0;         // restart explosion animation
5996
5997   last_phase = ExplodeDelay[x][y];
5998
5999   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6000
6001   // this can happen if the player leaves an explosion just in time
6002   if (GfxElement[x][y] == EL_UNDEFINED)
6003     GfxElement[x][y] = EL_EMPTY;
6004
6005   border_element = Store2[x][y];
6006   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6007     border_element = StorePlayer[x][y];
6008
6009   if (phase == element_info[border_element].ignition_delay ||
6010       phase == last_phase)
6011   {
6012     boolean border_explosion = FALSE;
6013
6014     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6015         !PLAYER_EXPLOSION_PROTECTED(x, y))
6016     {
6017       KillPlayerUnlessExplosionProtected(x, y);
6018       border_explosion = TRUE;
6019     }
6020     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6021     {
6022       Tile[x][y] = Store2[x][y];
6023       Store2[x][y] = 0;
6024       Bang(x, y);
6025       border_explosion = TRUE;
6026     }
6027     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6028     {
6029       AmoebaToDiamond(x, y);
6030       Store2[x][y] = 0;
6031       border_explosion = TRUE;
6032     }
6033
6034     // if an element just explodes due to another explosion (chain-reaction),
6035     // do not immediately end the new explosion when it was the last frame of
6036     // the explosion (as it would be done in the following "if"-statement!)
6037     if (border_explosion && phase == last_phase)
6038       return;
6039   }
6040
6041   // this can happen if the player was just killed by an explosion
6042   if (GfxElement[x][y] == EL_UNDEFINED)
6043     GfxElement[x][y] = EL_EMPTY;
6044
6045   if (phase == last_phase)
6046   {
6047     int element;
6048
6049     element = Tile[x][y] = Store[x][y];
6050     Store[x][y] = Store2[x][y] = 0;
6051     GfxElement[x][y] = EL_UNDEFINED;
6052
6053     // player can escape from explosions and might therefore be still alive
6054     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6055         element <= EL_PLAYER_IS_EXPLODING_4)
6056     {
6057       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6058       int explosion_element = EL_PLAYER_1 + player_nr;
6059       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6060       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6061
6062       if (level.use_explosion_element[player_nr])
6063         explosion_element = level.explosion_element[player_nr];
6064
6065       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6066                     element_info[explosion_element].content.e[xx][yy]);
6067     }
6068
6069     // restore probably existing indestructible background element
6070     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6071       element = Tile[x][y] = Back[x][y];
6072     Back[x][y] = 0;
6073
6074     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6075     GfxDir[x][y] = MV_NONE;
6076     ChangeDelay[x][y] = 0;
6077     ChangePage[x][y] = -1;
6078
6079     CustomValue[x][y] = 0;
6080
6081     InitField_WithBug2(x, y, FALSE);
6082
6083     TEST_DrawLevelField(x, y);
6084
6085     TestIfElementTouchesCustomElement(x, y);
6086
6087     if (GFX_CRUMBLED(element))
6088       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6089
6090     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6091       StorePlayer[x][y] = 0;
6092
6093     if (IS_PLAYER_ELEMENT(element))
6094       RelocatePlayer(x, y, element);
6095   }
6096   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6097   {
6098     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6099     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6100
6101     if (phase == delay)
6102       TEST_DrawLevelFieldCrumbled(x, y);
6103
6104     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6105     {
6106       DrawLevelElement(x, y, Back[x][y]);
6107       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6108     }
6109     else if (IS_WALKABLE_UNDER(Back[x][y]))
6110     {
6111       DrawLevelGraphic(x, y, graphic, frame);
6112       DrawLevelElementThruMask(x, y, Back[x][y]);
6113     }
6114     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6115       DrawLevelGraphic(x, y, graphic, frame);
6116   }
6117 }
6118
6119 static void DynaExplode(int ex, int ey)
6120 {
6121   int i, j;
6122   int dynabomb_element = Tile[ex][ey];
6123   int dynabomb_size = 1;
6124   boolean dynabomb_xl = FALSE;
6125   struct PlayerInfo *player;
6126   struct XY *xy = xy_topdown;
6127
6128   if (IS_ACTIVE_BOMB(dynabomb_element))
6129   {
6130     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6131     dynabomb_size = player->dynabomb_size;
6132     dynabomb_xl = player->dynabomb_xl;
6133     player->dynabombs_left++;
6134   }
6135
6136   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6137
6138   for (i = 0; i < NUM_DIRECTIONS; i++)
6139   {
6140     for (j = 1; j <= dynabomb_size; j++)
6141     {
6142       int x = ex + j * xy[i].x;
6143       int y = ey + j * xy[i].y;
6144       int element;
6145
6146       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6147         break;
6148
6149       element = Tile[x][y];
6150
6151       // do not restart explosions of fields with active bombs
6152       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6153         continue;
6154
6155       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6156
6157       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6158           !IS_DIGGABLE(element) && !dynabomb_xl)
6159         break;
6160     }
6161   }
6162 }
6163
6164 void Bang(int x, int y)
6165 {
6166   int element = MovingOrBlocked2Element(x, y);
6167   int explosion_type = EX_TYPE_NORMAL;
6168
6169   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6170   {
6171     struct PlayerInfo *player = PLAYERINFO(x, y);
6172
6173     element = Tile[x][y] = player->initial_element;
6174
6175     if (level.use_explosion_element[player->index_nr])
6176     {
6177       int explosion_element = level.explosion_element[player->index_nr];
6178
6179       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6180         explosion_type = EX_TYPE_CROSS;
6181       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6182         explosion_type = EX_TYPE_CENTER;
6183     }
6184   }
6185
6186   switch (element)
6187   {
6188     case EL_BUG:
6189     case EL_SPACESHIP:
6190     case EL_BD_BUTTERFLY:
6191     case EL_BD_FIREFLY:
6192     case EL_YAMYAM:
6193     case EL_DARK_YAMYAM:
6194     case EL_ROBOT:
6195     case EL_PACMAN:
6196     case EL_MOLE:
6197       RaiseScoreElement(element);
6198       break;
6199
6200     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6201     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6202     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6203     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6204     case EL_DYNABOMB_INCREASE_NUMBER:
6205     case EL_DYNABOMB_INCREASE_SIZE:
6206     case EL_DYNABOMB_INCREASE_POWER:
6207       explosion_type = EX_TYPE_DYNA;
6208       break;
6209
6210     case EL_DC_LANDMINE:
6211       explosion_type = EX_TYPE_CENTER;
6212       break;
6213
6214     case EL_PENGUIN:
6215     case EL_LAMP:
6216     case EL_LAMP_ACTIVE:
6217     case EL_AMOEBA_TO_DIAMOND:
6218       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6219         explosion_type = EX_TYPE_CENTER;
6220       break;
6221
6222     default:
6223       if (element_info[element].explosion_type == EXPLODES_CROSS)
6224         explosion_type = EX_TYPE_CROSS;
6225       else if (element_info[element].explosion_type == EXPLODES_1X1)
6226         explosion_type = EX_TYPE_CENTER;
6227       break;
6228   }
6229
6230   if (explosion_type == EX_TYPE_DYNA)
6231     DynaExplode(x, y);
6232   else
6233     Explode(x, y, EX_PHASE_START, explosion_type);
6234
6235   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6236 }
6237
6238 static void SplashAcid(int x, int y)
6239 {
6240   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6241       (!IN_LEV_FIELD(x - 1, y - 2) ||
6242        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6243     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6244
6245   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6246       (!IN_LEV_FIELD(x + 1, y - 2) ||
6247        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6248     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6249
6250   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6251 }
6252
6253 static void InitBeltMovement(void)
6254 {
6255   static int belt_base_element[4] =
6256   {
6257     EL_CONVEYOR_BELT_1_LEFT,
6258     EL_CONVEYOR_BELT_2_LEFT,
6259     EL_CONVEYOR_BELT_3_LEFT,
6260     EL_CONVEYOR_BELT_4_LEFT
6261   };
6262   static int belt_base_active_element[4] =
6263   {
6264     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6265     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6266     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6267     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6268   };
6269
6270   int x, y, i, j;
6271
6272   // set frame order for belt animation graphic according to belt direction
6273   for (i = 0; i < NUM_BELTS; i++)
6274   {
6275     int belt_nr = i;
6276
6277     for (j = 0; j < NUM_BELT_PARTS; j++)
6278     {
6279       int element = belt_base_active_element[belt_nr] + j;
6280       int graphic_1 = el2img(element);
6281       int graphic_2 = el2panelimg(element);
6282
6283       if (game.belt_dir[i] == MV_LEFT)
6284       {
6285         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6286         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6287       }
6288       else
6289       {
6290         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6291         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6292       }
6293     }
6294   }
6295
6296   SCAN_PLAYFIELD(x, y)
6297   {
6298     int element = Tile[x][y];
6299
6300     for (i = 0; i < NUM_BELTS; i++)
6301     {
6302       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6303       {
6304         int e_belt_nr = getBeltNrFromBeltElement(element);
6305         int belt_nr = i;
6306
6307         if (e_belt_nr == belt_nr)
6308         {
6309           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6310
6311           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6312         }
6313       }
6314     }
6315   }
6316 }
6317
6318 static void ToggleBeltSwitch(int x, int y)
6319 {
6320   static int belt_base_element[4] =
6321   {
6322     EL_CONVEYOR_BELT_1_LEFT,
6323     EL_CONVEYOR_BELT_2_LEFT,
6324     EL_CONVEYOR_BELT_3_LEFT,
6325     EL_CONVEYOR_BELT_4_LEFT
6326   };
6327   static int belt_base_active_element[4] =
6328   {
6329     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6330     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6331     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6332     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6333   };
6334   static int belt_base_switch_element[4] =
6335   {
6336     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6337     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6338     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6339     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6340   };
6341   static int belt_move_dir[4] =
6342   {
6343     MV_LEFT,
6344     MV_NONE,
6345     MV_RIGHT,
6346     MV_NONE,
6347   };
6348
6349   int element = Tile[x][y];
6350   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6351   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6352   int belt_dir = belt_move_dir[belt_dir_nr];
6353   int xx, yy, i;
6354
6355   if (!IS_BELT_SWITCH(element))
6356     return;
6357
6358   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6359   game.belt_dir[belt_nr] = belt_dir;
6360
6361   if (belt_dir_nr == 3)
6362     belt_dir_nr = 1;
6363
6364   // set frame order for belt animation graphic according to belt direction
6365   for (i = 0; i < NUM_BELT_PARTS; i++)
6366   {
6367     int element = belt_base_active_element[belt_nr] + i;
6368     int graphic_1 = el2img(element);
6369     int graphic_2 = el2panelimg(element);
6370
6371     if (belt_dir == MV_LEFT)
6372     {
6373       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6374       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6375     }
6376     else
6377     {
6378       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6379       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6380     }
6381   }
6382
6383   SCAN_PLAYFIELD(xx, yy)
6384   {
6385     int element = Tile[xx][yy];
6386
6387     if (IS_BELT_SWITCH(element))
6388     {
6389       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6390
6391       if (e_belt_nr == belt_nr)
6392       {
6393         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6394         TEST_DrawLevelField(xx, yy);
6395       }
6396     }
6397     else if (IS_BELT(element) && belt_dir != MV_NONE)
6398     {
6399       int e_belt_nr = getBeltNrFromBeltElement(element);
6400
6401       if (e_belt_nr == belt_nr)
6402       {
6403         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6404
6405         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6406         TEST_DrawLevelField(xx, yy);
6407       }
6408     }
6409     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6410     {
6411       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6412
6413       if (e_belt_nr == belt_nr)
6414       {
6415         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6416
6417         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6418         TEST_DrawLevelField(xx, yy);
6419       }
6420     }
6421   }
6422 }
6423
6424 static void ToggleSwitchgateSwitch(void)
6425 {
6426   int xx, yy;
6427
6428   game.switchgate_pos = !game.switchgate_pos;
6429
6430   SCAN_PLAYFIELD(xx, yy)
6431   {
6432     int element = Tile[xx][yy];
6433
6434     if (element == EL_SWITCHGATE_SWITCH_UP)
6435     {
6436       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6437       TEST_DrawLevelField(xx, yy);
6438     }
6439     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6440     {
6441       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6442       TEST_DrawLevelField(xx, yy);
6443     }
6444     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6445     {
6446       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6447       TEST_DrawLevelField(xx, yy);
6448     }
6449     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6450     {
6451       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6452       TEST_DrawLevelField(xx, yy);
6453     }
6454     else if (element == EL_SWITCHGATE_OPEN ||
6455              element == EL_SWITCHGATE_OPENING)
6456     {
6457       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6458
6459       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6460     }
6461     else if (element == EL_SWITCHGATE_CLOSED ||
6462              element == EL_SWITCHGATE_CLOSING)
6463     {
6464       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6465
6466       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6467     }
6468   }
6469 }
6470
6471 static int getInvisibleActiveFromInvisibleElement(int element)
6472 {
6473   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6474           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6475           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6476           element);
6477 }
6478
6479 static int getInvisibleFromInvisibleActiveElement(int element)
6480 {
6481   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6482           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6483           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6484           element);
6485 }
6486
6487 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6488 {
6489   int x, y;
6490
6491   SCAN_PLAYFIELD(x, y)
6492   {
6493     int element = Tile[x][y];
6494
6495     if (element == EL_LIGHT_SWITCH &&
6496         game.light_time_left > 0)
6497     {
6498       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6499       TEST_DrawLevelField(x, y);
6500     }
6501     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6502              game.light_time_left == 0)
6503     {
6504       Tile[x][y] = EL_LIGHT_SWITCH;
6505       TEST_DrawLevelField(x, y);
6506     }
6507     else if (element == EL_EMC_DRIPPER &&
6508              game.light_time_left > 0)
6509     {
6510       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6511       TEST_DrawLevelField(x, y);
6512     }
6513     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6514              game.light_time_left == 0)
6515     {
6516       Tile[x][y] = EL_EMC_DRIPPER;
6517       TEST_DrawLevelField(x, y);
6518     }
6519     else if (element == EL_INVISIBLE_STEELWALL ||
6520              element == EL_INVISIBLE_WALL ||
6521              element == EL_INVISIBLE_SAND)
6522     {
6523       if (game.light_time_left > 0)
6524         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6525
6526       TEST_DrawLevelField(x, y);
6527
6528       // uncrumble neighbour fields, if needed
6529       if (element == EL_INVISIBLE_SAND)
6530         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6531     }
6532     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6533              element == EL_INVISIBLE_WALL_ACTIVE ||
6534              element == EL_INVISIBLE_SAND_ACTIVE)
6535     {
6536       if (game.light_time_left == 0)
6537         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6538
6539       TEST_DrawLevelField(x, y);
6540
6541       // re-crumble neighbour fields, if needed
6542       if (element == EL_INVISIBLE_SAND)
6543         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6544     }
6545   }
6546 }
6547
6548 static void RedrawAllInvisibleElementsForLenses(void)
6549 {
6550   int x, y;
6551
6552   SCAN_PLAYFIELD(x, y)
6553   {
6554     int element = Tile[x][y];
6555
6556     if (element == EL_EMC_DRIPPER &&
6557         game.lenses_time_left > 0)
6558     {
6559       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6560       TEST_DrawLevelField(x, y);
6561     }
6562     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6563              game.lenses_time_left == 0)
6564     {
6565       Tile[x][y] = EL_EMC_DRIPPER;
6566       TEST_DrawLevelField(x, y);
6567     }
6568     else if (element == EL_INVISIBLE_STEELWALL ||
6569              element == EL_INVISIBLE_WALL ||
6570              element == EL_INVISIBLE_SAND)
6571     {
6572       if (game.lenses_time_left > 0)
6573         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6574
6575       TEST_DrawLevelField(x, y);
6576
6577       // uncrumble neighbour fields, if needed
6578       if (element == EL_INVISIBLE_SAND)
6579         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6580     }
6581     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6582              element == EL_INVISIBLE_WALL_ACTIVE ||
6583              element == EL_INVISIBLE_SAND_ACTIVE)
6584     {
6585       if (game.lenses_time_left == 0)
6586         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6587
6588       TEST_DrawLevelField(x, y);
6589
6590       // re-crumble neighbour fields, if needed
6591       if (element == EL_INVISIBLE_SAND)
6592         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6593     }
6594   }
6595 }
6596
6597 static void RedrawAllInvisibleElementsForMagnifier(void)
6598 {
6599   int x, y;
6600
6601   SCAN_PLAYFIELD(x, y)
6602   {
6603     int element = Tile[x][y];
6604
6605     if (element == EL_EMC_FAKE_GRASS &&
6606         game.magnify_time_left > 0)
6607     {
6608       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6609       TEST_DrawLevelField(x, y);
6610     }
6611     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6612              game.magnify_time_left == 0)
6613     {
6614       Tile[x][y] = EL_EMC_FAKE_GRASS;
6615       TEST_DrawLevelField(x, y);
6616     }
6617     else if (IS_GATE_GRAY(element) &&
6618              game.magnify_time_left > 0)
6619     {
6620       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6621                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6622                     IS_EM_GATE_GRAY(element) ?
6623                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6624                     IS_EMC_GATE_GRAY(element) ?
6625                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6626                     IS_DC_GATE_GRAY(element) ?
6627                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6628                     element);
6629       TEST_DrawLevelField(x, y);
6630     }
6631     else if (IS_GATE_GRAY_ACTIVE(element) &&
6632              game.magnify_time_left == 0)
6633     {
6634       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6635                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6636                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6637                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6638                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6639                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6640                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6641                     EL_DC_GATE_WHITE_GRAY :
6642                     element);
6643       TEST_DrawLevelField(x, y);
6644     }
6645   }
6646 }
6647
6648 static void ToggleLightSwitch(int x, int y)
6649 {
6650   int element = Tile[x][y];
6651
6652   game.light_time_left =
6653     (element == EL_LIGHT_SWITCH ?
6654      level.time_light * FRAMES_PER_SECOND : 0);
6655
6656   RedrawAllLightSwitchesAndInvisibleElements();
6657 }
6658
6659 static void ActivateTimegateSwitch(int x, int y)
6660 {
6661   int xx, yy;
6662
6663   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6664
6665   SCAN_PLAYFIELD(xx, yy)
6666   {
6667     int element = Tile[xx][yy];
6668
6669     if (element == EL_TIMEGATE_CLOSED ||
6670         element == EL_TIMEGATE_CLOSING)
6671     {
6672       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6673       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6674     }
6675
6676     /*
6677     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6678     {
6679       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6680       TEST_DrawLevelField(xx, yy);
6681     }
6682     */
6683
6684   }
6685
6686   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6687                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6688 }
6689
6690 static void Impact(int x, int y)
6691 {
6692   boolean last_line = (y == lev_fieldy - 1);
6693   boolean object_hit = FALSE;
6694   boolean impact = (last_line || object_hit);
6695   int element = Tile[x][y];
6696   int smashed = EL_STEELWALL;
6697
6698   if (!last_line)       // check if element below was hit
6699   {
6700     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6701       return;
6702
6703     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6704                                          MovDir[x][y + 1] != MV_DOWN ||
6705                                          MovPos[x][y + 1] <= TILEY / 2));
6706
6707     // do not smash moving elements that left the smashed field in time
6708     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6709         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6710       object_hit = FALSE;
6711
6712 #if USE_QUICKSAND_IMPACT_BUGFIX
6713     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6714     {
6715       RemoveMovingField(x, y + 1);
6716       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6717       Tile[x][y + 2] = EL_ROCK;
6718       TEST_DrawLevelField(x, y + 2);
6719
6720       object_hit = TRUE;
6721     }
6722
6723     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6724     {
6725       RemoveMovingField(x, y + 1);
6726       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6727       Tile[x][y + 2] = EL_ROCK;
6728       TEST_DrawLevelField(x, y + 2);
6729
6730       object_hit = TRUE;
6731     }
6732 #endif
6733
6734     if (object_hit)
6735       smashed = MovingOrBlocked2Element(x, y + 1);
6736
6737     impact = (last_line || object_hit);
6738   }
6739
6740   if (!last_line && smashed == EL_ACID) // element falls into acid
6741   {
6742     SplashAcid(x, y + 1);
6743     return;
6744   }
6745
6746   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6747   // only reset graphic animation if graphic really changes after impact
6748   if (impact &&
6749       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6750   {
6751     ResetGfxAnimation(x, y);
6752     TEST_DrawLevelField(x, y);
6753   }
6754
6755   if (impact && CAN_EXPLODE_IMPACT(element))
6756   {
6757     Bang(x, y);
6758     return;
6759   }
6760   else if (impact && element == EL_PEARL &&
6761            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6762   {
6763     ResetGfxAnimation(x, y);
6764
6765     Tile[x][y] = EL_PEARL_BREAKING;
6766     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6767     return;
6768   }
6769   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6770   {
6771     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6772
6773     return;
6774   }
6775
6776   if (impact && element == EL_AMOEBA_DROP)
6777   {
6778     if (object_hit && IS_PLAYER(x, y + 1))
6779       KillPlayerUnlessEnemyProtected(x, y + 1);
6780     else if (object_hit && smashed == EL_PENGUIN)
6781       Bang(x, y + 1);
6782     else
6783     {
6784       Tile[x][y] = EL_AMOEBA_GROWING;
6785       Store[x][y] = EL_AMOEBA_WET;
6786
6787       ResetRandomAnimationValue(x, y);
6788     }
6789     return;
6790   }
6791
6792   if (object_hit)               // check which object was hit
6793   {
6794     if ((CAN_PASS_MAGIC_WALL(element) && 
6795          (smashed == EL_MAGIC_WALL ||
6796           smashed == EL_BD_MAGIC_WALL)) ||
6797         (CAN_PASS_DC_MAGIC_WALL(element) &&
6798          smashed == EL_DC_MAGIC_WALL))
6799     {
6800       int xx, yy;
6801       int activated_magic_wall =
6802         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6803          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6804          EL_DC_MAGIC_WALL_ACTIVE);
6805
6806       // activate magic wall / mill
6807       SCAN_PLAYFIELD(xx, yy)
6808       {
6809         if (Tile[xx][yy] == smashed)
6810           Tile[xx][yy] = activated_magic_wall;
6811       }
6812
6813       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6814       game.magic_wall_active = TRUE;
6815
6816       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6817                             SND_MAGIC_WALL_ACTIVATING :
6818                             smashed == EL_BD_MAGIC_WALL ?
6819                             SND_BD_MAGIC_WALL_ACTIVATING :
6820                             SND_DC_MAGIC_WALL_ACTIVATING));
6821     }
6822
6823     if (IS_PLAYER(x, y + 1))
6824     {
6825       if (CAN_SMASH_PLAYER(element))
6826       {
6827         KillPlayerUnlessEnemyProtected(x, y + 1);
6828         return;
6829       }
6830     }
6831     else if (smashed == EL_PENGUIN)
6832     {
6833       if (CAN_SMASH_PLAYER(element))
6834       {
6835         Bang(x, y + 1);
6836         return;
6837       }
6838     }
6839     else if (element == EL_BD_DIAMOND)
6840     {
6841       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6842       {
6843         Bang(x, y + 1);
6844         return;
6845       }
6846     }
6847     else if (((element == EL_SP_INFOTRON ||
6848                element == EL_SP_ZONK) &&
6849               (smashed == EL_SP_SNIKSNAK ||
6850                smashed == EL_SP_ELECTRON ||
6851                smashed == EL_SP_DISK_ORANGE)) ||
6852              (element == EL_SP_INFOTRON &&
6853               smashed == EL_SP_DISK_YELLOW))
6854     {
6855       Bang(x, y + 1);
6856       return;
6857     }
6858     else if (CAN_SMASH_EVERYTHING(element))
6859     {
6860       if (IS_CLASSIC_ENEMY(smashed) ||
6861           CAN_EXPLODE_SMASHED(smashed))
6862       {
6863         Bang(x, y + 1);
6864         return;
6865       }
6866       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6867       {
6868         if (smashed == EL_LAMP ||
6869             smashed == EL_LAMP_ACTIVE)
6870         {
6871           Bang(x, y + 1);
6872           return;
6873         }
6874         else if (smashed == EL_NUT)
6875         {
6876           Tile[x][y + 1] = EL_NUT_BREAKING;
6877           PlayLevelSound(x, y, SND_NUT_BREAKING);
6878           RaiseScoreElement(EL_NUT);
6879           return;
6880         }
6881         else if (smashed == EL_PEARL)
6882         {
6883           ResetGfxAnimation(x, y);
6884
6885           Tile[x][y + 1] = EL_PEARL_BREAKING;
6886           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6887           return;
6888         }
6889         else if (smashed == EL_DIAMOND)
6890         {
6891           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6892           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6893           return;
6894         }
6895         else if (IS_BELT_SWITCH(smashed))
6896         {
6897           ToggleBeltSwitch(x, y + 1);
6898         }
6899         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6900                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6901                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6902                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6903         {
6904           ToggleSwitchgateSwitch();
6905         }
6906         else if (smashed == EL_LIGHT_SWITCH ||
6907                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6908         {
6909           ToggleLightSwitch(x, y + 1);
6910         }
6911         else
6912         {
6913           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6914
6915           CheckElementChangeBySide(x, y + 1, smashed, element,
6916                                    CE_SWITCHED, CH_SIDE_TOP);
6917           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6918                                             CH_SIDE_TOP);
6919         }
6920       }
6921       else
6922       {
6923         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6924       }
6925     }
6926   }
6927
6928   // play sound of magic wall / mill
6929   if (!last_line &&
6930       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6931        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6932        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6933   {
6934     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6935       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6936     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6937       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6938     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6939       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6940
6941     return;
6942   }
6943
6944   // play sound of object that hits the ground
6945   if (last_line || object_hit)
6946     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6947 }
6948
6949 static void TurnRoundExt(int x, int y)
6950 {
6951   static struct
6952   {
6953     int dx, dy;
6954   } move_xy[] =
6955   {
6956     {  0,  0 },
6957     { -1,  0 },
6958     { +1,  0 },
6959     {  0,  0 },
6960     {  0, -1 },
6961     {  0,  0 }, { 0, 0 }, { 0, 0 },
6962     {  0, +1 }
6963   };
6964   static struct
6965   {
6966     int left, right, back;
6967   } turn[] =
6968   {
6969     { 0,        0,              0        },
6970     { MV_DOWN,  MV_UP,          MV_RIGHT },
6971     { MV_UP,    MV_DOWN,        MV_LEFT  },
6972     { 0,        0,              0        },
6973     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6974     { 0,        0,              0        },
6975     { 0,        0,              0        },
6976     { 0,        0,              0        },
6977     { MV_RIGHT, MV_LEFT,        MV_UP    }
6978   };
6979
6980   int element = Tile[x][y];
6981   int move_pattern = element_info[element].move_pattern;
6982
6983   int old_move_dir = MovDir[x][y];
6984   int left_dir  = turn[old_move_dir].left;
6985   int right_dir = turn[old_move_dir].right;
6986   int back_dir  = turn[old_move_dir].back;
6987
6988   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6989   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6990   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6991   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6992
6993   int left_x  = x + left_dx,  left_y  = y + left_dy;
6994   int right_x = x + right_dx, right_y = y + right_dy;
6995   int move_x  = x + move_dx,  move_y  = y + move_dy;
6996
6997   int xx, yy;
6998
6999   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7000   {
7001     TestIfBadThingTouchesOtherBadThing(x, y);
7002
7003     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7004       MovDir[x][y] = right_dir;
7005     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7006       MovDir[x][y] = left_dir;
7007
7008     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7009       MovDelay[x][y] = 9;
7010     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7011       MovDelay[x][y] = 1;
7012   }
7013   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7014   {
7015     TestIfBadThingTouchesOtherBadThing(x, y);
7016
7017     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7018       MovDir[x][y] = left_dir;
7019     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7020       MovDir[x][y] = right_dir;
7021
7022     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7023       MovDelay[x][y] = 9;
7024     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7025       MovDelay[x][y] = 1;
7026   }
7027   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7028   {
7029     TestIfBadThingTouchesOtherBadThing(x, y);
7030
7031     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7032       MovDir[x][y] = left_dir;
7033     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7034       MovDir[x][y] = right_dir;
7035
7036     if (MovDir[x][y] != old_move_dir)
7037       MovDelay[x][y] = 9;
7038   }
7039   else if (element == EL_YAMYAM)
7040   {
7041     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7042     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7043
7044     if (can_turn_left && can_turn_right)
7045       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7046     else if (can_turn_left)
7047       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7048     else if (can_turn_right)
7049       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7050     else
7051       MovDir[x][y] = back_dir;
7052
7053     MovDelay[x][y] = 16 + 16 * RND(3);
7054   }
7055   else if (element == EL_DARK_YAMYAM)
7056   {
7057     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7058                                                          left_x, left_y);
7059     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7060                                                          right_x, right_y);
7061
7062     if (can_turn_left && can_turn_right)
7063       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7064     else if (can_turn_left)
7065       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7066     else if (can_turn_right)
7067       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7068     else
7069       MovDir[x][y] = back_dir;
7070
7071     MovDelay[x][y] = 16 + 16 * RND(3);
7072   }
7073   else if (element == EL_PACMAN)
7074   {
7075     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7076     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7077
7078     if (can_turn_left && can_turn_right)
7079       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7080     else if (can_turn_left)
7081       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7082     else if (can_turn_right)
7083       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7084     else
7085       MovDir[x][y] = back_dir;
7086
7087     MovDelay[x][y] = 6 + RND(40);
7088   }
7089   else if (element == EL_PIG)
7090   {
7091     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7092     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7093     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7094     boolean should_turn_left, should_turn_right, should_move_on;
7095     int rnd_value = 24;
7096     int rnd = RND(rnd_value);
7097
7098     should_turn_left = (can_turn_left &&
7099                         (!can_move_on ||
7100                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7101                                                    y + back_dy + left_dy)));
7102     should_turn_right = (can_turn_right &&
7103                          (!can_move_on ||
7104                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7105                                                     y + back_dy + right_dy)));
7106     should_move_on = (can_move_on &&
7107                       (!can_turn_left ||
7108                        !can_turn_right ||
7109                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7110                                                  y + move_dy + left_dy) ||
7111                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7112                                                  y + move_dy + right_dy)));
7113
7114     if (should_turn_left || should_turn_right || should_move_on)
7115     {
7116       if (should_turn_left && should_turn_right && should_move_on)
7117         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7118                         rnd < 2 * rnd_value / 3 ? right_dir :
7119                         old_move_dir);
7120       else if (should_turn_left && should_turn_right)
7121         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7122       else if (should_turn_left && should_move_on)
7123         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7124       else if (should_turn_right && should_move_on)
7125         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7126       else if (should_turn_left)
7127         MovDir[x][y] = left_dir;
7128       else if (should_turn_right)
7129         MovDir[x][y] = right_dir;
7130       else if (should_move_on)
7131         MovDir[x][y] = old_move_dir;
7132     }
7133     else if (can_move_on && rnd > rnd_value / 8)
7134       MovDir[x][y] = old_move_dir;
7135     else if (can_turn_left && can_turn_right)
7136       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7137     else if (can_turn_left && rnd > rnd_value / 8)
7138       MovDir[x][y] = left_dir;
7139     else if (can_turn_right && rnd > rnd_value/8)
7140       MovDir[x][y] = right_dir;
7141     else
7142       MovDir[x][y] = back_dir;
7143
7144     xx = x + move_xy[MovDir[x][y]].dx;
7145     yy = y + move_xy[MovDir[x][y]].dy;
7146
7147     if (!IN_LEV_FIELD(xx, yy) ||
7148         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7149       MovDir[x][y] = old_move_dir;
7150
7151     MovDelay[x][y] = 0;
7152   }
7153   else if (element == EL_DRAGON)
7154   {
7155     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7156     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7157     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7158     int rnd_value = 24;
7159     int rnd = RND(rnd_value);
7160
7161     if (can_move_on && rnd > rnd_value / 8)
7162       MovDir[x][y] = old_move_dir;
7163     else if (can_turn_left && can_turn_right)
7164       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7165     else if (can_turn_left && rnd > rnd_value / 8)
7166       MovDir[x][y] = left_dir;
7167     else if (can_turn_right && rnd > rnd_value / 8)
7168       MovDir[x][y] = right_dir;
7169     else
7170       MovDir[x][y] = back_dir;
7171
7172     xx = x + move_xy[MovDir[x][y]].dx;
7173     yy = y + move_xy[MovDir[x][y]].dy;
7174
7175     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7176       MovDir[x][y] = old_move_dir;
7177
7178     MovDelay[x][y] = 0;
7179   }
7180   else if (element == EL_MOLE)
7181   {
7182     boolean can_move_on =
7183       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7184                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7185                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7186     if (!can_move_on)
7187     {
7188       boolean can_turn_left =
7189         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7190                               IS_AMOEBOID(Tile[left_x][left_y])));
7191
7192       boolean can_turn_right =
7193         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7194                               IS_AMOEBOID(Tile[right_x][right_y])));
7195
7196       if (can_turn_left && can_turn_right)
7197         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7198       else if (can_turn_left)
7199         MovDir[x][y] = left_dir;
7200       else
7201         MovDir[x][y] = right_dir;
7202     }
7203
7204     if (MovDir[x][y] != old_move_dir)
7205       MovDelay[x][y] = 9;
7206   }
7207   else if (element == EL_BALLOON)
7208   {
7209     MovDir[x][y] = game.wind_direction;
7210     MovDelay[x][y] = 0;
7211   }
7212   else if (element == EL_SPRING)
7213   {
7214     if (MovDir[x][y] & MV_HORIZONTAL)
7215     {
7216       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7217           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7218       {
7219         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7220         ResetGfxAnimation(move_x, move_y);
7221         TEST_DrawLevelField(move_x, move_y);
7222
7223         MovDir[x][y] = back_dir;
7224       }
7225       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7226                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7227         MovDir[x][y] = MV_NONE;
7228     }
7229
7230     MovDelay[x][y] = 0;
7231   }
7232   else if (element == EL_ROBOT ||
7233            element == EL_SATELLITE ||
7234            element == EL_PENGUIN ||
7235            element == EL_EMC_ANDROID)
7236   {
7237     int attr_x = -1, attr_y = -1;
7238
7239     if (game.all_players_gone)
7240     {
7241       attr_x = game.exit_x;
7242       attr_y = game.exit_y;
7243     }
7244     else
7245     {
7246       int i;
7247
7248       for (i = 0; i < MAX_PLAYERS; i++)
7249       {
7250         struct PlayerInfo *player = &stored_player[i];
7251         int jx = player->jx, jy = player->jy;
7252
7253         if (!player->active)
7254           continue;
7255
7256         if (attr_x == -1 ||
7257             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7258         {
7259           attr_x = jx;
7260           attr_y = jy;
7261         }
7262       }
7263     }
7264
7265     if (element == EL_ROBOT &&
7266         game.robot_wheel_x >= 0 &&
7267         game.robot_wheel_y >= 0 &&
7268         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7269          game.engine_version < VERSION_IDENT(3,1,0,0)))
7270     {
7271       attr_x = game.robot_wheel_x;
7272       attr_y = game.robot_wheel_y;
7273     }
7274
7275     if (element == EL_PENGUIN)
7276     {
7277       int i;
7278       struct XY *xy = xy_topdown;
7279
7280       for (i = 0; i < NUM_DIRECTIONS; i++)
7281       {
7282         int ex = x + xy[i].x;
7283         int ey = y + xy[i].y;
7284
7285         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7286                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7287                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7288                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7289         {
7290           attr_x = ex;
7291           attr_y = ey;
7292           break;
7293         }
7294       }
7295     }
7296
7297     MovDir[x][y] = MV_NONE;
7298     if (attr_x < x)
7299       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7300     else if (attr_x > x)
7301       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7302     if (attr_y < y)
7303       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7304     else if (attr_y > y)
7305       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7306
7307     if (element == EL_ROBOT)
7308     {
7309       int newx, newy;
7310
7311       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7312         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7313       Moving2Blocked(x, y, &newx, &newy);
7314
7315       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7316         MovDelay[x][y] = 8 + 8 * !RND(3);
7317       else
7318         MovDelay[x][y] = 16;
7319     }
7320     else if (element == EL_PENGUIN)
7321     {
7322       int newx, newy;
7323
7324       MovDelay[x][y] = 1;
7325
7326       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7327       {
7328         boolean first_horiz = RND(2);
7329         int new_move_dir = MovDir[x][y];
7330
7331         MovDir[x][y] =
7332           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7333         Moving2Blocked(x, y, &newx, &newy);
7334
7335         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7336           return;
7337
7338         MovDir[x][y] =
7339           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7340         Moving2Blocked(x, y, &newx, &newy);
7341
7342         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7343           return;
7344
7345         MovDir[x][y] = old_move_dir;
7346         return;
7347       }
7348     }
7349     else if (element == EL_SATELLITE)
7350     {
7351       int newx, newy;
7352
7353       MovDelay[x][y] = 1;
7354
7355       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7356       {
7357         boolean first_horiz = RND(2);
7358         int new_move_dir = MovDir[x][y];
7359
7360         MovDir[x][y] =
7361           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7362         Moving2Blocked(x, y, &newx, &newy);
7363
7364         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7365           return;
7366
7367         MovDir[x][y] =
7368           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7369         Moving2Blocked(x, y, &newx, &newy);
7370
7371         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7372           return;
7373
7374         MovDir[x][y] = old_move_dir;
7375         return;
7376       }
7377     }
7378     else if (element == EL_EMC_ANDROID)
7379     {
7380       static int check_pos[16] =
7381       {
7382         -1,             //  0 => (invalid)
7383         7,              //  1 => MV_LEFT
7384         3,              //  2 => MV_RIGHT
7385         -1,             //  3 => (invalid)
7386         1,              //  4 =>            MV_UP
7387         0,              //  5 => MV_LEFT  | MV_UP
7388         2,              //  6 => MV_RIGHT | MV_UP
7389         -1,             //  7 => (invalid)
7390         5,              //  8 =>            MV_DOWN
7391         6,              //  9 => MV_LEFT  | MV_DOWN
7392         4,              // 10 => MV_RIGHT | MV_DOWN
7393         -1,             // 11 => (invalid)
7394         -1,             // 12 => (invalid)
7395         -1,             // 13 => (invalid)
7396         -1,             // 14 => (invalid)
7397         -1,             // 15 => (invalid)
7398       };
7399       static struct
7400       {
7401         int dx, dy;
7402         int dir;
7403       } check_xy[8] =
7404       {
7405         { -1, -1,       MV_LEFT  | MV_UP   },
7406         {  0, -1,                  MV_UP   },
7407         { +1, -1,       MV_RIGHT | MV_UP   },
7408         { +1,  0,       MV_RIGHT           },
7409         { +1, +1,       MV_RIGHT | MV_DOWN },
7410         {  0, +1,                  MV_DOWN },
7411         { -1, +1,       MV_LEFT  | MV_DOWN },
7412         { -1,  0,       MV_LEFT            },
7413       };
7414       int start_pos, check_order;
7415       boolean can_clone = FALSE;
7416       int i;
7417
7418       // check if there is any free field around current position
7419       for (i = 0; i < 8; i++)
7420       {
7421         int newx = x + check_xy[i].dx;
7422         int newy = y + check_xy[i].dy;
7423
7424         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7425         {
7426           can_clone = TRUE;
7427
7428           break;
7429         }
7430       }
7431
7432       if (can_clone)            // randomly find an element to clone
7433       {
7434         can_clone = FALSE;
7435
7436         start_pos = check_pos[RND(8)];
7437         check_order = (RND(2) ? -1 : +1);
7438
7439         for (i = 0; i < 8; i++)
7440         {
7441           int pos_raw = start_pos + i * check_order;
7442           int pos = (pos_raw + 8) % 8;
7443           int newx = x + check_xy[pos].dx;
7444           int newy = y + check_xy[pos].dy;
7445
7446           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7447           {
7448             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7449             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7450
7451             Store[x][y] = Tile[newx][newy];
7452
7453             can_clone = TRUE;
7454
7455             break;
7456           }
7457         }
7458       }
7459
7460       if (can_clone)            // randomly find a direction to move
7461       {
7462         can_clone = FALSE;
7463
7464         start_pos = check_pos[RND(8)];
7465         check_order = (RND(2) ? -1 : +1);
7466
7467         for (i = 0; i < 8; i++)
7468         {
7469           int pos_raw = start_pos + i * check_order;
7470           int pos = (pos_raw + 8) % 8;
7471           int newx = x + check_xy[pos].dx;
7472           int newy = y + check_xy[pos].dy;
7473           int new_move_dir = check_xy[pos].dir;
7474
7475           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7476           {
7477             MovDir[x][y] = new_move_dir;
7478             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7479
7480             can_clone = TRUE;
7481
7482             break;
7483           }
7484         }
7485       }
7486
7487       if (can_clone)            // cloning and moving successful
7488         return;
7489
7490       // cannot clone -- try to move towards player
7491
7492       start_pos = check_pos[MovDir[x][y] & 0x0f];
7493       check_order = (RND(2) ? -1 : +1);
7494
7495       for (i = 0; i < 3; i++)
7496       {
7497         // first check start_pos, then previous/next or (next/previous) pos
7498         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7499         int pos = (pos_raw + 8) % 8;
7500         int newx = x + check_xy[pos].dx;
7501         int newy = y + check_xy[pos].dy;
7502         int new_move_dir = check_xy[pos].dir;
7503
7504         if (IS_PLAYER(newx, newy))
7505           break;
7506
7507         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7508         {
7509           MovDir[x][y] = new_move_dir;
7510           MovDelay[x][y] = level.android_move_time * 8 + 1;
7511
7512           break;
7513         }
7514       }
7515     }
7516   }
7517   else if (move_pattern == MV_TURNING_LEFT ||
7518            move_pattern == MV_TURNING_RIGHT ||
7519            move_pattern == MV_TURNING_LEFT_RIGHT ||
7520            move_pattern == MV_TURNING_RIGHT_LEFT ||
7521            move_pattern == MV_TURNING_RANDOM ||
7522            move_pattern == MV_ALL_DIRECTIONS)
7523   {
7524     boolean can_turn_left =
7525       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7526     boolean can_turn_right =
7527       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7528
7529     if (element_info[element].move_stepsize == 0)       // "not moving"
7530       return;
7531
7532     if (move_pattern == MV_TURNING_LEFT)
7533       MovDir[x][y] = left_dir;
7534     else if (move_pattern == MV_TURNING_RIGHT)
7535       MovDir[x][y] = right_dir;
7536     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7537       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7538     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7539       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7540     else if (move_pattern == MV_TURNING_RANDOM)
7541       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7542                       can_turn_right && !can_turn_left ? right_dir :
7543                       RND(2) ? left_dir : right_dir);
7544     else if (can_turn_left && can_turn_right)
7545       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7546     else if (can_turn_left)
7547       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7548     else if (can_turn_right)
7549       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7550     else
7551       MovDir[x][y] = back_dir;
7552
7553     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7554   }
7555   else if (move_pattern == MV_HORIZONTAL ||
7556            move_pattern == MV_VERTICAL)
7557   {
7558     if (move_pattern & old_move_dir)
7559       MovDir[x][y] = back_dir;
7560     else if (move_pattern == MV_HORIZONTAL)
7561       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7562     else if (move_pattern == MV_VERTICAL)
7563       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7564
7565     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7566   }
7567   else if (move_pattern & MV_ANY_DIRECTION)
7568   {
7569     MovDir[x][y] = move_pattern;
7570     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7571   }
7572   else if (move_pattern & MV_WIND_DIRECTION)
7573   {
7574     MovDir[x][y] = game.wind_direction;
7575     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7576   }
7577   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7578   {
7579     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7580       MovDir[x][y] = left_dir;
7581     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7582       MovDir[x][y] = right_dir;
7583
7584     if (MovDir[x][y] != old_move_dir)
7585       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7586   }
7587   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7588   {
7589     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7590       MovDir[x][y] = right_dir;
7591     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7592       MovDir[x][y] = left_dir;
7593
7594     if (MovDir[x][y] != old_move_dir)
7595       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7596   }
7597   else if (move_pattern == MV_TOWARDS_PLAYER ||
7598            move_pattern == MV_AWAY_FROM_PLAYER)
7599   {
7600     int attr_x = -1, attr_y = -1;
7601     int newx, newy;
7602     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7603
7604     if (game.all_players_gone)
7605     {
7606       attr_x = game.exit_x;
7607       attr_y = game.exit_y;
7608     }
7609     else
7610     {
7611       int i;
7612
7613       for (i = 0; i < MAX_PLAYERS; i++)
7614       {
7615         struct PlayerInfo *player = &stored_player[i];
7616         int jx = player->jx, jy = player->jy;
7617
7618         if (!player->active)
7619           continue;
7620
7621         if (attr_x == -1 ||
7622             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7623         {
7624           attr_x = jx;
7625           attr_y = jy;
7626         }
7627       }
7628     }
7629
7630     MovDir[x][y] = MV_NONE;
7631     if (attr_x < x)
7632       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7633     else if (attr_x > x)
7634       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7635     if (attr_y < y)
7636       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7637     else if (attr_y > y)
7638       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7639
7640     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7641
7642     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7643     {
7644       boolean first_horiz = RND(2);
7645       int new_move_dir = MovDir[x][y];
7646
7647       if (element_info[element].move_stepsize == 0)     // "not moving"
7648       {
7649         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7650         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7651
7652         return;
7653       }
7654
7655       MovDir[x][y] =
7656         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7657       Moving2Blocked(x, y, &newx, &newy);
7658
7659       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7660         return;
7661
7662       MovDir[x][y] =
7663         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7664       Moving2Blocked(x, y, &newx, &newy);
7665
7666       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7667         return;
7668
7669       MovDir[x][y] = old_move_dir;
7670     }
7671   }
7672   else if (move_pattern == MV_WHEN_PUSHED ||
7673            move_pattern == MV_WHEN_DROPPED)
7674   {
7675     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7676       MovDir[x][y] = MV_NONE;
7677
7678     MovDelay[x][y] = 0;
7679   }
7680   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7681   {
7682     struct XY *test_xy = xy_topdown;
7683     static int test_dir[4] =
7684     {
7685       MV_UP,
7686       MV_LEFT,
7687       MV_RIGHT,
7688       MV_DOWN
7689     };
7690     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7691     int move_preference = -1000000;     // start with very low preference
7692     int new_move_dir = MV_NONE;
7693     int start_test = RND(4);
7694     int i;
7695
7696     for (i = 0; i < NUM_DIRECTIONS; i++)
7697     {
7698       int j = (start_test + i) % 4;
7699       int move_dir = test_dir[j];
7700       int move_dir_preference;
7701
7702       xx = x + test_xy[j].x;
7703       yy = y + test_xy[j].y;
7704
7705       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7706           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7707       {
7708         new_move_dir = move_dir;
7709
7710         break;
7711       }
7712
7713       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7714         continue;
7715
7716       move_dir_preference = -1 * RunnerVisit[xx][yy];
7717       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7718         move_dir_preference = PlayerVisit[xx][yy];
7719
7720       if (move_dir_preference > move_preference)
7721       {
7722         // prefer field that has not been visited for the longest time
7723         move_preference = move_dir_preference;
7724         new_move_dir = move_dir;
7725       }
7726       else if (move_dir_preference == move_preference &&
7727                move_dir == old_move_dir)
7728       {
7729         // prefer last direction when all directions are preferred equally
7730         move_preference = move_dir_preference;
7731         new_move_dir = move_dir;
7732       }
7733     }
7734
7735     MovDir[x][y] = new_move_dir;
7736     if (old_move_dir != new_move_dir)
7737       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7738   }
7739 }
7740
7741 static void TurnRound(int x, int y)
7742 {
7743   int direction = MovDir[x][y];
7744
7745   TurnRoundExt(x, y);
7746
7747   GfxDir[x][y] = MovDir[x][y];
7748
7749   if (direction != MovDir[x][y])
7750     GfxFrame[x][y] = 0;
7751
7752   if (MovDelay[x][y])
7753     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7754
7755   ResetGfxFrame(x, y);
7756 }
7757
7758 static boolean JustBeingPushed(int x, int y)
7759 {
7760   int i;
7761
7762   for (i = 0; i < MAX_PLAYERS; i++)
7763   {
7764     struct PlayerInfo *player = &stored_player[i];
7765
7766     if (player->active && player->is_pushing && player->MovPos)
7767     {
7768       int next_jx = player->jx + (player->jx - player->last_jx);
7769       int next_jy = player->jy + (player->jy - player->last_jy);
7770
7771       if (x == next_jx && y == next_jy)
7772         return TRUE;
7773     }
7774   }
7775
7776   return FALSE;
7777 }
7778
7779 static void StartMoving(int x, int y)
7780 {
7781   boolean started_moving = FALSE;       // some elements can fall _and_ move
7782   int element = Tile[x][y];
7783
7784   if (Stop[x][y])
7785     return;
7786
7787   if (MovDelay[x][y] == 0)
7788     GfxAction[x][y] = ACTION_DEFAULT;
7789
7790   if (CAN_FALL(element) && y < lev_fieldy - 1)
7791   {
7792     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7793         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7794       if (JustBeingPushed(x, y))
7795         return;
7796
7797     if (element == EL_QUICKSAND_FULL)
7798     {
7799       if (IS_FREE(x, y + 1))
7800       {
7801         InitMovingField(x, y, MV_DOWN);
7802         started_moving = TRUE;
7803
7804         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7805 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7806         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7807           Store[x][y] = EL_ROCK;
7808 #else
7809         Store[x][y] = EL_ROCK;
7810 #endif
7811
7812         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7813       }
7814       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7815       {
7816         if (!MovDelay[x][y])
7817         {
7818           MovDelay[x][y] = TILEY + 1;
7819
7820           ResetGfxAnimation(x, y);
7821           ResetGfxAnimation(x, y + 1);
7822         }
7823
7824         if (MovDelay[x][y])
7825         {
7826           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7827           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7828
7829           MovDelay[x][y]--;
7830           if (MovDelay[x][y])
7831             return;
7832         }
7833
7834         Tile[x][y] = EL_QUICKSAND_EMPTY;
7835         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7836         Store[x][y + 1] = Store[x][y];
7837         Store[x][y] = 0;
7838
7839         PlayLevelSoundAction(x, y, ACTION_FILLING);
7840       }
7841       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7842       {
7843         if (!MovDelay[x][y])
7844         {
7845           MovDelay[x][y] = TILEY + 1;
7846
7847           ResetGfxAnimation(x, y);
7848           ResetGfxAnimation(x, y + 1);
7849         }
7850
7851         if (MovDelay[x][y])
7852         {
7853           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7854           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7855
7856           MovDelay[x][y]--;
7857           if (MovDelay[x][y])
7858             return;
7859         }
7860
7861         Tile[x][y] = EL_QUICKSAND_EMPTY;
7862         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7863         Store[x][y + 1] = Store[x][y];
7864         Store[x][y] = 0;
7865
7866         PlayLevelSoundAction(x, y, ACTION_FILLING);
7867       }
7868     }
7869     else if (element == EL_QUICKSAND_FAST_FULL)
7870     {
7871       if (IS_FREE(x, y + 1))
7872       {
7873         InitMovingField(x, y, MV_DOWN);
7874         started_moving = TRUE;
7875
7876         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7877 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7878         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7879           Store[x][y] = EL_ROCK;
7880 #else
7881         Store[x][y] = EL_ROCK;
7882 #endif
7883
7884         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7885       }
7886       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7887       {
7888         if (!MovDelay[x][y])
7889         {
7890           MovDelay[x][y] = TILEY + 1;
7891
7892           ResetGfxAnimation(x, y);
7893           ResetGfxAnimation(x, y + 1);
7894         }
7895
7896         if (MovDelay[x][y])
7897         {
7898           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7899           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7900
7901           MovDelay[x][y]--;
7902           if (MovDelay[x][y])
7903             return;
7904         }
7905
7906         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7907         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7908         Store[x][y + 1] = Store[x][y];
7909         Store[x][y] = 0;
7910
7911         PlayLevelSoundAction(x, y, ACTION_FILLING);
7912       }
7913       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7914       {
7915         if (!MovDelay[x][y])
7916         {
7917           MovDelay[x][y] = TILEY + 1;
7918
7919           ResetGfxAnimation(x, y);
7920           ResetGfxAnimation(x, y + 1);
7921         }
7922
7923         if (MovDelay[x][y])
7924         {
7925           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7926           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7927
7928           MovDelay[x][y]--;
7929           if (MovDelay[x][y])
7930             return;
7931         }
7932
7933         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7934         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7935         Store[x][y + 1] = Store[x][y];
7936         Store[x][y] = 0;
7937
7938         PlayLevelSoundAction(x, y, ACTION_FILLING);
7939       }
7940     }
7941     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7942              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7943     {
7944       InitMovingField(x, y, MV_DOWN);
7945       started_moving = TRUE;
7946
7947       Tile[x][y] = EL_QUICKSAND_FILLING;
7948       Store[x][y] = element;
7949
7950       PlayLevelSoundAction(x, y, ACTION_FILLING);
7951     }
7952     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7953              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7954     {
7955       InitMovingField(x, y, MV_DOWN);
7956       started_moving = TRUE;
7957
7958       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7959       Store[x][y] = element;
7960
7961       PlayLevelSoundAction(x, y, ACTION_FILLING);
7962     }
7963     else if (element == EL_MAGIC_WALL_FULL)
7964     {
7965       if (IS_FREE(x, y + 1))
7966       {
7967         InitMovingField(x, y, MV_DOWN);
7968         started_moving = TRUE;
7969
7970         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7971         Store[x][y] = EL_CHANGED(Store[x][y]);
7972       }
7973       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7974       {
7975         if (!MovDelay[x][y])
7976           MovDelay[x][y] = TILEY / 4 + 1;
7977
7978         if (MovDelay[x][y])
7979         {
7980           MovDelay[x][y]--;
7981           if (MovDelay[x][y])
7982             return;
7983         }
7984
7985         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7986         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7987         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7988         Store[x][y] = 0;
7989       }
7990     }
7991     else if (element == EL_BD_MAGIC_WALL_FULL)
7992     {
7993       if (IS_FREE(x, y + 1))
7994       {
7995         InitMovingField(x, y, MV_DOWN);
7996         started_moving = TRUE;
7997
7998         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7999         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8000       }
8001       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8002       {
8003         if (!MovDelay[x][y])
8004           MovDelay[x][y] = TILEY / 4 + 1;
8005
8006         if (MovDelay[x][y])
8007         {
8008           MovDelay[x][y]--;
8009           if (MovDelay[x][y])
8010             return;
8011         }
8012
8013         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8014         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8015         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8016         Store[x][y] = 0;
8017       }
8018     }
8019     else if (element == EL_DC_MAGIC_WALL_FULL)
8020     {
8021       if (IS_FREE(x, y + 1))
8022       {
8023         InitMovingField(x, y, MV_DOWN);
8024         started_moving = TRUE;
8025
8026         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8027         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8028       }
8029       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8030       {
8031         if (!MovDelay[x][y])
8032           MovDelay[x][y] = TILEY / 4 + 1;
8033
8034         if (MovDelay[x][y])
8035         {
8036           MovDelay[x][y]--;
8037           if (MovDelay[x][y])
8038             return;
8039         }
8040
8041         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8042         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8043         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8044         Store[x][y] = 0;
8045       }
8046     }
8047     else if ((CAN_PASS_MAGIC_WALL(element) &&
8048               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8049                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8050              (CAN_PASS_DC_MAGIC_WALL(element) &&
8051               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8052
8053     {
8054       InitMovingField(x, y, MV_DOWN);
8055       started_moving = TRUE;
8056
8057       Tile[x][y] =
8058         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8059          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8060          EL_DC_MAGIC_WALL_FILLING);
8061       Store[x][y] = element;
8062     }
8063     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8064     {
8065       SplashAcid(x, y + 1);
8066
8067       InitMovingField(x, y, MV_DOWN);
8068       started_moving = TRUE;
8069
8070       Store[x][y] = EL_ACID;
8071     }
8072     else if (
8073              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8074               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8075              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8076               CAN_FALL(element) && WasJustFalling[x][y] &&
8077               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8078
8079              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8080               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8081               (Tile[x][y + 1] == EL_BLOCKED)))
8082     {
8083       /* this is needed for a special case not covered by calling "Impact()"
8084          from "ContinueMoving()": if an element moves to a tile directly below
8085          another element which was just falling on that tile (which was empty
8086          in the previous frame), the falling element above would just stop
8087          instead of smashing the element below (in previous version, the above
8088          element was just checked for "moving" instead of "falling", resulting
8089          in incorrect smashes caused by horizontal movement of the above
8090          element; also, the case of the player being the element to smash was
8091          simply not covered here... :-/ ) */
8092
8093       CheckCollision[x][y] = 0;
8094       CheckImpact[x][y] = 0;
8095
8096       Impact(x, y);
8097     }
8098     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8099     {
8100       if (MovDir[x][y] == MV_NONE)
8101       {
8102         InitMovingField(x, y, MV_DOWN);
8103         started_moving = TRUE;
8104       }
8105     }
8106     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8107     {
8108       if (WasJustFalling[x][y]) // prevent animation from being restarted
8109         MovDir[x][y] = MV_DOWN;
8110
8111       InitMovingField(x, y, MV_DOWN);
8112       started_moving = TRUE;
8113     }
8114     else if (element == EL_AMOEBA_DROP)
8115     {
8116       Tile[x][y] = EL_AMOEBA_GROWING;
8117       Store[x][y] = EL_AMOEBA_WET;
8118     }
8119     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8120               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8121              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8122              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8123     {
8124       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8125                                 (IS_FREE(x - 1, y + 1) ||
8126                                  Tile[x - 1][y + 1] == EL_ACID));
8127       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8128                                 (IS_FREE(x + 1, y + 1) ||
8129                                  Tile[x + 1][y + 1] == EL_ACID));
8130       boolean can_fall_any  = (can_fall_left || can_fall_right);
8131       boolean can_fall_both = (can_fall_left && can_fall_right);
8132       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8133
8134       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8135       {
8136         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8137           can_fall_right = FALSE;
8138         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8139           can_fall_left = FALSE;
8140         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8141           can_fall_right = FALSE;
8142         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8143           can_fall_left = FALSE;
8144
8145         can_fall_any  = (can_fall_left || can_fall_right);
8146         can_fall_both = FALSE;
8147       }
8148
8149       if (can_fall_both)
8150       {
8151         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8152           can_fall_right = FALSE;       // slip down on left side
8153         else
8154           can_fall_left = !(can_fall_right = RND(2));
8155
8156         can_fall_both = FALSE;
8157       }
8158
8159       if (can_fall_any)
8160       {
8161         // if not determined otherwise, prefer left side for slipping down
8162         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8163         started_moving = TRUE;
8164       }
8165     }
8166     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8167     {
8168       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8169       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8170       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8171       int belt_dir = game.belt_dir[belt_nr];
8172
8173       if ((belt_dir == MV_LEFT  && left_is_free) ||
8174           (belt_dir == MV_RIGHT && right_is_free))
8175       {
8176         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8177
8178         InitMovingField(x, y, belt_dir);
8179         started_moving = TRUE;
8180
8181         Pushed[x][y] = TRUE;
8182         Pushed[nextx][y] = TRUE;
8183
8184         GfxAction[x][y] = ACTION_DEFAULT;
8185       }
8186       else
8187       {
8188         MovDir[x][y] = 0;       // if element was moving, stop it
8189       }
8190     }
8191   }
8192
8193   // not "else if" because of elements that can fall and move (EL_SPRING)
8194   if (CAN_MOVE(element) && !started_moving)
8195   {
8196     int move_pattern = element_info[element].move_pattern;
8197     int newx, newy;
8198
8199     Moving2Blocked(x, y, &newx, &newy);
8200
8201     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8202       return;
8203
8204     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8205         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8206     {
8207       WasJustMoving[x][y] = 0;
8208       CheckCollision[x][y] = 0;
8209
8210       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8211
8212       if (Tile[x][y] != element)        // element has changed
8213         return;
8214     }
8215
8216     if (!MovDelay[x][y])        // start new movement phase
8217     {
8218       // all objects that can change their move direction after each step
8219       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8220
8221       if (element != EL_YAMYAM &&
8222           element != EL_DARK_YAMYAM &&
8223           element != EL_PACMAN &&
8224           !(move_pattern & MV_ANY_DIRECTION) &&
8225           move_pattern != MV_TURNING_LEFT &&
8226           move_pattern != MV_TURNING_RIGHT &&
8227           move_pattern != MV_TURNING_LEFT_RIGHT &&
8228           move_pattern != MV_TURNING_RIGHT_LEFT &&
8229           move_pattern != MV_TURNING_RANDOM)
8230       {
8231         TurnRound(x, y);
8232
8233         if (MovDelay[x][y] && (element == EL_BUG ||
8234                                element == EL_SPACESHIP ||
8235                                element == EL_SP_SNIKSNAK ||
8236                                element == EL_SP_ELECTRON ||
8237                                element == EL_MOLE))
8238           TEST_DrawLevelField(x, y);
8239       }
8240     }
8241
8242     if (MovDelay[x][y])         // wait some time before next movement
8243     {
8244       MovDelay[x][y]--;
8245
8246       if (element == EL_ROBOT ||
8247           element == EL_YAMYAM ||
8248           element == EL_DARK_YAMYAM)
8249       {
8250         DrawLevelElementAnimationIfNeeded(x, y, element);
8251         PlayLevelSoundAction(x, y, ACTION_WAITING);
8252       }
8253       else if (element == EL_SP_ELECTRON)
8254         DrawLevelElementAnimationIfNeeded(x, y, element);
8255       else if (element == EL_DRAGON)
8256       {
8257         int i;
8258         int dir = MovDir[x][y];
8259         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8260         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8261         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8262                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8263                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8264                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8265         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8266
8267         GfxAction[x][y] = ACTION_ATTACKING;
8268
8269         if (IS_PLAYER(x, y))
8270           DrawPlayerField(x, y);
8271         else
8272           TEST_DrawLevelField(x, y);
8273
8274         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8275
8276         for (i = 1; i <= 3; i++)
8277         {
8278           int xx = x + i * dx;
8279           int yy = y + i * dy;
8280           int sx = SCREENX(xx);
8281           int sy = SCREENY(yy);
8282           int flame_graphic = graphic + (i - 1);
8283
8284           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8285             break;
8286
8287           if (MovDelay[x][y])
8288           {
8289             int flamed = MovingOrBlocked2Element(xx, yy);
8290
8291             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8292               Bang(xx, yy);
8293             else
8294               RemoveMovingField(xx, yy);
8295
8296             ChangeDelay[xx][yy] = 0;
8297
8298             Tile[xx][yy] = EL_FLAMES;
8299
8300             if (IN_SCR_FIELD(sx, sy))
8301             {
8302               TEST_DrawLevelFieldCrumbled(xx, yy);
8303               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8304             }
8305           }
8306           else
8307           {
8308             if (Tile[xx][yy] == EL_FLAMES)
8309               Tile[xx][yy] = EL_EMPTY;
8310             TEST_DrawLevelField(xx, yy);
8311           }
8312         }
8313       }
8314
8315       if (MovDelay[x][y])       // element still has to wait some time
8316       {
8317         PlayLevelSoundAction(x, y, ACTION_WAITING);
8318
8319         return;
8320       }
8321     }
8322
8323     // now make next step
8324
8325     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8326
8327     if (DONT_COLLIDE_WITH(element) &&
8328         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8329         !PLAYER_ENEMY_PROTECTED(newx, newy))
8330     {
8331       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8332
8333       return;
8334     }
8335
8336     else if (CAN_MOVE_INTO_ACID(element) &&
8337              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8338              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8339              (MovDir[x][y] == MV_DOWN ||
8340               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8341     {
8342       SplashAcid(newx, newy);
8343       Store[x][y] = EL_ACID;
8344     }
8345     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8346     {
8347       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8348           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8349           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8350           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8351       {
8352         RemoveField(x, y);
8353         TEST_DrawLevelField(x, y);
8354
8355         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8356         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8357           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8358
8359         game.friends_still_needed--;
8360         if (!game.friends_still_needed &&
8361             !game.GameOver &&
8362             game.all_players_gone)
8363           LevelSolved();
8364
8365         return;
8366       }
8367       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8368       {
8369         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8370           TEST_DrawLevelField(newx, newy);
8371         else
8372           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8373       }
8374       else if (!IS_FREE(newx, newy))
8375       {
8376         GfxAction[x][y] = ACTION_WAITING;
8377
8378         if (IS_PLAYER(x, y))
8379           DrawPlayerField(x, y);
8380         else
8381           TEST_DrawLevelField(x, y);
8382
8383         return;
8384       }
8385     }
8386     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8387     {
8388       if (IS_FOOD_PIG(Tile[newx][newy]))
8389       {
8390         if (IS_MOVING(newx, newy))
8391           RemoveMovingField(newx, newy);
8392         else
8393         {
8394           Tile[newx][newy] = EL_EMPTY;
8395           TEST_DrawLevelField(newx, newy);
8396         }
8397
8398         PlayLevelSound(x, y, SND_PIG_DIGGING);
8399       }
8400       else if (!IS_FREE(newx, newy))
8401       {
8402         if (IS_PLAYER(x, y))
8403           DrawPlayerField(x, y);
8404         else
8405           TEST_DrawLevelField(x, y);
8406
8407         return;
8408       }
8409     }
8410     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8411     {
8412       if (Store[x][y] != EL_EMPTY)
8413       {
8414         boolean can_clone = FALSE;
8415         int xx, yy;
8416
8417         // check if element to clone is still there
8418         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8419         {
8420           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8421           {
8422             can_clone = TRUE;
8423
8424             break;
8425           }
8426         }
8427
8428         // cannot clone or target field not free anymore -- do not clone
8429         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8430           Store[x][y] = EL_EMPTY;
8431       }
8432
8433       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8434       {
8435         if (IS_MV_DIAGONAL(MovDir[x][y]))
8436         {
8437           int diagonal_move_dir = MovDir[x][y];
8438           int stored = Store[x][y];
8439           int change_delay = 8;
8440           int graphic;
8441
8442           // android is moving diagonally
8443
8444           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8445
8446           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8447           GfxElement[x][y] = EL_EMC_ANDROID;
8448           GfxAction[x][y] = ACTION_SHRINKING;
8449           GfxDir[x][y] = diagonal_move_dir;
8450           ChangeDelay[x][y] = change_delay;
8451
8452           if (Store[x][y] == EL_EMPTY)
8453             Store[x][y] = GfxElementEmpty[x][y];
8454
8455           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8456                                    GfxDir[x][y]);
8457
8458           DrawLevelGraphicAnimation(x, y, graphic);
8459           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8460
8461           if (Tile[newx][newy] == EL_ACID)
8462           {
8463             SplashAcid(newx, newy);
8464
8465             return;
8466           }
8467
8468           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8469
8470           Store[newx][newy] = EL_EMC_ANDROID;
8471           GfxElement[newx][newy] = EL_EMC_ANDROID;
8472           GfxAction[newx][newy] = ACTION_GROWING;
8473           GfxDir[newx][newy] = diagonal_move_dir;
8474           ChangeDelay[newx][newy] = change_delay;
8475
8476           graphic = el_act_dir2img(GfxElement[newx][newy],
8477                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8478
8479           DrawLevelGraphicAnimation(newx, newy, graphic);
8480           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8481
8482           return;
8483         }
8484         else
8485         {
8486           Tile[newx][newy] = EL_EMPTY;
8487           TEST_DrawLevelField(newx, newy);
8488
8489           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8490         }
8491       }
8492       else if (!IS_FREE(newx, newy))
8493       {
8494         return;
8495       }
8496     }
8497     else if (IS_CUSTOM_ELEMENT(element) &&
8498              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8499     {
8500       if (!DigFieldByCE(newx, newy, element))
8501         return;
8502
8503       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8504       {
8505         RunnerVisit[x][y] = FrameCounter;
8506         PlayerVisit[x][y] /= 8;         // expire player visit path
8507       }
8508     }
8509     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8510     {
8511       if (!IS_FREE(newx, newy))
8512       {
8513         if (IS_PLAYER(x, y))
8514           DrawPlayerField(x, y);
8515         else
8516           TEST_DrawLevelField(x, y);
8517
8518         return;
8519       }
8520       else
8521       {
8522         boolean wanna_flame = !RND(10);
8523         int dx = newx - x, dy = newy - y;
8524         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8525         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8526         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8527                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8528         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8529                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8530
8531         if ((wanna_flame ||
8532              IS_CLASSIC_ENEMY(element1) ||
8533              IS_CLASSIC_ENEMY(element2)) &&
8534             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8535             element1 != EL_FLAMES && element2 != EL_FLAMES)
8536         {
8537           ResetGfxAnimation(x, y);
8538           GfxAction[x][y] = ACTION_ATTACKING;
8539
8540           if (IS_PLAYER(x, y))
8541             DrawPlayerField(x, y);
8542           else
8543             TEST_DrawLevelField(x, y);
8544
8545           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8546
8547           MovDelay[x][y] = 50;
8548
8549           Tile[newx][newy] = EL_FLAMES;
8550           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8551             Tile[newx1][newy1] = EL_FLAMES;
8552           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8553             Tile[newx2][newy2] = EL_FLAMES;
8554
8555           return;
8556         }
8557       }
8558     }
8559     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8560              Tile[newx][newy] == EL_DIAMOND)
8561     {
8562       if (IS_MOVING(newx, newy))
8563         RemoveMovingField(newx, newy);
8564       else
8565       {
8566         Tile[newx][newy] = EL_EMPTY;
8567         TEST_DrawLevelField(newx, newy);
8568       }
8569
8570       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8571     }
8572     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8573              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8574     {
8575       if (AmoebaNr[newx][newy])
8576       {
8577         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8578         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8579             Tile[newx][newy] == EL_BD_AMOEBA)
8580           AmoebaCnt[AmoebaNr[newx][newy]]--;
8581       }
8582
8583       if (IS_MOVING(newx, newy))
8584       {
8585         RemoveMovingField(newx, newy);
8586       }
8587       else
8588       {
8589         Tile[newx][newy] = EL_EMPTY;
8590         TEST_DrawLevelField(newx, newy);
8591       }
8592
8593       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8594     }
8595     else if ((element == EL_PACMAN || element == EL_MOLE)
8596              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8597     {
8598       if (AmoebaNr[newx][newy])
8599       {
8600         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8601         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8602             Tile[newx][newy] == EL_BD_AMOEBA)
8603           AmoebaCnt[AmoebaNr[newx][newy]]--;
8604       }
8605
8606       if (element == EL_MOLE)
8607       {
8608         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8609         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8610
8611         ResetGfxAnimation(x, y);
8612         GfxAction[x][y] = ACTION_DIGGING;
8613         TEST_DrawLevelField(x, y);
8614
8615         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8616
8617         return;                         // wait for shrinking amoeba
8618       }
8619       else      // element == EL_PACMAN
8620       {
8621         Tile[newx][newy] = EL_EMPTY;
8622         TEST_DrawLevelField(newx, newy);
8623         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8624       }
8625     }
8626     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8627              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8628               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8629     {
8630       // wait for shrinking amoeba to completely disappear
8631       return;
8632     }
8633     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8634     {
8635       // object was running against a wall
8636
8637       TurnRound(x, y);
8638
8639       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8640         DrawLevelElementAnimation(x, y, element);
8641
8642       if (DONT_TOUCH(element))
8643         TestIfBadThingTouchesPlayer(x, y);
8644
8645       return;
8646     }
8647
8648     InitMovingField(x, y, MovDir[x][y]);
8649
8650     PlayLevelSoundAction(x, y, ACTION_MOVING);
8651   }
8652
8653   if (MovDir[x][y])
8654     ContinueMoving(x, y);
8655 }
8656
8657 void ContinueMoving(int x, int y)
8658 {
8659   int element = Tile[x][y];
8660   struct ElementInfo *ei = &element_info[element];
8661   int direction = MovDir[x][y];
8662   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8663   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8664   int newx = x + dx, newy = y + dy;
8665   int stored = Store[x][y];
8666   int stored_new = Store[newx][newy];
8667   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8668   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8669   boolean last_line = (newy == lev_fieldy - 1);
8670   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8671
8672   if (pushed_by_player)         // special case: moving object pushed by player
8673   {
8674     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8675   }
8676   else if (use_step_delay)      // special case: moving object has step delay
8677   {
8678     if (!MovDelay[x][y])
8679       MovPos[x][y] += getElementMoveStepsize(x, y);
8680
8681     if (MovDelay[x][y])
8682       MovDelay[x][y]--;
8683     else
8684       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8685
8686     if (MovDelay[x][y])
8687     {
8688       TEST_DrawLevelField(x, y);
8689
8690       return;   // element is still waiting
8691     }
8692   }
8693   else                          // normal case: generically moving object
8694   {
8695     MovPos[x][y] += getElementMoveStepsize(x, y);
8696   }
8697
8698   if (ABS(MovPos[x][y]) < TILEX)
8699   {
8700     TEST_DrawLevelField(x, y);
8701
8702     return;     // element is still moving
8703   }
8704
8705   // element reached destination field
8706
8707   Tile[x][y] = EL_EMPTY;
8708   Tile[newx][newy] = element;
8709   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8710
8711   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8712   {
8713     element = Tile[newx][newy] = EL_ACID;
8714   }
8715   else if (element == EL_MOLE)
8716   {
8717     Tile[x][y] = EL_SAND;
8718
8719     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8720   }
8721   else if (element == EL_QUICKSAND_FILLING)
8722   {
8723     element = Tile[newx][newy] = get_next_element(element);
8724     Store[newx][newy] = Store[x][y];
8725   }
8726   else if (element == EL_QUICKSAND_EMPTYING)
8727   {
8728     Tile[x][y] = get_next_element(element);
8729     element = Tile[newx][newy] = Store[x][y];
8730   }
8731   else if (element == EL_QUICKSAND_FAST_FILLING)
8732   {
8733     element = Tile[newx][newy] = get_next_element(element);
8734     Store[newx][newy] = Store[x][y];
8735   }
8736   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8737   {
8738     Tile[x][y] = get_next_element(element);
8739     element = Tile[newx][newy] = Store[x][y];
8740   }
8741   else if (element == EL_MAGIC_WALL_FILLING)
8742   {
8743     element = Tile[newx][newy] = get_next_element(element);
8744     if (!game.magic_wall_active)
8745       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8746     Store[newx][newy] = Store[x][y];
8747   }
8748   else if (element == EL_MAGIC_WALL_EMPTYING)
8749   {
8750     Tile[x][y] = get_next_element(element);
8751     if (!game.magic_wall_active)
8752       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8753     element = Tile[newx][newy] = Store[x][y];
8754
8755     InitField(newx, newy, FALSE);
8756   }
8757   else if (element == EL_BD_MAGIC_WALL_FILLING)
8758   {
8759     element = Tile[newx][newy] = get_next_element(element);
8760     if (!game.magic_wall_active)
8761       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8762     Store[newx][newy] = Store[x][y];
8763   }
8764   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8765   {
8766     Tile[x][y] = get_next_element(element);
8767     if (!game.magic_wall_active)
8768       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8769     element = Tile[newx][newy] = Store[x][y];
8770
8771     InitField(newx, newy, FALSE);
8772   }
8773   else if (element == EL_DC_MAGIC_WALL_FILLING)
8774   {
8775     element = Tile[newx][newy] = get_next_element(element);
8776     if (!game.magic_wall_active)
8777       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8778     Store[newx][newy] = Store[x][y];
8779   }
8780   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8781   {
8782     Tile[x][y] = get_next_element(element);
8783     if (!game.magic_wall_active)
8784       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8785     element = Tile[newx][newy] = Store[x][y];
8786
8787     InitField(newx, newy, FALSE);
8788   }
8789   else if (element == EL_AMOEBA_DROPPING)
8790   {
8791     Tile[x][y] = get_next_element(element);
8792     element = Tile[newx][newy] = Store[x][y];
8793   }
8794   else if (element == EL_SOKOBAN_OBJECT)
8795   {
8796     if (Back[x][y])
8797       Tile[x][y] = Back[x][y];
8798
8799     if (Back[newx][newy])
8800       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8801
8802     Back[x][y] = Back[newx][newy] = 0;
8803   }
8804
8805   Store[x][y] = EL_EMPTY;
8806   MovPos[x][y] = 0;
8807   MovDir[x][y] = 0;
8808   MovDelay[x][y] = 0;
8809
8810   MovDelay[newx][newy] = 0;
8811
8812   if (CAN_CHANGE_OR_HAS_ACTION(element))
8813   {
8814     // copy element change control values to new field
8815     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8816     ChangePage[newx][newy]  = ChangePage[x][y];
8817     ChangeCount[newx][newy] = ChangeCount[x][y];
8818     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8819   }
8820
8821   CustomValue[newx][newy] = CustomValue[x][y];
8822
8823   ChangeDelay[x][y] = 0;
8824   ChangePage[x][y] = -1;
8825   ChangeCount[x][y] = 0;
8826   ChangeEvent[x][y] = -1;
8827
8828   CustomValue[x][y] = 0;
8829
8830   // copy animation control values to new field
8831   GfxFrame[newx][newy]  = GfxFrame[x][y];
8832   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8833   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8834   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8835
8836   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8837
8838   // some elements can leave other elements behind after moving
8839   if (ei->move_leave_element != EL_EMPTY &&
8840       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8841       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8842   {
8843     int move_leave_element = ei->move_leave_element;
8844
8845     // this makes it possible to leave the removed element again
8846     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8847       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8848
8849     Tile[x][y] = move_leave_element;
8850
8851     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8852       MovDir[x][y] = direction;
8853
8854     InitField(x, y, FALSE);
8855
8856     if (GFX_CRUMBLED(Tile[x][y]))
8857       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8858
8859     if (IS_PLAYER_ELEMENT(move_leave_element))
8860       RelocatePlayer(x, y, move_leave_element);
8861   }
8862
8863   // do this after checking for left-behind element
8864   ResetGfxAnimation(x, y);      // reset animation values for old field
8865
8866   if (!CAN_MOVE(element) ||
8867       (CAN_FALL(element) && direction == MV_DOWN &&
8868        (element == EL_SPRING ||
8869         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8870         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8871     GfxDir[x][y] = MovDir[newx][newy] = 0;
8872
8873   TEST_DrawLevelField(x, y);
8874   TEST_DrawLevelField(newx, newy);
8875
8876   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8877
8878   // prevent pushed element from moving on in pushed direction
8879   if (pushed_by_player && CAN_MOVE(element) &&
8880       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8881       !(element_info[element].move_pattern & direction))
8882     TurnRound(newx, newy);
8883
8884   // prevent elements on conveyor belt from moving on in last direction
8885   if (pushed_by_conveyor && CAN_FALL(element) &&
8886       direction & MV_HORIZONTAL)
8887     MovDir[newx][newy] = 0;
8888
8889   if (!pushed_by_player)
8890   {
8891     int nextx = newx + dx, nexty = newy + dy;
8892     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8893
8894     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8895
8896     if (CAN_FALL(element) && direction == MV_DOWN)
8897       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8898
8899     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8900       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8901
8902     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8903       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8904   }
8905
8906   if (DONT_TOUCH(element))      // object may be nasty to player or others
8907   {
8908     TestIfBadThingTouchesPlayer(newx, newy);
8909     TestIfBadThingTouchesFriend(newx, newy);
8910
8911     if (!IS_CUSTOM_ELEMENT(element))
8912       TestIfBadThingTouchesOtherBadThing(newx, newy);
8913   }
8914   else if (element == EL_PENGUIN)
8915     TestIfFriendTouchesBadThing(newx, newy);
8916
8917   if (DONT_GET_HIT_BY(element))
8918   {
8919     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8920   }
8921
8922   // give the player one last chance (one more frame) to move away
8923   if (CAN_FALL(element) && direction == MV_DOWN &&
8924       (last_line || (!IS_FREE(x, newy + 1) &&
8925                      (!IS_PLAYER(x, newy + 1) ||
8926                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8927     Impact(x, newy);
8928
8929   if (pushed_by_player && !game.use_change_when_pushing_bug)
8930   {
8931     int push_side = MV_DIR_OPPOSITE(direction);
8932     struct PlayerInfo *player = PLAYERINFO(x, y);
8933
8934     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8935                                player->index_bit, push_side);
8936     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8937                                         player->index_bit, push_side);
8938   }
8939
8940   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8941     MovDelay[newx][newy] = 1;
8942
8943   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8944
8945   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8946   TestIfElementHitsCustomElement(newx, newy, direction);
8947   TestIfPlayerTouchesCustomElement(newx, newy);
8948   TestIfElementTouchesCustomElement(newx, newy);
8949
8950   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8951       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8952     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8953                              MV_DIR_OPPOSITE(direction));
8954 }
8955
8956 int AmoebaNeighbourNr(int ax, int ay)
8957 {
8958   int i;
8959   int element = Tile[ax][ay];
8960   int group_nr = 0;
8961   struct XY *xy = xy_topdown;
8962
8963   for (i = 0; i < NUM_DIRECTIONS; i++)
8964   {
8965     int x = ax + xy[i].x;
8966     int y = ay + xy[i].y;
8967
8968     if (!IN_LEV_FIELD(x, y))
8969       continue;
8970
8971     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8972       group_nr = AmoebaNr[x][y];
8973   }
8974
8975   return group_nr;
8976 }
8977
8978 static void AmoebaMerge(int ax, int ay)
8979 {
8980   int i, x, y, xx, yy;
8981   int new_group_nr = AmoebaNr[ax][ay];
8982   struct XY *xy = xy_topdown;
8983
8984   if (new_group_nr == 0)
8985     return;
8986
8987   for (i = 0; i < NUM_DIRECTIONS; i++)
8988   {
8989     x = ax + xy[i].x;
8990     y = ay + xy[i].y;
8991
8992     if (!IN_LEV_FIELD(x, y))
8993       continue;
8994
8995     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8996          Tile[x][y] == EL_BD_AMOEBA ||
8997          Tile[x][y] == EL_AMOEBA_DEAD) &&
8998         AmoebaNr[x][y] != new_group_nr)
8999     {
9000       int old_group_nr = AmoebaNr[x][y];
9001
9002       if (old_group_nr == 0)
9003         return;
9004
9005       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9006       AmoebaCnt[old_group_nr] = 0;
9007       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9008       AmoebaCnt2[old_group_nr] = 0;
9009
9010       SCAN_PLAYFIELD(xx, yy)
9011       {
9012         if (AmoebaNr[xx][yy] == old_group_nr)
9013           AmoebaNr[xx][yy] = new_group_nr;
9014       }
9015     }
9016   }
9017 }
9018
9019 void AmoebaToDiamond(int ax, int ay)
9020 {
9021   int i, x, y;
9022
9023   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9024   {
9025     int group_nr = AmoebaNr[ax][ay];
9026
9027 #ifdef DEBUG
9028     if (group_nr == 0)
9029     {
9030       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9031       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9032
9033       return;
9034     }
9035 #endif
9036
9037     SCAN_PLAYFIELD(x, y)
9038     {
9039       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9040       {
9041         AmoebaNr[x][y] = 0;
9042         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9043       }
9044     }
9045
9046     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9047                             SND_AMOEBA_TURNING_TO_GEM :
9048                             SND_AMOEBA_TURNING_TO_ROCK));
9049     Bang(ax, ay);
9050   }
9051   else
9052   {
9053     struct XY *xy = xy_topdown;
9054
9055     for (i = 0; i < NUM_DIRECTIONS; i++)
9056     {
9057       x = ax + xy[i].x;
9058       y = ay + xy[i].y;
9059
9060       if (!IN_LEV_FIELD(x, y))
9061         continue;
9062
9063       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9064       {
9065         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9066                               SND_AMOEBA_TURNING_TO_GEM :
9067                               SND_AMOEBA_TURNING_TO_ROCK));
9068         Bang(x, y);
9069       }
9070     }
9071   }
9072 }
9073
9074 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9075 {
9076   int x, y;
9077   int group_nr = AmoebaNr[ax][ay];
9078   boolean done = FALSE;
9079
9080 #ifdef DEBUG
9081   if (group_nr == 0)
9082   {
9083     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9084     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9085
9086     return;
9087   }
9088 #endif
9089
9090   SCAN_PLAYFIELD(x, y)
9091   {
9092     if (AmoebaNr[x][y] == group_nr &&
9093         (Tile[x][y] == EL_AMOEBA_DEAD ||
9094          Tile[x][y] == EL_BD_AMOEBA ||
9095          Tile[x][y] == EL_AMOEBA_GROWING))
9096     {
9097       AmoebaNr[x][y] = 0;
9098       Tile[x][y] = new_element;
9099       InitField(x, y, FALSE);
9100       TEST_DrawLevelField(x, y);
9101       done = TRUE;
9102     }
9103   }
9104
9105   if (done)
9106     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9107                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9108                             SND_BD_AMOEBA_TURNING_TO_GEM));
9109 }
9110
9111 static void AmoebaGrowing(int x, int y)
9112 {
9113   static DelayCounter sound_delay = { 0 };
9114
9115   if (!MovDelay[x][y])          // start new growing cycle
9116   {
9117     MovDelay[x][y] = 7;
9118
9119     if (DelayReached(&sound_delay))
9120     {
9121       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9122       sound_delay.value = 30;
9123     }
9124   }
9125
9126   if (MovDelay[x][y])           // wait some time before growing bigger
9127   {
9128     MovDelay[x][y]--;
9129     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9130     {
9131       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9132                                            6 - MovDelay[x][y]);
9133
9134       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9135     }
9136
9137     if (!MovDelay[x][y])
9138     {
9139       Tile[x][y] = Store[x][y];
9140       Store[x][y] = 0;
9141       TEST_DrawLevelField(x, y);
9142     }
9143   }
9144 }
9145
9146 static void AmoebaShrinking(int x, int y)
9147 {
9148   static DelayCounter sound_delay = { 0 };
9149
9150   if (!MovDelay[x][y])          // start new shrinking cycle
9151   {
9152     MovDelay[x][y] = 7;
9153
9154     if (DelayReached(&sound_delay))
9155       sound_delay.value = 30;
9156   }
9157
9158   if (MovDelay[x][y])           // wait some time before shrinking
9159   {
9160     MovDelay[x][y]--;
9161     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9162     {
9163       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9164                                            6 - MovDelay[x][y]);
9165
9166       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9167     }
9168
9169     if (!MovDelay[x][y])
9170     {
9171       Tile[x][y] = EL_EMPTY;
9172       TEST_DrawLevelField(x, y);
9173
9174       // don't let mole enter this field in this cycle;
9175       // (give priority to objects falling to this field from above)
9176       Stop[x][y] = TRUE;
9177     }
9178   }
9179 }
9180
9181 static void AmoebaReproduce(int ax, int ay)
9182 {
9183   int i;
9184   int element = Tile[ax][ay];
9185   int graphic = el2img(element);
9186   int newax = ax, neway = ay;
9187   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9188   struct XY *xy = xy_topdown;
9189
9190   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9191   {
9192     Tile[ax][ay] = EL_AMOEBA_DEAD;
9193     TEST_DrawLevelField(ax, ay);
9194     return;
9195   }
9196
9197   if (IS_ANIMATED(graphic))
9198     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9199
9200   if (!MovDelay[ax][ay])        // start making new amoeba field
9201     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9202
9203   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9204   {
9205     MovDelay[ax][ay]--;
9206     if (MovDelay[ax][ay])
9207       return;
9208   }
9209
9210   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9211   {
9212     int start = RND(4);
9213     int x = ax + xy[start].x;
9214     int y = ay + xy[start].y;
9215
9216     if (!IN_LEV_FIELD(x, y))
9217       return;
9218
9219     if (IS_FREE(x, y) ||
9220         CAN_GROW_INTO(Tile[x][y]) ||
9221         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9222         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9223     {
9224       newax = x;
9225       neway = y;
9226     }
9227
9228     if (newax == ax && neway == ay)
9229       return;
9230   }
9231   else                          // normal or "filled" (BD style) amoeba
9232   {
9233     int start = RND(4);
9234     boolean waiting_for_player = FALSE;
9235
9236     for (i = 0; i < NUM_DIRECTIONS; i++)
9237     {
9238       int j = (start + i) % 4;
9239       int x = ax + xy[j].x;
9240       int y = ay + xy[j].y;
9241
9242       if (!IN_LEV_FIELD(x, y))
9243         continue;
9244
9245       if (IS_FREE(x, y) ||
9246           CAN_GROW_INTO(Tile[x][y]) ||
9247           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9248           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9249       {
9250         newax = x;
9251         neway = y;
9252         break;
9253       }
9254       else if (IS_PLAYER(x, y))
9255         waiting_for_player = TRUE;
9256     }
9257
9258     if (newax == ax && neway == ay)             // amoeba cannot grow
9259     {
9260       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9261       {
9262         Tile[ax][ay] = EL_AMOEBA_DEAD;
9263         TEST_DrawLevelField(ax, ay);
9264         AmoebaCnt[AmoebaNr[ax][ay]]--;
9265
9266         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9267         {
9268           if (element == EL_AMOEBA_FULL)
9269             AmoebaToDiamond(ax, ay);
9270           else if (element == EL_BD_AMOEBA)
9271             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9272         }
9273       }
9274       return;
9275     }
9276     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9277     {
9278       // amoeba gets larger by growing in some direction
9279
9280       int new_group_nr = AmoebaNr[ax][ay];
9281
9282 #ifdef DEBUG
9283   if (new_group_nr == 0)
9284   {
9285     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9286           newax, neway);
9287     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9288
9289     return;
9290   }
9291 #endif
9292
9293       AmoebaNr[newax][neway] = new_group_nr;
9294       AmoebaCnt[new_group_nr]++;
9295       AmoebaCnt2[new_group_nr]++;
9296
9297       // if amoeba touches other amoeba(s) after growing, unify them
9298       AmoebaMerge(newax, neway);
9299
9300       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9301       {
9302         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9303         return;
9304       }
9305     }
9306   }
9307
9308   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9309       (neway == lev_fieldy - 1 && newax != ax))
9310   {
9311     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9312     Store[newax][neway] = element;
9313   }
9314   else if (neway == ay || element == EL_EMC_DRIPPER)
9315   {
9316     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9317
9318     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9319   }
9320   else
9321   {
9322     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9323     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9324     Store[ax][ay] = EL_AMOEBA_DROP;
9325     ContinueMoving(ax, ay);
9326     return;
9327   }
9328
9329   TEST_DrawLevelField(newax, neway);
9330 }
9331
9332 static void Life(int ax, int ay)
9333 {
9334   int x1, y1, x2, y2;
9335   int life_time = 40;
9336   int element = Tile[ax][ay];
9337   int graphic = el2img(element);
9338   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9339                          level.biomaze);
9340   boolean changed = FALSE;
9341
9342   if (IS_ANIMATED(graphic))
9343     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9344
9345   if (Stop[ax][ay])
9346     return;
9347
9348   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9349     MovDelay[ax][ay] = life_time;
9350
9351   if (MovDelay[ax][ay])         // wait some time before next cycle
9352   {
9353     MovDelay[ax][ay]--;
9354     if (MovDelay[ax][ay])
9355       return;
9356   }
9357
9358   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9359   {
9360     int xx = ax+x1, yy = ay+y1;
9361     int old_element = Tile[xx][yy];
9362     int num_neighbours = 0;
9363
9364     if (!IN_LEV_FIELD(xx, yy))
9365       continue;
9366
9367     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9368     {
9369       int x = xx+x2, y = yy+y2;
9370
9371       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9372         continue;
9373
9374       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9375       boolean is_neighbour = FALSE;
9376
9377       if (level.use_life_bugs)
9378         is_neighbour =
9379           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9380            (IS_FREE(x, y)                             &&  Stop[x][y]));
9381       else
9382         is_neighbour =
9383           (Last[x][y] == element || is_player_cell);
9384
9385       if (is_neighbour)
9386         num_neighbours++;
9387     }
9388
9389     boolean is_free = FALSE;
9390
9391     if (level.use_life_bugs)
9392       is_free = (IS_FREE(xx, yy));
9393     else
9394       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9395
9396     if (xx == ax && yy == ay)           // field in the middle
9397     {
9398       if (num_neighbours < life_parameter[0] ||
9399           num_neighbours > life_parameter[1])
9400       {
9401         Tile[xx][yy] = EL_EMPTY;
9402         if (Tile[xx][yy] != old_element)
9403           TEST_DrawLevelField(xx, yy);
9404         Stop[xx][yy] = TRUE;
9405         changed = TRUE;
9406       }
9407     }
9408     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9409     {                                   // free border field
9410       if (num_neighbours >= life_parameter[2] &&
9411           num_neighbours <= life_parameter[3])
9412       {
9413         Tile[xx][yy] = element;
9414         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9415         if (Tile[xx][yy] != old_element)
9416           TEST_DrawLevelField(xx, yy);
9417         Stop[xx][yy] = TRUE;
9418         changed = TRUE;
9419       }
9420     }
9421   }
9422
9423   if (changed)
9424     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9425                    SND_GAME_OF_LIFE_GROWING);
9426 }
9427
9428 static void InitRobotWheel(int x, int y)
9429 {
9430   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9431 }
9432
9433 static void RunRobotWheel(int x, int y)
9434 {
9435   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9436 }
9437
9438 static void StopRobotWheel(int x, int y)
9439 {
9440   if (game.robot_wheel_x == x &&
9441       game.robot_wheel_y == y)
9442   {
9443     game.robot_wheel_x = -1;
9444     game.robot_wheel_y = -1;
9445     game.robot_wheel_active = FALSE;
9446   }
9447 }
9448
9449 static void InitTimegateWheel(int x, int y)
9450 {
9451   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9452 }
9453
9454 static void RunTimegateWheel(int x, int y)
9455 {
9456   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9457 }
9458
9459 static void InitMagicBallDelay(int x, int y)
9460 {
9461   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9462 }
9463
9464 static void ActivateMagicBall(int bx, int by)
9465 {
9466   int x, y;
9467
9468   if (level.ball_random)
9469   {
9470     int pos_border = RND(8);    // select one of the eight border elements
9471     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9472     int xx = pos_content % 3;
9473     int yy = pos_content / 3;
9474
9475     x = bx - 1 + xx;
9476     y = by - 1 + yy;
9477
9478     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9479       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9480   }
9481   else
9482   {
9483     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9484     {
9485       int xx = x - bx + 1;
9486       int yy = y - by + 1;
9487
9488       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9489         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9490     }
9491   }
9492
9493   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9494 }
9495
9496 static void CheckExit(int x, int y)
9497 {
9498   if (game.gems_still_needed > 0 ||
9499       game.sokoban_fields_still_needed > 0 ||
9500       game.sokoban_objects_still_needed > 0 ||
9501       game.lights_still_needed > 0)
9502   {
9503     int element = Tile[x][y];
9504     int graphic = el2img(element);
9505
9506     if (IS_ANIMATED(graphic))
9507       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9508
9509     return;
9510   }
9511
9512   // do not re-open exit door closed after last player
9513   if (game.all_players_gone)
9514     return;
9515
9516   Tile[x][y] = EL_EXIT_OPENING;
9517
9518   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9519 }
9520
9521 static void CheckExitEM(int x, int y)
9522 {
9523   if (game.gems_still_needed > 0 ||
9524       game.sokoban_fields_still_needed > 0 ||
9525       game.sokoban_objects_still_needed > 0 ||
9526       game.lights_still_needed > 0)
9527   {
9528     int element = Tile[x][y];
9529     int graphic = el2img(element);
9530
9531     if (IS_ANIMATED(graphic))
9532       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9533
9534     return;
9535   }
9536
9537   // do not re-open exit door closed after last player
9538   if (game.all_players_gone)
9539     return;
9540
9541   Tile[x][y] = EL_EM_EXIT_OPENING;
9542
9543   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9544 }
9545
9546 static void CheckExitSteel(int x, int y)
9547 {
9548   if (game.gems_still_needed > 0 ||
9549       game.sokoban_fields_still_needed > 0 ||
9550       game.sokoban_objects_still_needed > 0 ||
9551       game.lights_still_needed > 0)
9552   {
9553     int element = Tile[x][y];
9554     int graphic = el2img(element);
9555
9556     if (IS_ANIMATED(graphic))
9557       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9558
9559     return;
9560   }
9561
9562   // do not re-open exit door closed after last player
9563   if (game.all_players_gone)
9564     return;
9565
9566   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9567
9568   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9569 }
9570
9571 static void CheckExitSteelEM(int x, int y)
9572 {
9573   if (game.gems_still_needed > 0 ||
9574       game.sokoban_fields_still_needed > 0 ||
9575       game.sokoban_objects_still_needed > 0 ||
9576       game.lights_still_needed > 0)
9577   {
9578     int element = Tile[x][y];
9579     int graphic = el2img(element);
9580
9581     if (IS_ANIMATED(graphic))
9582       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9583
9584     return;
9585   }
9586
9587   // do not re-open exit door closed after last player
9588   if (game.all_players_gone)
9589     return;
9590
9591   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9592
9593   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9594 }
9595
9596 static void CheckExitSP(int x, int y)
9597 {
9598   if (game.gems_still_needed > 0)
9599   {
9600     int element = Tile[x][y];
9601     int graphic = el2img(element);
9602
9603     if (IS_ANIMATED(graphic))
9604       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9605
9606     return;
9607   }
9608
9609   // do not re-open exit door closed after last player
9610   if (game.all_players_gone)
9611     return;
9612
9613   Tile[x][y] = EL_SP_EXIT_OPENING;
9614
9615   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9616 }
9617
9618 static void CloseAllOpenTimegates(void)
9619 {
9620   int x, y;
9621
9622   SCAN_PLAYFIELD(x, y)
9623   {
9624     int element = Tile[x][y];
9625
9626     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9627     {
9628       Tile[x][y] = EL_TIMEGATE_CLOSING;
9629
9630       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9631     }
9632   }
9633 }
9634
9635 static void DrawTwinkleOnField(int x, int y)
9636 {
9637   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9638     return;
9639
9640   if (Tile[x][y] == EL_BD_DIAMOND)
9641     return;
9642
9643   if (MovDelay[x][y] == 0)      // next animation frame
9644     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9645
9646   if (MovDelay[x][y] != 0)      // wait some time before next frame
9647   {
9648     MovDelay[x][y]--;
9649
9650     DrawLevelElementAnimation(x, y, Tile[x][y]);
9651
9652     if (MovDelay[x][y] != 0)
9653     {
9654       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9655                                            10 - MovDelay[x][y]);
9656
9657       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9658     }
9659   }
9660 }
9661
9662 static void WallGrowing(int x, int y)
9663 {
9664   int delay = 6;
9665
9666   if (!MovDelay[x][y])          // next animation frame
9667     MovDelay[x][y] = 3 * delay;
9668
9669   if (MovDelay[x][y])           // wait some time before next frame
9670   {
9671     MovDelay[x][y]--;
9672
9673     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9674     {
9675       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9676       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9677
9678       DrawLevelGraphic(x, y, graphic, frame);
9679     }
9680
9681     if (!MovDelay[x][y])
9682     {
9683       if (MovDir[x][y] == MV_LEFT)
9684       {
9685         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9686           TEST_DrawLevelField(x - 1, y);
9687       }
9688       else if (MovDir[x][y] == MV_RIGHT)
9689       {
9690         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9691           TEST_DrawLevelField(x + 1, y);
9692       }
9693       else if (MovDir[x][y] == MV_UP)
9694       {
9695         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9696           TEST_DrawLevelField(x, y - 1);
9697       }
9698       else
9699       {
9700         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9701           TEST_DrawLevelField(x, y + 1);
9702       }
9703
9704       Tile[x][y] = Store[x][y];
9705       Store[x][y] = 0;
9706       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9707       TEST_DrawLevelField(x, y);
9708     }
9709   }
9710 }
9711
9712 static void CheckWallGrowing(int ax, int ay)
9713 {
9714   int element = Tile[ax][ay];
9715   int graphic = el2img(element);
9716   boolean free_top    = FALSE;
9717   boolean free_bottom = FALSE;
9718   boolean free_left   = FALSE;
9719   boolean free_right  = FALSE;
9720   boolean stop_top    = FALSE;
9721   boolean stop_bottom = FALSE;
9722   boolean stop_left   = FALSE;
9723   boolean stop_right  = FALSE;
9724   boolean new_wall    = FALSE;
9725
9726   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9727                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9728                            element == EL_EXPANDABLE_STEELWALL_ANY);
9729
9730   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9731                              element == EL_EXPANDABLE_WALL_ANY ||
9732                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9733                              element == EL_EXPANDABLE_STEELWALL_ANY);
9734
9735   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9736                              element == EL_EXPANDABLE_WALL_ANY ||
9737                              element == EL_EXPANDABLE_WALL ||
9738                              element == EL_BD_EXPANDABLE_WALL ||
9739                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9740                              element == EL_EXPANDABLE_STEELWALL_ANY);
9741
9742   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9743                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9744
9745   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9746                              element == EL_EXPANDABLE_WALL ||
9747                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9748
9749   int wall_growing = (is_steelwall ?
9750                       EL_EXPANDABLE_STEELWALL_GROWING :
9751                       EL_EXPANDABLE_WALL_GROWING);
9752
9753   int gfx_wall_growing_up    = (is_steelwall ?
9754                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9755                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9756   int gfx_wall_growing_down  = (is_steelwall ?
9757                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9758                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9759   int gfx_wall_growing_left  = (is_steelwall ?
9760                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9761                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9762   int gfx_wall_growing_right = (is_steelwall ?
9763                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9764                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9765
9766   if (IS_ANIMATED(graphic))
9767     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9768
9769   if (!MovDelay[ax][ay])        // start building new wall
9770     MovDelay[ax][ay] = 6;
9771
9772   if (MovDelay[ax][ay])         // wait some time before building new wall
9773   {
9774     MovDelay[ax][ay]--;
9775     if (MovDelay[ax][ay])
9776       return;
9777   }
9778
9779   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9780     free_top = TRUE;
9781   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9782     free_bottom = TRUE;
9783   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9784     free_left = TRUE;
9785   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9786     free_right = TRUE;
9787
9788   if (grow_vertical)
9789   {
9790     if (free_top)
9791     {
9792       Tile[ax][ay - 1] = wall_growing;
9793       Store[ax][ay - 1] = element;
9794       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9795
9796       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9797         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9798
9799       new_wall = TRUE;
9800     }
9801
9802     if (free_bottom)
9803     {
9804       Tile[ax][ay + 1] = wall_growing;
9805       Store[ax][ay + 1] = element;
9806       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9807
9808       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9809         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9810
9811       new_wall = TRUE;
9812     }
9813   }
9814
9815   if (grow_horizontal)
9816   {
9817     if (free_left)
9818     {
9819       Tile[ax - 1][ay] = wall_growing;
9820       Store[ax - 1][ay] = element;
9821       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9822
9823       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9824         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9825
9826       new_wall = TRUE;
9827     }
9828
9829     if (free_right)
9830     {
9831       Tile[ax + 1][ay] = wall_growing;
9832       Store[ax + 1][ay] = element;
9833       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9834
9835       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9836         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9837
9838       new_wall = TRUE;
9839     }
9840   }
9841
9842   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9843     TEST_DrawLevelField(ax, ay);
9844
9845   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9846     stop_top = TRUE;
9847   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9848     stop_bottom = TRUE;
9849   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9850     stop_left = TRUE;
9851   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9852     stop_right = TRUE;
9853
9854   if (((stop_top && stop_bottom) || stop_horizontal) &&
9855       ((stop_left && stop_right) || stop_vertical))
9856     Tile[ax][ay] = EL_WALL;
9857
9858   if (new_wall)
9859     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9860 }
9861
9862 static void CheckForDragon(int x, int y)
9863 {
9864   int i, j;
9865   boolean dragon_found = FALSE;
9866   struct XY *xy = xy_topdown;
9867
9868   for (i = 0; i < NUM_DIRECTIONS; i++)
9869   {
9870     for (j = 0; j < 4; j++)
9871     {
9872       int xx = x + j * xy[i].x;
9873       int yy = y + j * xy[i].y;
9874
9875       if (IN_LEV_FIELD(xx, yy) &&
9876           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9877       {
9878         if (Tile[xx][yy] == EL_DRAGON)
9879           dragon_found = TRUE;
9880       }
9881       else
9882         break;
9883     }
9884   }
9885
9886   if (!dragon_found)
9887   {
9888     for (i = 0; i < NUM_DIRECTIONS; i++)
9889     {
9890       for (j = 0; j < 3; j++)
9891       {
9892         int xx = x + j * xy[i].x;
9893         int yy = y + j * xy[i].y;
9894
9895         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9896         {
9897           Tile[xx][yy] = EL_EMPTY;
9898           TEST_DrawLevelField(xx, yy);
9899         }
9900         else
9901           break;
9902       }
9903     }
9904   }
9905 }
9906
9907 static void InitBuggyBase(int x, int y)
9908 {
9909   int element = Tile[x][y];
9910   int activating_delay = FRAMES_PER_SECOND / 4;
9911
9912   ChangeDelay[x][y] =
9913     (element == EL_SP_BUGGY_BASE ?
9914      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9915      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9916      activating_delay :
9917      element == EL_SP_BUGGY_BASE_ACTIVE ?
9918      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9919 }
9920
9921 static void WarnBuggyBase(int x, int y)
9922 {
9923   int i;
9924   struct XY *xy = xy_topdown;
9925
9926   for (i = 0; i < NUM_DIRECTIONS; i++)
9927   {
9928     int xx = x + xy[i].x;
9929     int yy = y + xy[i].y;
9930
9931     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9932     {
9933       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9934
9935       break;
9936     }
9937   }
9938 }
9939
9940 static void InitTrap(int x, int y)
9941 {
9942   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9943 }
9944
9945 static void ActivateTrap(int x, int y)
9946 {
9947   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9948 }
9949
9950 static void ChangeActiveTrap(int x, int y)
9951 {
9952   int graphic = IMG_TRAP_ACTIVE;
9953
9954   // if new animation frame was drawn, correct crumbled sand border
9955   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9956     TEST_DrawLevelFieldCrumbled(x, y);
9957 }
9958
9959 static int getSpecialActionElement(int element, int number, int base_element)
9960 {
9961   return (element != EL_EMPTY ? element :
9962           number != -1 ? base_element + number - 1 :
9963           EL_EMPTY);
9964 }
9965
9966 static int getModifiedActionNumber(int value_old, int operator, int operand,
9967                                    int value_min, int value_max)
9968 {
9969   int value_new = (operator == CA_MODE_SET      ? operand :
9970                    operator == CA_MODE_ADD      ? value_old + operand :
9971                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9972                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9973                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9974                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9975                    value_old);
9976
9977   return (value_new < value_min ? value_min :
9978           value_new > value_max ? value_max :
9979           value_new);
9980 }
9981
9982 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9983 {
9984   struct ElementInfo *ei = &element_info[element];
9985   struct ElementChangeInfo *change = &ei->change_page[page];
9986   int target_element = change->target_element;
9987   int action_type = change->action_type;
9988   int action_mode = change->action_mode;
9989   int action_arg = change->action_arg;
9990   int action_element = change->action_element;
9991   int i;
9992
9993   if (!change->has_action)
9994     return;
9995
9996   // ---------- determine action paramater values -----------------------------
9997
9998   int level_time_value =
9999     (level.time > 0 ? TimeLeft :
10000      TimePlayed);
10001
10002   int action_arg_element_raw =
10003     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10004      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10005      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10006      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10007      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10008      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10009      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10010      EL_EMPTY);
10011   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10012
10013   int action_arg_direction =
10014     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10015      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10016      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10017      change->actual_trigger_side :
10018      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10019      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10020      MV_NONE);
10021
10022   int action_arg_number_min =
10023     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10024      CA_ARG_MIN);
10025
10026   int action_arg_number_max =
10027     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10028      action_type == CA_SET_LEVEL_GEMS ? 999 :
10029      action_type == CA_SET_LEVEL_TIME ? 9999 :
10030      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10031      action_type == CA_SET_CE_VALUE ? 9999 :
10032      action_type == CA_SET_CE_SCORE ? 9999 :
10033      CA_ARG_MAX);
10034
10035   int action_arg_number_reset =
10036     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10037      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10038      action_type == CA_SET_LEVEL_TIME ? level.time :
10039      action_type == CA_SET_LEVEL_SCORE ? 0 :
10040      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10041      action_type == CA_SET_CE_SCORE ? 0 :
10042      0);
10043
10044   int action_arg_number =
10045     (action_arg <= CA_ARG_MAX ? action_arg :
10046      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10047      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10048      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10049      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10050      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10051      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10052      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10053      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10054      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10055      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10056      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10057      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10058      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10059      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10060      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10061      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10062      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10063      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10064      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10065      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10066      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10067      -1);
10068
10069   int action_arg_number_old =
10070     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10071      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10072      action_type == CA_SET_LEVEL_SCORE ? game.score :
10073      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10074      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10075      0);
10076
10077   int action_arg_number_new =
10078     getModifiedActionNumber(action_arg_number_old,
10079                             action_mode, action_arg_number,
10080                             action_arg_number_min, action_arg_number_max);
10081
10082   int trigger_player_bits =
10083     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10084      change->actual_trigger_player_bits : change->trigger_player);
10085
10086   int action_arg_player_bits =
10087     (action_arg >= CA_ARG_PLAYER_1 &&
10088      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10089      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10090      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10091      PLAYER_BITS_ANY);
10092
10093   // ---------- execute action  -----------------------------------------------
10094
10095   switch (action_type)
10096   {
10097     case CA_NO_ACTION:
10098     {
10099       return;
10100     }
10101
10102     // ---------- level actions  ----------------------------------------------
10103
10104     case CA_RESTART_LEVEL:
10105     {
10106       game.restart_level = TRUE;
10107
10108       break;
10109     }
10110
10111     case CA_SHOW_ENVELOPE:
10112     {
10113       int element = getSpecialActionElement(action_arg_element,
10114                                             action_arg_number, EL_ENVELOPE_1);
10115
10116       if (IS_ENVELOPE(element))
10117         local_player->show_envelope = element;
10118
10119       break;
10120     }
10121
10122     case CA_SET_LEVEL_TIME:
10123     {
10124       if (level.time > 0)       // only modify limited time value
10125       {
10126         TimeLeft = action_arg_number_new;
10127
10128         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10129
10130         DisplayGameControlValues();
10131
10132         if (!TimeLeft && game.time_limit)
10133           for (i = 0; i < MAX_PLAYERS; i++)
10134             KillPlayer(&stored_player[i]);
10135       }
10136
10137       break;
10138     }
10139
10140     case CA_SET_LEVEL_SCORE:
10141     {
10142       game.score = action_arg_number_new;
10143
10144       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10145
10146       DisplayGameControlValues();
10147
10148       break;
10149     }
10150
10151     case CA_SET_LEVEL_GEMS:
10152     {
10153       game.gems_still_needed = action_arg_number_new;
10154
10155       game.snapshot.collected_item = TRUE;
10156
10157       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10158
10159       DisplayGameControlValues();
10160
10161       break;
10162     }
10163
10164     case CA_SET_LEVEL_WIND:
10165     {
10166       game.wind_direction = action_arg_direction;
10167
10168       break;
10169     }
10170
10171     case CA_SET_LEVEL_RANDOM_SEED:
10172     {
10173       // ensure that setting a new random seed while playing is predictable
10174       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10175
10176       break;
10177     }
10178
10179     // ---------- player actions  ---------------------------------------------
10180
10181     case CA_MOVE_PLAYER:
10182     case CA_MOVE_PLAYER_NEW:
10183     {
10184       // automatically move to the next field in specified direction
10185       for (i = 0; i < MAX_PLAYERS; i++)
10186         if (trigger_player_bits & (1 << i))
10187           if (action_type == CA_MOVE_PLAYER ||
10188               stored_player[i].MovPos == 0)
10189             stored_player[i].programmed_action = action_arg_direction;
10190
10191       break;
10192     }
10193
10194     case CA_EXIT_PLAYER:
10195     {
10196       for (i = 0; i < MAX_PLAYERS; i++)
10197         if (action_arg_player_bits & (1 << i))
10198           ExitPlayer(&stored_player[i]);
10199
10200       if (game.players_still_needed == 0)
10201         LevelSolved();
10202
10203       break;
10204     }
10205
10206     case CA_KILL_PLAYER:
10207     {
10208       for (i = 0; i < MAX_PLAYERS; i++)
10209         if (action_arg_player_bits & (1 << i))
10210           KillPlayer(&stored_player[i]);
10211
10212       break;
10213     }
10214
10215     case CA_SET_PLAYER_KEYS:
10216     {
10217       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10218       int element = getSpecialActionElement(action_arg_element,
10219                                             action_arg_number, EL_KEY_1);
10220
10221       if (IS_KEY(element))
10222       {
10223         for (i = 0; i < MAX_PLAYERS; i++)
10224         {
10225           if (trigger_player_bits & (1 << i))
10226           {
10227             stored_player[i].key[KEY_NR(element)] = key_state;
10228
10229             DrawGameDoorValues();
10230           }
10231         }
10232       }
10233
10234       break;
10235     }
10236
10237     case CA_SET_PLAYER_SPEED:
10238     {
10239       for (i = 0; i < MAX_PLAYERS; i++)
10240       {
10241         if (trigger_player_bits & (1 << i))
10242         {
10243           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10244
10245           if (action_arg == CA_ARG_SPEED_FASTER &&
10246               stored_player[i].cannot_move)
10247           {
10248             action_arg_number = STEPSIZE_VERY_SLOW;
10249           }
10250           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10251                    action_arg == CA_ARG_SPEED_FASTER)
10252           {
10253             action_arg_number = 2;
10254             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10255                            CA_MODE_MULTIPLY);
10256           }
10257           else if (action_arg == CA_ARG_NUMBER_RESET)
10258           {
10259             action_arg_number = level.initial_player_stepsize[i];
10260           }
10261
10262           move_stepsize =
10263             getModifiedActionNumber(move_stepsize,
10264                                     action_mode,
10265                                     action_arg_number,
10266                                     action_arg_number_min,
10267                                     action_arg_number_max);
10268
10269           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10270         }
10271       }
10272
10273       break;
10274     }
10275
10276     case CA_SET_PLAYER_SHIELD:
10277     {
10278       for (i = 0; i < MAX_PLAYERS; i++)
10279       {
10280         if (trigger_player_bits & (1 << i))
10281         {
10282           if (action_arg == CA_ARG_SHIELD_OFF)
10283           {
10284             stored_player[i].shield_normal_time_left = 0;
10285             stored_player[i].shield_deadly_time_left = 0;
10286           }
10287           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10288           {
10289             stored_player[i].shield_normal_time_left = 999999;
10290           }
10291           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10292           {
10293             stored_player[i].shield_normal_time_left = 999999;
10294             stored_player[i].shield_deadly_time_left = 999999;
10295           }
10296         }
10297       }
10298
10299       break;
10300     }
10301
10302     case CA_SET_PLAYER_GRAVITY:
10303     {
10304       for (i = 0; i < MAX_PLAYERS; i++)
10305       {
10306         if (trigger_player_bits & (1 << i))
10307         {
10308           stored_player[i].gravity =
10309             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10310              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10311              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10312              stored_player[i].gravity);
10313         }
10314       }
10315
10316       break;
10317     }
10318
10319     case CA_SET_PLAYER_ARTWORK:
10320     {
10321       for (i = 0; i < MAX_PLAYERS; i++)
10322       {
10323         if (trigger_player_bits & (1 << i))
10324         {
10325           int artwork_element = action_arg_element;
10326
10327           if (action_arg == CA_ARG_ELEMENT_RESET)
10328             artwork_element =
10329               (level.use_artwork_element[i] ? level.artwork_element[i] :
10330                stored_player[i].element_nr);
10331
10332           if (stored_player[i].artwork_element != artwork_element)
10333             stored_player[i].Frame = 0;
10334
10335           stored_player[i].artwork_element = artwork_element;
10336
10337           SetPlayerWaiting(&stored_player[i], FALSE);
10338
10339           // set number of special actions for bored and sleeping animation
10340           stored_player[i].num_special_action_bored =
10341             get_num_special_action(artwork_element,
10342                                    ACTION_BORING_1, ACTION_BORING_LAST);
10343           stored_player[i].num_special_action_sleeping =
10344             get_num_special_action(artwork_element,
10345                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10346         }
10347       }
10348
10349       break;
10350     }
10351
10352     case CA_SET_PLAYER_INVENTORY:
10353     {
10354       for (i = 0; i < MAX_PLAYERS; i++)
10355       {
10356         struct PlayerInfo *player = &stored_player[i];
10357         int j, k;
10358
10359         if (trigger_player_bits & (1 << i))
10360         {
10361           int inventory_element = action_arg_element;
10362
10363           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10364               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10365               action_arg == CA_ARG_ELEMENT_ACTION)
10366           {
10367             int element = inventory_element;
10368             int collect_count = element_info[element].collect_count_initial;
10369
10370             if (!IS_CUSTOM_ELEMENT(element))
10371               collect_count = 1;
10372
10373             if (collect_count == 0)
10374               player->inventory_infinite_element = element;
10375             else
10376               for (k = 0; k < collect_count; k++)
10377                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10378                   player->inventory_element[player->inventory_size++] =
10379                     element;
10380           }
10381           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10382                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10383                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10384           {
10385             if (player->inventory_infinite_element != EL_UNDEFINED &&
10386                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10387                                      action_arg_element_raw))
10388               player->inventory_infinite_element = EL_UNDEFINED;
10389
10390             for (k = 0, j = 0; j < player->inventory_size; j++)
10391             {
10392               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10393                                         action_arg_element_raw))
10394                 player->inventory_element[k++] = player->inventory_element[j];
10395             }
10396
10397             player->inventory_size = k;
10398           }
10399           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10400           {
10401             if (player->inventory_size > 0)
10402             {
10403               for (j = 0; j < player->inventory_size - 1; j++)
10404                 player->inventory_element[j] = player->inventory_element[j + 1];
10405
10406               player->inventory_size--;
10407             }
10408           }
10409           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10410           {
10411             if (player->inventory_size > 0)
10412               player->inventory_size--;
10413           }
10414           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10415           {
10416             player->inventory_infinite_element = EL_UNDEFINED;
10417             player->inventory_size = 0;
10418           }
10419           else if (action_arg == CA_ARG_INVENTORY_RESET)
10420           {
10421             player->inventory_infinite_element = EL_UNDEFINED;
10422             player->inventory_size = 0;
10423
10424             if (level.use_initial_inventory[i])
10425             {
10426               for (j = 0; j < level.initial_inventory_size[i]; j++)
10427               {
10428                 int element = level.initial_inventory_content[i][j];
10429                 int collect_count = element_info[element].collect_count_initial;
10430
10431                 if (!IS_CUSTOM_ELEMENT(element))
10432                   collect_count = 1;
10433
10434                 if (collect_count == 0)
10435                   player->inventory_infinite_element = element;
10436                 else
10437                   for (k = 0; k < collect_count; k++)
10438                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10439                       player->inventory_element[player->inventory_size++] =
10440                         element;
10441               }
10442             }
10443           }
10444         }
10445       }
10446
10447       break;
10448     }
10449
10450     // ---------- CE actions  -------------------------------------------------
10451
10452     case CA_SET_CE_VALUE:
10453     {
10454       int last_ce_value = CustomValue[x][y];
10455
10456       CustomValue[x][y] = action_arg_number_new;
10457
10458       if (CustomValue[x][y] != last_ce_value)
10459       {
10460         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10461         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10462
10463         if (CustomValue[x][y] == 0)
10464         {
10465           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10466           ChangeCount[x][y] = 0;        // allow at least one more change
10467
10468           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10469           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10470         }
10471       }
10472
10473       break;
10474     }
10475
10476     case CA_SET_CE_SCORE:
10477     {
10478       int last_ce_score = ei->collect_score;
10479
10480       ei->collect_score = action_arg_number_new;
10481
10482       if (ei->collect_score != last_ce_score)
10483       {
10484         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10485         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10486
10487         if (ei->collect_score == 0)
10488         {
10489           int xx, yy;
10490
10491           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10492           ChangeCount[x][y] = 0;        // allow at least one more change
10493
10494           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10495           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10496
10497           /*
10498             This is a very special case that seems to be a mixture between
10499             CheckElementChange() and CheckTriggeredElementChange(): while
10500             the first one only affects single elements that are triggered
10501             directly, the second one affects multiple elements in the playfield
10502             that are triggered indirectly by another element. This is a third
10503             case: Changing the CE score always affects multiple identical CEs,
10504             so every affected CE must be checked, not only the single CE for
10505             which the CE score was changed in the first place (as every instance
10506             of that CE shares the same CE score, and therefore also can change)!
10507           */
10508           SCAN_PLAYFIELD(xx, yy)
10509           {
10510             if (Tile[xx][yy] == element)
10511               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10512                                  CE_SCORE_GETS_ZERO);
10513           }
10514         }
10515       }
10516
10517       break;
10518     }
10519
10520     case CA_SET_CE_ARTWORK:
10521     {
10522       int artwork_element = action_arg_element;
10523       boolean reset_frame = FALSE;
10524       int xx, yy;
10525
10526       if (action_arg == CA_ARG_ELEMENT_RESET)
10527         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10528                            element);
10529
10530       if (ei->gfx_element != artwork_element)
10531         reset_frame = TRUE;
10532
10533       ei->gfx_element = artwork_element;
10534
10535       SCAN_PLAYFIELD(xx, yy)
10536       {
10537         if (Tile[xx][yy] == element)
10538         {
10539           if (reset_frame)
10540           {
10541             ResetGfxAnimation(xx, yy);
10542             ResetRandomAnimationValue(xx, yy);
10543           }
10544
10545           TEST_DrawLevelField(xx, yy);
10546         }
10547       }
10548
10549       break;
10550     }
10551
10552     // ---------- engine actions  ---------------------------------------------
10553
10554     case CA_SET_ENGINE_SCAN_MODE:
10555     {
10556       InitPlayfieldScanMode(action_arg);
10557
10558       break;
10559     }
10560
10561     default:
10562       break;
10563   }
10564 }
10565
10566 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10567 {
10568   int old_element = Tile[x][y];
10569   int new_element = GetElementFromGroupElement(element);
10570   int previous_move_direction = MovDir[x][y];
10571   int last_ce_value = CustomValue[x][y];
10572   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10573   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10574   boolean add_player_onto_element = (new_element_is_player &&
10575                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10576                                      IS_WALKABLE(old_element));
10577
10578   if (!add_player_onto_element)
10579   {
10580     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10581       RemoveMovingField(x, y);
10582     else
10583       RemoveField(x, y);
10584
10585     Tile[x][y] = new_element;
10586
10587     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10588       MovDir[x][y] = previous_move_direction;
10589
10590     if (element_info[new_element].use_last_ce_value)
10591       CustomValue[x][y] = last_ce_value;
10592
10593     InitField_WithBug1(x, y, FALSE);
10594
10595     new_element = Tile[x][y];   // element may have changed
10596
10597     ResetGfxAnimation(x, y);
10598     ResetRandomAnimationValue(x, y);
10599
10600     TEST_DrawLevelField(x, y);
10601
10602     if (GFX_CRUMBLED(new_element))
10603       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10604   }
10605
10606   // check if element under the player changes from accessible to unaccessible
10607   // (needed for special case of dropping element which then changes)
10608   // (must be checked after creating new element for walkable group elements)
10609   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10610       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10611   {
10612     Bang(x, y);
10613
10614     return;
10615   }
10616
10617   // "ChangeCount" not set yet to allow "entered by player" change one time
10618   if (new_element_is_player)
10619     RelocatePlayer(x, y, new_element);
10620
10621   if (is_change)
10622     ChangeCount[x][y]++;        // count number of changes in the same frame
10623
10624   TestIfBadThingTouchesPlayer(x, y);
10625   TestIfPlayerTouchesCustomElement(x, y);
10626   TestIfElementTouchesCustomElement(x, y);
10627 }
10628
10629 static void CreateField(int x, int y, int element)
10630 {
10631   CreateFieldExt(x, y, element, FALSE);
10632 }
10633
10634 static void CreateElementFromChange(int x, int y, int element)
10635 {
10636   element = GET_VALID_RUNTIME_ELEMENT(element);
10637
10638   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10639   {
10640     int old_element = Tile[x][y];
10641
10642     // prevent changed element from moving in same engine frame
10643     // unless both old and new element can either fall or move
10644     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10645         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10646       Stop[x][y] = TRUE;
10647   }
10648
10649   CreateFieldExt(x, y, element, TRUE);
10650 }
10651
10652 static boolean ChangeElement(int x, int y, int element, int page)
10653 {
10654   struct ElementInfo *ei = &element_info[element];
10655   struct ElementChangeInfo *change = &ei->change_page[page];
10656   int ce_value = CustomValue[x][y];
10657   int ce_score = ei->collect_score;
10658   int target_element;
10659   int old_element = Tile[x][y];
10660
10661   // always use default change event to prevent running into a loop
10662   if (ChangeEvent[x][y] == -1)
10663     ChangeEvent[x][y] = CE_DELAY;
10664
10665   if (ChangeEvent[x][y] == CE_DELAY)
10666   {
10667     // reset actual trigger element, trigger player and action element
10668     change->actual_trigger_element = EL_EMPTY;
10669     change->actual_trigger_player = EL_EMPTY;
10670     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10671     change->actual_trigger_side = CH_SIDE_NONE;
10672     change->actual_trigger_ce_value = 0;
10673     change->actual_trigger_ce_score = 0;
10674   }
10675
10676   // do not change elements more than a specified maximum number of changes
10677   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10678     return FALSE;
10679
10680   ChangeCount[x][y]++;          // count number of changes in the same frame
10681
10682   if (change->explode)
10683   {
10684     Bang(x, y);
10685
10686     return TRUE;
10687   }
10688
10689   if (change->use_target_content)
10690   {
10691     boolean complete_replace = TRUE;
10692     boolean can_replace[3][3];
10693     int xx, yy;
10694
10695     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10696     {
10697       boolean is_empty;
10698       boolean is_walkable;
10699       boolean is_diggable;
10700       boolean is_collectible;
10701       boolean is_removable;
10702       boolean is_destructible;
10703       int ex = x + xx - 1;
10704       int ey = y + yy - 1;
10705       int content_element = change->target_content.e[xx][yy];
10706       int e;
10707
10708       can_replace[xx][yy] = TRUE;
10709
10710       if (ex == x && ey == y)   // do not check changing element itself
10711         continue;
10712
10713       if (content_element == EL_EMPTY_SPACE)
10714       {
10715         can_replace[xx][yy] = FALSE;    // do not replace border with space
10716
10717         continue;
10718       }
10719
10720       if (!IN_LEV_FIELD(ex, ey))
10721       {
10722         can_replace[xx][yy] = FALSE;
10723         complete_replace = FALSE;
10724
10725         continue;
10726       }
10727
10728       e = Tile[ex][ey];
10729
10730       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10731         e = MovingOrBlocked2Element(ex, ey);
10732
10733       is_empty = (IS_FREE(ex, ey) ||
10734                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10735
10736       is_walkable     = (is_empty || IS_WALKABLE(e));
10737       is_diggable     = (is_empty || IS_DIGGABLE(e));
10738       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10739       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10740       is_removable    = (is_diggable || is_collectible);
10741
10742       can_replace[xx][yy] =
10743         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10744           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10745           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10746           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10747           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10748           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10749          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10750
10751       if (!can_replace[xx][yy])
10752         complete_replace = FALSE;
10753     }
10754
10755     if (!change->only_if_complete || complete_replace)
10756     {
10757       boolean something_has_changed = FALSE;
10758
10759       if (change->only_if_complete && change->use_random_replace &&
10760           RND(100) < change->random_percentage)
10761         return FALSE;
10762
10763       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10764       {
10765         int ex = x + xx - 1;
10766         int ey = y + yy - 1;
10767         int content_element;
10768
10769         if (can_replace[xx][yy] && (!change->use_random_replace ||
10770                                     RND(100) < change->random_percentage))
10771         {
10772           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10773             RemoveMovingField(ex, ey);
10774
10775           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10776
10777           content_element = change->target_content.e[xx][yy];
10778           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10779                                               ce_value, ce_score);
10780
10781           CreateElementFromChange(ex, ey, target_element);
10782
10783           something_has_changed = TRUE;
10784
10785           // for symmetry reasons, freeze newly created border elements
10786           if (ex != x || ey != y)
10787             Stop[ex][ey] = TRUE;        // no more moving in this frame
10788         }
10789       }
10790
10791       if (something_has_changed)
10792       {
10793         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10794         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10795       }
10796     }
10797   }
10798   else
10799   {
10800     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10801                                         ce_value, ce_score);
10802
10803     if (element == EL_DIAGONAL_GROWING ||
10804         element == EL_DIAGONAL_SHRINKING)
10805     {
10806       target_element = Store[x][y];
10807
10808       Store[x][y] = EL_EMPTY;
10809     }
10810
10811     // special case: element changes to player (and may be kept if walkable)
10812     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10813       CreateElementFromChange(x, y, EL_EMPTY);
10814
10815     CreateElementFromChange(x, y, target_element);
10816
10817     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10818     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10819   }
10820
10821   // this uses direct change before indirect change
10822   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10823
10824   return TRUE;
10825 }
10826
10827 static void HandleElementChange(int x, int y, int page)
10828 {
10829   int element = MovingOrBlocked2Element(x, y);
10830   struct ElementInfo *ei = &element_info[element];
10831   struct ElementChangeInfo *change = &ei->change_page[page];
10832   boolean handle_action_before_change = FALSE;
10833
10834 #ifdef DEBUG
10835   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10836       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10837   {
10838     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10839           x, y, element, element_info[element].token_name);
10840     Debug("game:playing:HandleElementChange", "This should never happen!");
10841   }
10842 #endif
10843
10844   // this can happen with classic bombs on walkable, changing elements
10845   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10846   {
10847     return;
10848   }
10849
10850   if (ChangeDelay[x][y] == 0)           // initialize element change
10851   {
10852     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10853
10854     if (change->can_change)
10855     {
10856       // !!! not clear why graphic animation should be reset at all here !!!
10857       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10858       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10859
10860       /*
10861         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10862
10863         When using an animation frame delay of 1 (this only happens with
10864         "sp_zonk.moving.left/right" in the classic graphics), the default
10865         (non-moving) animation shows wrong animation frames (while the
10866         moving animation, like "sp_zonk.moving.left/right", is correct,
10867         so this graphical bug never shows up with the classic graphics).
10868         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10869         be drawn instead of the correct frames 0,1,2,3. This is caused by
10870         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10871         an element change: First when the change delay ("ChangeDelay[][]")
10872         counter has reached zero after decrementing, then a second time in
10873         the next frame (after "GfxFrame[][]" was already incremented) when
10874         "ChangeDelay[][]" is reset to the initial delay value again.
10875
10876         This causes frame 0 to be drawn twice, while the last frame won't
10877         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10878
10879         As some animations may already be cleverly designed around this bug
10880         (at least the "Snake Bite" snake tail animation does this), it cannot
10881         simply be fixed here without breaking such existing animations.
10882         Unfortunately, it cannot easily be detected if a graphics set was
10883         designed "before" or "after" the bug was fixed. As a workaround,
10884         a new graphics set option "game.graphics_engine_version" was added
10885         to be able to specify the game's major release version for which the
10886         graphics set was designed, which can then be used to decide if the
10887         bugfix should be used (version 4 and above) or not (version 3 or
10888         below, or if no version was specified at all, as with old sets).
10889
10890         (The wrong/fixed animation frames can be tested with the test level set
10891         "test_gfxframe" and level "000", which contains a specially prepared
10892         custom element at level position (x/y) == (11/9) which uses the zonk
10893         animation mentioned above. Using "game.graphics_engine_version: 4"
10894         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10895         This can also be seen from the debug output for this test element.)
10896       */
10897
10898       // when a custom element is about to change (for example by change delay),
10899       // do not reset graphic animation when the custom element is moving
10900       if (game.graphics_engine_version < 4 &&
10901           !IS_MOVING(x, y))
10902       {
10903         ResetGfxAnimation(x, y);
10904         ResetRandomAnimationValue(x, y);
10905       }
10906
10907       if (change->pre_change_function)
10908         change->pre_change_function(x, y);
10909     }
10910   }
10911
10912   ChangeDelay[x][y]--;
10913
10914   if (ChangeDelay[x][y] != 0)           // continue element change
10915   {
10916     if (change->can_change)
10917     {
10918       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10919
10920       if (IS_ANIMATED(graphic))
10921         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10922
10923       if (change->change_function)
10924         change->change_function(x, y);
10925     }
10926   }
10927   else                                  // finish element change
10928   {
10929     if (ChangePage[x][y] != -1)         // remember page from delayed change
10930     {
10931       page = ChangePage[x][y];
10932       ChangePage[x][y] = -1;
10933
10934       change = &ei->change_page[page];
10935     }
10936
10937     if (IS_MOVING(x, y))                // never change a running system ;-)
10938     {
10939       ChangeDelay[x][y] = 1;            // try change after next move step
10940       ChangePage[x][y] = page;          // remember page to use for change
10941
10942       return;
10943     }
10944
10945     // special case: set new level random seed before changing element
10946     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10947       handle_action_before_change = TRUE;
10948
10949     if (change->has_action && handle_action_before_change)
10950       ExecuteCustomElementAction(x, y, element, page);
10951
10952     if (change->can_change)
10953     {
10954       if (ChangeElement(x, y, element, page))
10955       {
10956         if (change->post_change_function)
10957           change->post_change_function(x, y);
10958       }
10959     }
10960
10961     if (change->has_action && !handle_action_before_change)
10962       ExecuteCustomElementAction(x, y, element, page);
10963   }
10964 }
10965
10966 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10967                                               int trigger_element,
10968                                               int trigger_event,
10969                                               int trigger_player,
10970                                               int trigger_side,
10971                                               int trigger_page)
10972 {
10973   boolean change_done_any = FALSE;
10974   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10975   int i;
10976
10977   if (!(trigger_events[trigger_element][trigger_event]))
10978     return FALSE;
10979
10980   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10981
10982   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10983   {
10984     int element = EL_CUSTOM_START + i;
10985     boolean change_done = FALSE;
10986     int p;
10987
10988     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10989         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10990       continue;
10991
10992     for (p = 0; p < element_info[element].num_change_pages; p++)
10993     {
10994       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10995
10996       if (change->can_change_or_has_action &&
10997           change->has_event[trigger_event] &&
10998           change->trigger_side & trigger_side &&
10999           change->trigger_player & trigger_player &&
11000           change->trigger_page & trigger_page_bits &&
11001           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11002       {
11003         change->actual_trigger_element = trigger_element;
11004         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11005         change->actual_trigger_player_bits = trigger_player;
11006         change->actual_trigger_side = trigger_side;
11007         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11008         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11009
11010         if ((change->can_change && !change_done) || change->has_action)
11011         {
11012           int x, y;
11013
11014           SCAN_PLAYFIELD(x, y)
11015           {
11016             if (Tile[x][y] == element)
11017             {
11018               if (change->can_change && !change_done)
11019               {
11020                 // if element already changed in this frame, not only prevent
11021                 // another element change (checked in ChangeElement()), but
11022                 // also prevent additional element actions for this element
11023
11024                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11025                     !level.use_action_after_change_bug)
11026                   continue;
11027
11028                 ChangeDelay[x][y] = 1;
11029                 ChangeEvent[x][y] = trigger_event;
11030
11031                 HandleElementChange(x, y, p);
11032               }
11033               else if (change->has_action)
11034               {
11035                 // if element already changed in this frame, not only prevent
11036                 // another element change (checked in ChangeElement()), but
11037                 // also prevent additional element actions for this element
11038
11039                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11040                     !level.use_action_after_change_bug)
11041                   continue;
11042
11043                 ExecuteCustomElementAction(x, y, element, p);
11044                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11045               }
11046             }
11047           }
11048
11049           if (change->can_change)
11050           {
11051             change_done = TRUE;
11052             change_done_any = TRUE;
11053           }
11054         }
11055       }
11056     }
11057   }
11058
11059   RECURSION_LOOP_DETECTION_END();
11060
11061   return change_done_any;
11062 }
11063
11064 static boolean CheckElementChangeExt(int x, int y,
11065                                      int element,
11066                                      int trigger_element,
11067                                      int trigger_event,
11068                                      int trigger_player,
11069                                      int trigger_side)
11070 {
11071   boolean change_done = FALSE;
11072   int p;
11073
11074   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11075       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11076     return FALSE;
11077
11078   if (Tile[x][y] == EL_BLOCKED)
11079   {
11080     Blocked2Moving(x, y, &x, &y);
11081     element = Tile[x][y];
11082   }
11083
11084   // check if element has already changed or is about to change after moving
11085   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11086        Tile[x][y] != element) ||
11087
11088       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11089        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11090         ChangePage[x][y] != -1)))
11091     return FALSE;
11092
11093   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11094
11095   for (p = 0; p < element_info[element].num_change_pages; p++)
11096   {
11097     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11098
11099     /* check trigger element for all events where the element that is checked
11100        for changing interacts with a directly adjacent element -- this is
11101        different to element changes that affect other elements to change on the
11102        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11103     boolean check_trigger_element =
11104       (trigger_event == CE_NEXT_TO_X ||
11105        trigger_event == CE_TOUCHING_X ||
11106        trigger_event == CE_HITTING_X ||
11107        trigger_event == CE_HIT_BY_X ||
11108        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11109
11110     if (change->can_change_or_has_action &&
11111         change->has_event[trigger_event] &&
11112         change->trigger_side & trigger_side &&
11113         change->trigger_player & trigger_player &&
11114         (!check_trigger_element ||
11115          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11116     {
11117       change->actual_trigger_element = trigger_element;
11118       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11119       change->actual_trigger_player_bits = trigger_player;
11120       change->actual_trigger_side = trigger_side;
11121       change->actual_trigger_ce_value = CustomValue[x][y];
11122       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11123
11124       // special case: trigger element not at (x,y) position for some events
11125       if (check_trigger_element)
11126       {
11127         static struct
11128         {
11129           int dx, dy;
11130         } move_xy[] =
11131           {
11132             {  0,  0 },
11133             { -1,  0 },
11134             { +1,  0 },
11135             {  0,  0 },
11136             {  0, -1 },
11137             {  0,  0 }, { 0, 0 }, { 0, 0 },
11138             {  0, +1 }
11139           };
11140
11141         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11142         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11143
11144         change->actual_trigger_ce_value = CustomValue[xx][yy];
11145         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11146       }
11147
11148       if (change->can_change && !change_done)
11149       {
11150         ChangeDelay[x][y] = 1;
11151         ChangeEvent[x][y] = trigger_event;
11152
11153         HandleElementChange(x, y, p);
11154
11155         change_done = TRUE;
11156       }
11157       else if (change->has_action)
11158       {
11159         ExecuteCustomElementAction(x, y, element, p);
11160         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11161       }
11162     }
11163   }
11164
11165   RECURSION_LOOP_DETECTION_END();
11166
11167   return change_done;
11168 }
11169
11170 static void PlayPlayerSound(struct PlayerInfo *player)
11171 {
11172   int jx = player->jx, jy = player->jy;
11173   int sound_element = player->artwork_element;
11174   int last_action = player->last_action_waiting;
11175   int action = player->action_waiting;
11176
11177   if (player->is_waiting)
11178   {
11179     if (action != last_action)
11180       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11181     else
11182       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11183   }
11184   else
11185   {
11186     if (action != last_action)
11187       StopSound(element_info[sound_element].sound[last_action]);
11188
11189     if (last_action == ACTION_SLEEPING)
11190       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11191   }
11192 }
11193
11194 static void PlayAllPlayersSound(void)
11195 {
11196   int i;
11197
11198   for (i = 0; i < MAX_PLAYERS; i++)
11199     if (stored_player[i].active)
11200       PlayPlayerSound(&stored_player[i]);
11201 }
11202
11203 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11204 {
11205   boolean last_waiting = player->is_waiting;
11206   int move_dir = player->MovDir;
11207
11208   player->dir_waiting = move_dir;
11209   player->last_action_waiting = player->action_waiting;
11210
11211   if (is_waiting)
11212   {
11213     if (!last_waiting)          // not waiting -> waiting
11214     {
11215       player->is_waiting = TRUE;
11216
11217       player->frame_counter_bored =
11218         FrameCounter +
11219         game.player_boring_delay_fixed +
11220         GetSimpleRandom(game.player_boring_delay_random);
11221       player->frame_counter_sleeping =
11222         FrameCounter +
11223         game.player_sleeping_delay_fixed +
11224         GetSimpleRandom(game.player_sleeping_delay_random);
11225
11226       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11227     }
11228
11229     if (game.player_sleeping_delay_fixed +
11230         game.player_sleeping_delay_random > 0 &&
11231         player->anim_delay_counter == 0 &&
11232         player->post_delay_counter == 0 &&
11233         FrameCounter >= player->frame_counter_sleeping)
11234       player->is_sleeping = TRUE;
11235     else if (game.player_boring_delay_fixed +
11236              game.player_boring_delay_random > 0 &&
11237              FrameCounter >= player->frame_counter_bored)
11238       player->is_bored = TRUE;
11239
11240     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11241                               player->is_bored ? ACTION_BORING :
11242                               ACTION_WAITING);
11243
11244     if (player->is_sleeping && player->use_murphy)
11245     {
11246       // special case for sleeping Murphy when leaning against non-free tile
11247
11248       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11249           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11250            !IS_MOVING(player->jx - 1, player->jy)))
11251         move_dir = MV_LEFT;
11252       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11253                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11254                 !IS_MOVING(player->jx + 1, player->jy)))
11255         move_dir = MV_RIGHT;
11256       else
11257         player->is_sleeping = FALSE;
11258
11259       player->dir_waiting = move_dir;
11260     }
11261
11262     if (player->is_sleeping)
11263     {
11264       if (player->num_special_action_sleeping > 0)
11265       {
11266         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11267         {
11268           int last_special_action = player->special_action_sleeping;
11269           int num_special_action = player->num_special_action_sleeping;
11270           int special_action =
11271             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11272              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11273              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11274              last_special_action + 1 : ACTION_SLEEPING);
11275           int special_graphic =
11276             el_act_dir2img(player->artwork_element, special_action, move_dir);
11277
11278           player->anim_delay_counter =
11279             graphic_info[special_graphic].anim_delay_fixed +
11280             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11281           player->post_delay_counter =
11282             graphic_info[special_graphic].post_delay_fixed +
11283             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11284
11285           player->special_action_sleeping = special_action;
11286         }
11287
11288         if (player->anim_delay_counter > 0)
11289         {
11290           player->action_waiting = player->special_action_sleeping;
11291           player->anim_delay_counter--;
11292         }
11293         else if (player->post_delay_counter > 0)
11294         {
11295           player->post_delay_counter--;
11296         }
11297       }
11298     }
11299     else if (player->is_bored)
11300     {
11301       if (player->num_special_action_bored > 0)
11302       {
11303         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11304         {
11305           int special_action =
11306             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11307           int special_graphic =
11308             el_act_dir2img(player->artwork_element, special_action, move_dir);
11309
11310           player->anim_delay_counter =
11311             graphic_info[special_graphic].anim_delay_fixed +
11312             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11313           player->post_delay_counter =
11314             graphic_info[special_graphic].post_delay_fixed +
11315             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11316
11317           player->special_action_bored = special_action;
11318         }
11319
11320         if (player->anim_delay_counter > 0)
11321         {
11322           player->action_waiting = player->special_action_bored;
11323           player->anim_delay_counter--;
11324         }
11325         else if (player->post_delay_counter > 0)
11326         {
11327           player->post_delay_counter--;
11328         }
11329       }
11330     }
11331   }
11332   else if (last_waiting)        // waiting -> not waiting
11333   {
11334     player->is_waiting = FALSE;
11335     player->is_bored = FALSE;
11336     player->is_sleeping = FALSE;
11337
11338     player->frame_counter_bored = -1;
11339     player->frame_counter_sleeping = -1;
11340
11341     player->anim_delay_counter = 0;
11342     player->post_delay_counter = 0;
11343
11344     player->dir_waiting = player->MovDir;
11345     player->action_waiting = ACTION_DEFAULT;
11346
11347     player->special_action_bored = ACTION_DEFAULT;
11348     player->special_action_sleeping = ACTION_DEFAULT;
11349   }
11350 }
11351
11352 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11353 {
11354   if ((!player->is_moving  && player->was_moving) ||
11355       (player->MovPos == 0 && player->was_moving) ||
11356       (player->is_snapping && !player->was_snapping) ||
11357       (player->is_dropping && !player->was_dropping))
11358   {
11359     if (!CheckSaveEngineSnapshotToList())
11360       return;
11361
11362     player->was_moving = FALSE;
11363     player->was_snapping = TRUE;
11364     player->was_dropping = TRUE;
11365   }
11366   else
11367   {
11368     if (player->is_moving)
11369       player->was_moving = TRUE;
11370
11371     if (!player->is_snapping)
11372       player->was_snapping = FALSE;
11373
11374     if (!player->is_dropping)
11375       player->was_dropping = FALSE;
11376   }
11377
11378   static struct MouseActionInfo mouse_action_last = { 0 };
11379   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11380   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11381
11382   if (new_released)
11383     CheckSaveEngineSnapshotToList();
11384
11385   mouse_action_last = mouse_action;
11386 }
11387
11388 static void CheckSingleStepMode(struct PlayerInfo *player)
11389 {
11390   if (tape.single_step && tape.recording && !tape.pausing)
11391   {
11392     // as it is called "single step mode", just return to pause mode when the
11393     // player stopped moving after one tile (or never starts moving at all)
11394     // (reverse logic needed here in case single step mode used in team mode)
11395     if (player->is_moving ||
11396         player->is_pushing ||
11397         player->is_dropping_pressed ||
11398         player->effective_mouse_action.button)
11399       game.enter_single_step_mode = FALSE;
11400   }
11401
11402   CheckSaveEngineSnapshot(player);
11403 }
11404
11405 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11406 {
11407   int left      = player_action & JOY_LEFT;
11408   int right     = player_action & JOY_RIGHT;
11409   int up        = player_action & JOY_UP;
11410   int down      = player_action & JOY_DOWN;
11411   int button1   = player_action & JOY_BUTTON_1;
11412   int button2   = player_action & JOY_BUTTON_2;
11413   int dx        = (left ? -1 : right ? 1 : 0);
11414   int dy        = (up   ? -1 : down  ? 1 : 0);
11415
11416   if (!player->active || tape.pausing)
11417     return 0;
11418
11419   if (player_action)
11420   {
11421     if (button1)
11422       SnapField(player, dx, dy);
11423     else
11424     {
11425       if (button2)
11426         DropElement(player);
11427
11428       MovePlayer(player, dx, dy);
11429     }
11430
11431     CheckSingleStepMode(player);
11432
11433     SetPlayerWaiting(player, FALSE);
11434
11435     return player_action;
11436   }
11437   else
11438   {
11439     // no actions for this player (no input at player's configured device)
11440
11441     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11442     SnapField(player, 0, 0);
11443     CheckGravityMovementWhenNotMoving(player);
11444
11445     if (player->MovPos == 0)
11446       SetPlayerWaiting(player, TRUE);
11447
11448     if (player->MovPos == 0)    // needed for tape.playing
11449       player->is_moving = FALSE;
11450
11451     player->is_dropping = FALSE;
11452     player->is_dropping_pressed = FALSE;
11453     player->drop_pressed_delay = 0;
11454
11455     CheckSingleStepMode(player);
11456
11457     return 0;
11458   }
11459 }
11460
11461 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11462                                          byte *tape_action)
11463 {
11464   if (!tape.use_mouse_actions)
11465     return;
11466
11467   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11468   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11469   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11470 }
11471
11472 static void SetTapeActionFromMouseAction(byte *tape_action,
11473                                          struct MouseActionInfo *mouse_action)
11474 {
11475   if (!tape.use_mouse_actions)
11476     return;
11477
11478   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11479   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11480   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11481 }
11482
11483 static void CheckLevelSolved(void)
11484 {
11485   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11486   {
11487     if (game_em.level_solved &&
11488         !game_em.game_over)                             // game won
11489     {
11490       LevelSolved();
11491
11492       game_em.game_over = TRUE;
11493
11494       game.all_players_gone = TRUE;
11495     }
11496
11497     if (game_em.game_over)                              // game lost
11498       game.all_players_gone = TRUE;
11499   }
11500   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11501   {
11502     if (game_sp.level_solved &&
11503         !game_sp.game_over)                             // game won
11504     {
11505       LevelSolved();
11506
11507       game_sp.game_over = TRUE;
11508
11509       game.all_players_gone = TRUE;
11510     }
11511
11512     if (game_sp.game_over)                              // game lost
11513       game.all_players_gone = TRUE;
11514   }
11515   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11516   {
11517     if (game_mm.level_solved &&
11518         !game_mm.game_over)                             // game won
11519     {
11520       LevelSolved();
11521
11522       game_mm.game_over = TRUE;
11523
11524       game.all_players_gone = TRUE;
11525     }
11526
11527     if (game_mm.game_over)                              // game lost
11528       game.all_players_gone = TRUE;
11529   }
11530 }
11531
11532 static void CheckLevelTime_StepCounter(void)
11533 {
11534   int i;
11535
11536   TimePlayed++;
11537
11538   if (TimeLeft > 0)
11539   {
11540     TimeLeft--;
11541
11542     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11543       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11544
11545     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11546
11547     DisplayGameControlValues();
11548
11549     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11550       for (i = 0; i < MAX_PLAYERS; i++)
11551         KillPlayer(&stored_player[i]);
11552   }
11553   else if (game.no_level_time_limit && !game.all_players_gone)
11554   {
11555     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11556
11557     DisplayGameControlValues();
11558   }
11559 }
11560
11561 static void CheckLevelTime(void)
11562 {
11563   int i;
11564
11565   if (TimeFrames >= FRAMES_PER_SECOND)
11566   {
11567     TimeFrames = 0;
11568     TapeTime++;
11569
11570     for (i = 0; i < MAX_PLAYERS; i++)
11571     {
11572       struct PlayerInfo *player = &stored_player[i];
11573
11574       if (SHIELD_ON(player))
11575       {
11576         player->shield_normal_time_left--;
11577
11578         if (player->shield_deadly_time_left > 0)
11579           player->shield_deadly_time_left--;
11580       }
11581     }
11582
11583     if (!game.LevelSolved && !level.use_step_counter)
11584     {
11585       TimePlayed++;
11586
11587       if (TimeLeft > 0)
11588       {
11589         TimeLeft--;
11590
11591         if (TimeLeft <= 10 && game.time_limit)
11592           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11593
11594         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11595            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11596
11597         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11598
11599         if (!TimeLeft && game.time_limit)
11600         {
11601           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11602             game_em.lev->killed_out_of_time = TRUE;
11603           else
11604             for (i = 0; i < MAX_PLAYERS; i++)
11605               KillPlayer(&stored_player[i]);
11606         }
11607       }
11608       else if (game.no_level_time_limit && !game.all_players_gone)
11609       {
11610         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11611       }
11612
11613       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11614     }
11615
11616     if (tape.recording || tape.playing)
11617       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11618   }
11619
11620   if (tape.recording || tape.playing)
11621     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11622
11623   UpdateAndDisplayGameControlValues();
11624 }
11625
11626 void AdvanceFrameAndPlayerCounters(int player_nr)
11627 {
11628   int i;
11629
11630   // advance frame counters (global frame counter and time frame counter)
11631   FrameCounter++;
11632   TimeFrames++;
11633
11634   // advance player counters (counters for move delay, move animation etc.)
11635   for (i = 0; i < MAX_PLAYERS; i++)
11636   {
11637     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11638     int move_delay_value = stored_player[i].move_delay_value;
11639     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11640
11641     if (!advance_player_counters)       // not all players may be affected
11642       continue;
11643
11644     if (move_frames == 0)       // less than one move per game frame
11645     {
11646       int stepsize = TILEX / move_delay_value;
11647       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11648       int count = (stored_player[i].is_moving ?
11649                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11650
11651       if (count % delay == 0)
11652         move_frames = 1;
11653     }
11654
11655     stored_player[i].Frame += move_frames;
11656
11657     if (stored_player[i].MovPos != 0)
11658       stored_player[i].StepFrame += move_frames;
11659
11660     if (stored_player[i].move_delay > 0)
11661       stored_player[i].move_delay--;
11662
11663     // due to bugs in previous versions, counter must count up, not down
11664     if (stored_player[i].push_delay != -1)
11665       stored_player[i].push_delay++;
11666
11667     if (stored_player[i].drop_delay > 0)
11668       stored_player[i].drop_delay--;
11669
11670     if (stored_player[i].is_dropping_pressed)
11671       stored_player[i].drop_pressed_delay++;
11672   }
11673 }
11674
11675 void StartGameActions(boolean init_network_game, boolean record_tape,
11676                       int random_seed)
11677 {
11678   unsigned int new_random_seed = InitRND(random_seed);
11679
11680   if (record_tape)
11681     TapeStartRecording(new_random_seed);
11682
11683   if (setup.auto_pause_on_start && !tape.pausing)
11684     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11685
11686   if (init_network_game)
11687   {
11688     SendToServer_LevelFile();
11689     SendToServer_StartPlaying();
11690
11691     return;
11692   }
11693
11694   InitGame();
11695 }
11696
11697 static void GameActionsExt(void)
11698 {
11699 #if 0
11700   static unsigned int game_frame_delay = 0;
11701 #endif
11702   unsigned int game_frame_delay_value;
11703   byte *recorded_player_action;
11704   byte summarized_player_action = 0;
11705   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11706   int i;
11707
11708   // detect endless loops, caused by custom element programming
11709   if (recursion_loop_detected && recursion_loop_depth == 0)
11710   {
11711     char *message = getStringCat3("Internal Error! Element ",
11712                                   EL_NAME(recursion_loop_element),
11713                                   " caused endless loop! Quit the game?");
11714
11715     Warn("element '%s' caused endless loop in game engine",
11716          EL_NAME(recursion_loop_element));
11717
11718     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11719
11720     recursion_loop_detected = FALSE;    // if game should be continued
11721
11722     free(message);
11723
11724     return;
11725   }
11726
11727   if (game.restart_level)
11728     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11729
11730   CheckLevelSolved();
11731
11732   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11733     GameWon();
11734
11735   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11736     TapeStop();
11737
11738   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11739     return;
11740
11741   game_frame_delay_value =
11742     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11743
11744   if (tape.playing && tape.warp_forward && !tape.pausing)
11745     game_frame_delay_value = 0;
11746
11747   SetVideoFrameDelay(game_frame_delay_value);
11748
11749   // (de)activate virtual buttons depending on current game status
11750   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11751   {
11752     if (game.all_players_gone)  // if no players there to be controlled anymore
11753       SetOverlayActive(FALSE);
11754     else if (!tape.playing)     // if game continues after tape stopped playing
11755       SetOverlayActive(TRUE);
11756   }
11757
11758 #if 0
11759 #if 0
11760   // ---------- main game synchronization point ----------
11761
11762   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11763
11764   Debug("game:playing:skip", "skip == %d", skip);
11765
11766 #else
11767   // ---------- main game synchronization point ----------
11768
11769   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11770 #endif
11771 #endif
11772
11773   if (network_playing && !network_player_action_received)
11774   {
11775     // try to get network player actions in time
11776
11777     // last chance to get network player actions without main loop delay
11778     HandleNetworking();
11779
11780     // game was quit by network peer
11781     if (game_status != GAME_MODE_PLAYING)
11782       return;
11783
11784     // check if network player actions still missing and game still running
11785     if (!network_player_action_received && !checkGameEnded())
11786       return;           // failed to get network player actions in time
11787
11788     // do not yet reset "network_player_action_received" (for tape.pausing)
11789   }
11790
11791   if (tape.pausing)
11792     return;
11793
11794   // at this point we know that we really continue executing the game
11795
11796   network_player_action_received = FALSE;
11797
11798   // when playing tape, read previously recorded player input from tape data
11799   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11800
11801   local_player->effective_mouse_action = local_player->mouse_action;
11802
11803   if (recorded_player_action != NULL)
11804     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11805                                  recorded_player_action);
11806
11807   // TapePlayAction() may return NULL when toggling to "pause before death"
11808   if (tape.pausing)
11809     return;
11810
11811   if (tape.set_centered_player)
11812   {
11813     game.centered_player_nr_next = tape.centered_player_nr_next;
11814     game.set_centered_player = TRUE;
11815   }
11816
11817   for (i = 0; i < MAX_PLAYERS; i++)
11818   {
11819     summarized_player_action |= stored_player[i].action;
11820
11821     if (!network_playing && (game.team_mode || tape.playing))
11822       stored_player[i].effective_action = stored_player[i].action;
11823   }
11824
11825   if (network_playing && !checkGameEnded())
11826     SendToServer_MovePlayer(summarized_player_action);
11827
11828   // summarize all actions at local players mapped input device position
11829   // (this allows using different input devices in single player mode)
11830   if (!network.enabled && !game.team_mode)
11831     stored_player[map_player_action[local_player->index_nr]].effective_action =
11832       summarized_player_action;
11833
11834   // summarize all actions at centered player in local team mode
11835   if (tape.recording &&
11836       setup.team_mode && !network.enabled &&
11837       setup.input_on_focus &&
11838       game.centered_player_nr != -1)
11839   {
11840     for (i = 0; i < MAX_PLAYERS; i++)
11841       stored_player[map_player_action[i]].effective_action =
11842         (i == game.centered_player_nr ? summarized_player_action : 0);
11843   }
11844
11845   if (recorded_player_action != NULL)
11846     for (i = 0; i < MAX_PLAYERS; i++)
11847       stored_player[i].effective_action = recorded_player_action[i];
11848
11849   for (i = 0; i < MAX_PLAYERS; i++)
11850   {
11851     tape_action[i] = stored_player[i].effective_action;
11852
11853     /* (this may happen in the RND game engine if a player was not present on
11854        the playfield on level start, but appeared later from a custom element */
11855     if (setup.team_mode &&
11856         tape.recording &&
11857         tape_action[i] &&
11858         !tape.player_participates[i])
11859       tape.player_participates[i] = TRUE;
11860   }
11861
11862   SetTapeActionFromMouseAction(tape_action,
11863                                &local_player->effective_mouse_action);
11864
11865   // only record actions from input devices, but not programmed actions
11866   if (tape.recording)
11867     TapeRecordAction(tape_action);
11868
11869   // remember if game was played (especially after tape stopped playing)
11870   if (!tape.playing && summarized_player_action)
11871     game.GamePlayed = TRUE;
11872
11873 #if USE_NEW_PLAYER_ASSIGNMENTS
11874   // !!! also map player actions in single player mode !!!
11875   // if (game.team_mode)
11876   if (1)
11877   {
11878     byte mapped_action[MAX_PLAYERS];
11879
11880 #if DEBUG_PLAYER_ACTIONS
11881     for (i = 0; i < MAX_PLAYERS; i++)
11882       DebugContinued("", "%d, ", stored_player[i].effective_action);
11883 #endif
11884
11885     for (i = 0; i < MAX_PLAYERS; i++)
11886       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11887
11888     for (i = 0; i < MAX_PLAYERS; i++)
11889       stored_player[i].effective_action = mapped_action[i];
11890
11891 #if DEBUG_PLAYER_ACTIONS
11892     DebugContinued("", "=> ");
11893     for (i = 0; i < MAX_PLAYERS; i++)
11894       DebugContinued("", "%d, ", stored_player[i].effective_action);
11895     DebugContinued("game:playing:player", "\n");
11896 #endif
11897   }
11898 #if DEBUG_PLAYER_ACTIONS
11899   else
11900   {
11901     for (i = 0; i < MAX_PLAYERS; i++)
11902       DebugContinued("", "%d, ", stored_player[i].effective_action);
11903     DebugContinued("game:playing:player", "\n");
11904   }
11905 #endif
11906 #endif
11907
11908   for (i = 0; i < MAX_PLAYERS; i++)
11909   {
11910     // allow engine snapshot in case of changed movement attempt
11911     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11912         (stored_player[i].effective_action & KEY_MOTION))
11913       game.snapshot.changed_action = TRUE;
11914
11915     // allow engine snapshot in case of snapping/dropping attempt
11916     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11917         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11918       game.snapshot.changed_action = TRUE;
11919
11920     game.snapshot.last_action[i] = stored_player[i].effective_action;
11921   }
11922
11923   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11924   {
11925     GameActions_EM_Main();
11926   }
11927   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11928   {
11929     GameActions_SP_Main();
11930   }
11931   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11932   {
11933     GameActions_MM_Main();
11934   }
11935   else
11936   {
11937     GameActions_RND_Main();
11938   }
11939
11940   BlitScreenToBitmap(backbuffer);
11941
11942   CheckLevelSolved();
11943   CheckLevelTime();
11944
11945   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11946
11947   if (global.show_frames_per_second)
11948   {
11949     static unsigned int fps_counter = 0;
11950     static int fps_frames = 0;
11951     unsigned int fps_delay_ms = Counter() - fps_counter;
11952
11953     fps_frames++;
11954
11955     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11956     {
11957       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11958
11959       fps_frames = 0;
11960       fps_counter = Counter();
11961
11962       // always draw FPS to screen after FPS value was updated
11963       redraw_mask |= REDRAW_FPS;
11964     }
11965
11966     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11967     if (GetDrawDeactivationMask() == REDRAW_NONE)
11968       redraw_mask |= REDRAW_FPS;
11969   }
11970 }
11971
11972 static void GameActions_CheckSaveEngineSnapshot(void)
11973 {
11974   if (!game.snapshot.save_snapshot)
11975     return;
11976
11977   // clear flag for saving snapshot _before_ saving snapshot
11978   game.snapshot.save_snapshot = FALSE;
11979
11980   SaveEngineSnapshotToList();
11981 }
11982
11983 void GameActions(void)
11984 {
11985   GameActionsExt();
11986
11987   GameActions_CheckSaveEngineSnapshot();
11988 }
11989
11990 void GameActions_EM_Main(void)
11991 {
11992   byte effective_action[MAX_PLAYERS];
11993   int i;
11994
11995   for (i = 0; i < MAX_PLAYERS; i++)
11996     effective_action[i] = stored_player[i].effective_action;
11997
11998   GameActions_EM(effective_action);
11999 }
12000
12001 void GameActions_SP_Main(void)
12002 {
12003   byte effective_action[MAX_PLAYERS];
12004   int i;
12005
12006   for (i = 0; i < MAX_PLAYERS; i++)
12007     effective_action[i] = stored_player[i].effective_action;
12008
12009   GameActions_SP(effective_action);
12010
12011   for (i = 0; i < MAX_PLAYERS; i++)
12012   {
12013     if (stored_player[i].force_dropping)
12014       stored_player[i].action |= KEY_BUTTON_DROP;
12015
12016     stored_player[i].force_dropping = FALSE;
12017   }
12018 }
12019
12020 void GameActions_MM_Main(void)
12021 {
12022   GameActions_MM(local_player->effective_mouse_action);
12023 }
12024
12025 void GameActions_RND_Main(void)
12026 {
12027   GameActions_RND();
12028 }
12029
12030 void GameActions_RND(void)
12031 {
12032   static struct MouseActionInfo mouse_action_last = { 0 };
12033   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12034   int magic_wall_x = 0, magic_wall_y = 0;
12035   int i, x, y, element, graphic, last_gfx_frame;
12036
12037   InitPlayfieldScanModeVars();
12038
12039   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12040   {
12041     SCAN_PLAYFIELD(x, y)
12042     {
12043       ChangeCount[x][y] = 0;
12044       ChangeEvent[x][y] = -1;
12045     }
12046   }
12047
12048   if (game.set_centered_player)
12049   {
12050     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12051
12052     // switching to "all players" only possible if all players fit to screen
12053     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12054     {
12055       game.centered_player_nr_next = game.centered_player_nr;
12056       game.set_centered_player = FALSE;
12057     }
12058
12059     // do not switch focus to non-existing (or non-active) player
12060     if (game.centered_player_nr_next >= 0 &&
12061         !stored_player[game.centered_player_nr_next].active)
12062     {
12063       game.centered_player_nr_next = game.centered_player_nr;
12064       game.set_centered_player = FALSE;
12065     }
12066   }
12067
12068   if (game.set_centered_player &&
12069       ScreenMovPos == 0)        // screen currently aligned at tile position
12070   {
12071     int sx, sy;
12072
12073     if (game.centered_player_nr_next == -1)
12074     {
12075       setScreenCenteredToAllPlayers(&sx, &sy);
12076     }
12077     else
12078     {
12079       sx = stored_player[game.centered_player_nr_next].jx;
12080       sy = stored_player[game.centered_player_nr_next].jy;
12081     }
12082
12083     game.centered_player_nr = game.centered_player_nr_next;
12084     game.set_centered_player = FALSE;
12085
12086     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12087     DrawGameDoorValues();
12088   }
12089
12090   // check single step mode (set flag and clear again if any player is active)
12091   game.enter_single_step_mode =
12092     (tape.single_step && tape.recording && !tape.pausing);
12093
12094   for (i = 0; i < MAX_PLAYERS; i++)
12095   {
12096     int actual_player_action = stored_player[i].effective_action;
12097
12098 #if 1
12099     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12100        - rnd_equinox_tetrachloride 048
12101        - rnd_equinox_tetrachloride_ii 096
12102        - rnd_emanuel_schmieg 002
12103        - doctor_sloan_ww 001, 020
12104     */
12105     if (stored_player[i].MovPos == 0)
12106       CheckGravityMovement(&stored_player[i]);
12107 #endif
12108
12109     // overwrite programmed action with tape action
12110     if (stored_player[i].programmed_action)
12111       actual_player_action = stored_player[i].programmed_action;
12112
12113     PlayerActions(&stored_player[i], actual_player_action);
12114
12115     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12116   }
12117
12118   // single step pause mode may already have been toggled by "ScrollPlayer()"
12119   if (game.enter_single_step_mode && !tape.pausing)
12120     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12121
12122   ScrollScreen(NULL, SCROLL_GO_ON);
12123
12124   /* for backwards compatibility, the following code emulates a fixed bug that
12125      occured when pushing elements (causing elements that just made their last
12126      pushing step to already (if possible) make their first falling step in the
12127      same game frame, which is bad); this code is also needed to use the famous
12128      "spring push bug" which is used in older levels and might be wanted to be
12129      used also in newer levels, but in this case the buggy pushing code is only
12130      affecting the "spring" element and no other elements */
12131
12132   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12133   {
12134     for (i = 0; i < MAX_PLAYERS; i++)
12135     {
12136       struct PlayerInfo *player = &stored_player[i];
12137       int x = player->jx;
12138       int y = player->jy;
12139
12140       if (player->active && player->is_pushing && player->is_moving &&
12141           IS_MOVING(x, y) &&
12142           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12143            Tile[x][y] == EL_SPRING))
12144       {
12145         ContinueMoving(x, y);
12146
12147         // continue moving after pushing (this is actually a bug)
12148         if (!IS_MOVING(x, y))
12149           Stop[x][y] = FALSE;
12150       }
12151     }
12152   }
12153
12154   SCAN_PLAYFIELD(x, y)
12155   {
12156     Last[x][y] = Tile[x][y];
12157
12158     ChangeCount[x][y] = 0;
12159     ChangeEvent[x][y] = -1;
12160
12161     // this must be handled before main playfield loop
12162     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12163     {
12164       MovDelay[x][y]--;
12165       if (MovDelay[x][y] <= 0)
12166         RemoveField(x, y);
12167     }
12168
12169     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12170     {
12171       MovDelay[x][y]--;
12172       if (MovDelay[x][y] <= 0)
12173       {
12174         int element = Store[x][y];
12175         int move_direction = MovDir[x][y];
12176         int player_index_bit = Store2[x][y];
12177
12178         Store[x][y] = 0;
12179         Store2[x][y] = 0;
12180
12181         RemoveField(x, y);
12182         TEST_DrawLevelField(x, y);
12183
12184         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12185
12186         if (IS_ENVELOPE(element))
12187           local_player->show_envelope = element;
12188       }
12189     }
12190
12191 #if DEBUG
12192     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12193     {
12194       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12195             x, y);
12196       Debug("game:playing:GameActions_RND", "This should never happen!");
12197
12198       ChangePage[x][y] = -1;
12199     }
12200 #endif
12201
12202     Stop[x][y] = FALSE;
12203     if (WasJustMoving[x][y] > 0)
12204       WasJustMoving[x][y]--;
12205     if (WasJustFalling[x][y] > 0)
12206       WasJustFalling[x][y]--;
12207     if (CheckCollision[x][y] > 0)
12208       CheckCollision[x][y]--;
12209     if (CheckImpact[x][y] > 0)
12210       CheckImpact[x][y]--;
12211
12212     GfxFrame[x][y]++;
12213
12214     /* reset finished pushing action (not done in ContinueMoving() to allow
12215        continuous pushing animation for elements with zero push delay) */
12216     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12217     {
12218       ResetGfxAnimation(x, y);
12219       TEST_DrawLevelField(x, y);
12220     }
12221
12222 #if DEBUG
12223     if (IS_BLOCKED(x, y))
12224     {
12225       int oldx, oldy;
12226
12227       Blocked2Moving(x, y, &oldx, &oldy);
12228       if (!IS_MOVING(oldx, oldy))
12229       {
12230         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12231         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12232         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12233         Debug("game:playing:GameActions_RND", "This should never happen!");
12234       }
12235     }
12236 #endif
12237   }
12238
12239   if (mouse_action.button)
12240   {
12241     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12242     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12243
12244     x = mouse_action.lx;
12245     y = mouse_action.ly;
12246     element = Tile[x][y];
12247
12248     if (new_button)
12249     {
12250       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12251       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12252                                          ch_button);
12253     }
12254
12255     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12256     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12257                                        ch_button);
12258
12259     if (level.use_step_counter)
12260     {
12261       boolean counted_click = FALSE;
12262
12263       // element clicked that can change when clicked/pressed
12264       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12265           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12266            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12267         counted_click = TRUE;
12268
12269       // element clicked that can trigger change when clicked/pressed
12270       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12271           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12272         counted_click = TRUE;
12273
12274       if (new_button && counted_click)
12275         CheckLevelTime_StepCounter();
12276     }
12277   }
12278
12279   SCAN_PLAYFIELD(x, y)
12280   {
12281     element = Tile[x][y];
12282     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12283     last_gfx_frame = GfxFrame[x][y];
12284
12285     if (element == EL_EMPTY)
12286       graphic = el2img(GfxElementEmpty[x][y]);
12287
12288     ResetGfxFrame(x, y);
12289
12290     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12291       DrawLevelGraphicAnimation(x, y, graphic);
12292
12293     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12294         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12295       ResetRandomAnimationValue(x, y);
12296
12297     SetRandomAnimationValue(x, y);
12298
12299     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12300
12301     if (IS_INACTIVE(element))
12302     {
12303       if (IS_ANIMATED(graphic))
12304         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12305
12306       continue;
12307     }
12308
12309     // this may take place after moving, so 'element' may have changed
12310     if (IS_CHANGING(x, y) &&
12311         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12312     {
12313       int page = element_info[element].event_page_nr[CE_DELAY];
12314
12315       HandleElementChange(x, y, page);
12316
12317       element = Tile[x][y];
12318       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12319     }
12320
12321     CheckNextToConditions(x, y);
12322
12323     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12324     {
12325       StartMoving(x, y);
12326
12327       element = Tile[x][y];
12328       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12329
12330       if (IS_ANIMATED(graphic) &&
12331           !IS_MOVING(x, y) &&
12332           !Stop[x][y])
12333         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12334
12335       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12336         TEST_DrawTwinkleOnField(x, y);
12337     }
12338     else if (element == EL_ACID)
12339     {
12340       if (!Stop[x][y])
12341         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12342     }
12343     else if ((element == EL_EXIT_OPEN ||
12344               element == EL_EM_EXIT_OPEN ||
12345               element == EL_SP_EXIT_OPEN ||
12346               element == EL_STEEL_EXIT_OPEN ||
12347               element == EL_EM_STEEL_EXIT_OPEN ||
12348               element == EL_SP_TERMINAL ||
12349               element == EL_SP_TERMINAL_ACTIVE ||
12350               element == EL_EXTRA_TIME ||
12351               element == EL_SHIELD_NORMAL ||
12352               element == EL_SHIELD_DEADLY) &&
12353              IS_ANIMATED(graphic))
12354       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12355     else if (IS_MOVING(x, y))
12356       ContinueMoving(x, y);
12357     else if (IS_ACTIVE_BOMB(element))
12358       CheckDynamite(x, y);
12359     else if (element == EL_AMOEBA_GROWING)
12360       AmoebaGrowing(x, y);
12361     else if (element == EL_AMOEBA_SHRINKING)
12362       AmoebaShrinking(x, y);
12363
12364 #if !USE_NEW_AMOEBA_CODE
12365     else if (IS_AMOEBALIVE(element))
12366       AmoebaReproduce(x, y);
12367 #endif
12368
12369     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12370       Life(x, y);
12371     else if (element == EL_EXIT_CLOSED)
12372       CheckExit(x, y);
12373     else if (element == EL_EM_EXIT_CLOSED)
12374       CheckExitEM(x, y);
12375     else if (element == EL_STEEL_EXIT_CLOSED)
12376       CheckExitSteel(x, y);
12377     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12378       CheckExitSteelEM(x, y);
12379     else if (element == EL_SP_EXIT_CLOSED)
12380       CheckExitSP(x, y);
12381     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12382              element == EL_EXPANDABLE_STEELWALL_GROWING)
12383       WallGrowing(x, y);
12384     else if (element == EL_EXPANDABLE_WALL ||
12385              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12386              element == EL_EXPANDABLE_WALL_VERTICAL ||
12387              element == EL_EXPANDABLE_WALL_ANY ||
12388              element == EL_BD_EXPANDABLE_WALL ||
12389              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12390              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12391              element == EL_EXPANDABLE_STEELWALL_ANY)
12392       CheckWallGrowing(x, y);
12393     else if (element == EL_FLAMES)
12394       CheckForDragon(x, y);
12395     else if (element == EL_EXPLOSION)
12396       ; // drawing of correct explosion animation is handled separately
12397     else if (element == EL_ELEMENT_SNAPPING ||
12398              element == EL_DIAGONAL_SHRINKING ||
12399              element == EL_DIAGONAL_GROWING)
12400     {
12401       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12402
12403       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12404     }
12405     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12406       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12407
12408     if (IS_BELT_ACTIVE(element))
12409       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12410
12411     if (game.magic_wall_active)
12412     {
12413       int jx = local_player->jx, jy = local_player->jy;
12414
12415       // play the element sound at the position nearest to the player
12416       if ((element == EL_MAGIC_WALL_FULL ||
12417            element == EL_MAGIC_WALL_ACTIVE ||
12418            element == EL_MAGIC_WALL_EMPTYING ||
12419            element == EL_BD_MAGIC_WALL_FULL ||
12420            element == EL_BD_MAGIC_WALL_ACTIVE ||
12421            element == EL_BD_MAGIC_WALL_EMPTYING ||
12422            element == EL_DC_MAGIC_WALL_FULL ||
12423            element == EL_DC_MAGIC_WALL_ACTIVE ||
12424            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12425           ABS(x - jx) + ABS(y - jy) <
12426           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12427       {
12428         magic_wall_x = x;
12429         magic_wall_y = y;
12430       }
12431     }
12432   }
12433
12434 #if USE_NEW_AMOEBA_CODE
12435   // new experimental amoeba growth stuff
12436   if (!(FrameCounter % 8))
12437   {
12438     static unsigned int random = 1684108901;
12439
12440     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12441     {
12442       x = RND(lev_fieldx);
12443       y = RND(lev_fieldy);
12444       element = Tile[x][y];
12445
12446       if (!IS_PLAYER(x,y) &&
12447           (element == EL_EMPTY ||
12448            CAN_GROW_INTO(element) ||
12449            element == EL_QUICKSAND_EMPTY ||
12450            element == EL_QUICKSAND_FAST_EMPTY ||
12451            element == EL_ACID_SPLASH_LEFT ||
12452            element == EL_ACID_SPLASH_RIGHT))
12453       {
12454         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12455             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12456             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12457             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12458           Tile[x][y] = EL_AMOEBA_DROP;
12459       }
12460
12461       random = random * 129 + 1;
12462     }
12463   }
12464 #endif
12465
12466   game.explosions_delayed = FALSE;
12467
12468   SCAN_PLAYFIELD(x, y)
12469   {
12470     element = Tile[x][y];
12471
12472     if (ExplodeField[x][y])
12473       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12474     else if (element == EL_EXPLOSION)
12475       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12476
12477     ExplodeField[x][y] = EX_TYPE_NONE;
12478   }
12479
12480   game.explosions_delayed = TRUE;
12481
12482   if (game.magic_wall_active)
12483   {
12484     if (!(game.magic_wall_time_left % 4))
12485     {
12486       int element = Tile[magic_wall_x][magic_wall_y];
12487
12488       if (element == EL_BD_MAGIC_WALL_FULL ||
12489           element == EL_BD_MAGIC_WALL_ACTIVE ||
12490           element == EL_BD_MAGIC_WALL_EMPTYING)
12491         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12492       else if (element == EL_DC_MAGIC_WALL_FULL ||
12493                element == EL_DC_MAGIC_WALL_ACTIVE ||
12494                element == EL_DC_MAGIC_WALL_EMPTYING)
12495         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12496       else
12497         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12498     }
12499
12500     if (game.magic_wall_time_left > 0)
12501     {
12502       game.magic_wall_time_left--;
12503
12504       if (!game.magic_wall_time_left)
12505       {
12506         SCAN_PLAYFIELD(x, y)
12507         {
12508           element = Tile[x][y];
12509
12510           if (element == EL_MAGIC_WALL_ACTIVE ||
12511               element == EL_MAGIC_WALL_FULL)
12512           {
12513             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12514             TEST_DrawLevelField(x, y);
12515           }
12516           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12517                    element == EL_BD_MAGIC_WALL_FULL)
12518           {
12519             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12520             TEST_DrawLevelField(x, y);
12521           }
12522           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12523                    element == EL_DC_MAGIC_WALL_FULL)
12524           {
12525             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12526             TEST_DrawLevelField(x, y);
12527           }
12528         }
12529
12530         game.magic_wall_active = FALSE;
12531       }
12532     }
12533   }
12534
12535   if (game.light_time_left > 0)
12536   {
12537     game.light_time_left--;
12538
12539     if (game.light_time_left == 0)
12540       RedrawAllLightSwitchesAndInvisibleElements();
12541   }
12542
12543   if (game.timegate_time_left > 0)
12544   {
12545     game.timegate_time_left--;
12546
12547     if (game.timegate_time_left == 0)
12548       CloseAllOpenTimegates();
12549   }
12550
12551   if (game.lenses_time_left > 0)
12552   {
12553     game.lenses_time_left--;
12554
12555     if (game.lenses_time_left == 0)
12556       RedrawAllInvisibleElementsForLenses();
12557   }
12558
12559   if (game.magnify_time_left > 0)
12560   {
12561     game.magnify_time_left--;
12562
12563     if (game.magnify_time_left == 0)
12564       RedrawAllInvisibleElementsForMagnifier();
12565   }
12566
12567   for (i = 0; i < MAX_PLAYERS; i++)
12568   {
12569     struct PlayerInfo *player = &stored_player[i];
12570
12571     if (SHIELD_ON(player))
12572     {
12573       if (player->shield_deadly_time_left)
12574         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12575       else if (player->shield_normal_time_left)
12576         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12577     }
12578   }
12579
12580 #if USE_DELAYED_GFX_REDRAW
12581   SCAN_PLAYFIELD(x, y)
12582   {
12583     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12584     {
12585       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12586          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12587
12588       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12589         DrawLevelField(x, y);
12590
12591       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12592         DrawLevelFieldCrumbled(x, y);
12593
12594       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12595         DrawLevelFieldCrumbledNeighbours(x, y);
12596
12597       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12598         DrawTwinkleOnField(x, y);
12599     }
12600
12601     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12602   }
12603 #endif
12604
12605   DrawAllPlayers();
12606   PlayAllPlayersSound();
12607
12608   for (i = 0; i < MAX_PLAYERS; i++)
12609   {
12610     struct PlayerInfo *player = &stored_player[i];
12611
12612     if (player->show_envelope != 0 && (!player->active ||
12613                                        player->MovPos == 0))
12614     {
12615       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12616
12617       player->show_envelope = 0;
12618     }
12619   }
12620
12621   // use random number generator in every frame to make it less predictable
12622   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12623     RND(1);
12624
12625   mouse_action_last = mouse_action;
12626 }
12627
12628 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12629 {
12630   int min_x = x, min_y = y, max_x = x, max_y = y;
12631   int scr_fieldx = getScreenFieldSizeX();
12632   int scr_fieldy = getScreenFieldSizeY();
12633   int i;
12634
12635   for (i = 0; i < MAX_PLAYERS; i++)
12636   {
12637     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12638
12639     if (!stored_player[i].active || &stored_player[i] == player)
12640       continue;
12641
12642     min_x = MIN(min_x, jx);
12643     min_y = MIN(min_y, jy);
12644     max_x = MAX(max_x, jx);
12645     max_y = MAX(max_y, jy);
12646   }
12647
12648   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12649 }
12650
12651 static boolean AllPlayersInVisibleScreen(void)
12652 {
12653   int i;
12654
12655   for (i = 0; i < MAX_PLAYERS; i++)
12656   {
12657     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12658
12659     if (!stored_player[i].active)
12660       continue;
12661
12662     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12663       return FALSE;
12664   }
12665
12666   return TRUE;
12667 }
12668
12669 void ScrollLevel(int dx, int dy)
12670 {
12671   int scroll_offset = 2 * TILEX_VAR;
12672   int x, y;
12673
12674   BlitBitmap(drawto_field, drawto_field,
12675              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12676              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12677              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12678              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12679              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12680              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12681
12682   if (dx != 0)
12683   {
12684     x = (dx == 1 ? BX1 : BX2);
12685     for (y = BY1; y <= BY2; y++)
12686       DrawScreenField(x, y);
12687   }
12688
12689   if (dy != 0)
12690   {
12691     y = (dy == 1 ? BY1 : BY2);
12692     for (x = BX1; x <= BX2; x++)
12693       DrawScreenField(x, y);
12694   }
12695
12696   redraw_mask |= REDRAW_FIELD;
12697 }
12698
12699 static boolean canFallDown(struct PlayerInfo *player)
12700 {
12701   int jx = player->jx, jy = player->jy;
12702
12703   return (IN_LEV_FIELD(jx, jy + 1) &&
12704           (IS_FREE(jx, jy + 1) ||
12705            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12706           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12707           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12708 }
12709
12710 static boolean canPassField(int x, int y, int move_dir)
12711 {
12712   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12713   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12714   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12715   int nextx = x + dx;
12716   int nexty = y + dy;
12717   int element = Tile[x][y];
12718
12719   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12720           !CAN_MOVE(element) &&
12721           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12722           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12723           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12724 }
12725
12726 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12727 {
12728   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12729   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12730   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12731   int newx = x + dx;
12732   int newy = y + dy;
12733
12734   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12735           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12736           (IS_DIGGABLE(Tile[newx][newy]) ||
12737            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12738            canPassField(newx, newy, move_dir)));
12739 }
12740
12741 static void CheckGravityMovement(struct PlayerInfo *player)
12742 {
12743   if (player->gravity && !player->programmed_action)
12744   {
12745     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12746     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12747     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12748     int jx = player->jx, jy = player->jy;
12749     boolean player_is_moving_to_valid_field =
12750       (!player_is_snapping &&
12751        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12752         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12753     boolean player_can_fall_down = canFallDown(player);
12754
12755     if (player_can_fall_down &&
12756         !player_is_moving_to_valid_field)
12757       player->programmed_action = MV_DOWN;
12758   }
12759 }
12760
12761 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12762 {
12763   return CheckGravityMovement(player);
12764
12765   if (player->gravity && !player->programmed_action)
12766   {
12767     int jx = player->jx, jy = player->jy;
12768     boolean field_under_player_is_free =
12769       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12770     boolean player_is_standing_on_valid_field =
12771       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12772        (IS_WALKABLE(Tile[jx][jy]) &&
12773         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12774
12775     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12776       player->programmed_action = MV_DOWN;
12777   }
12778 }
12779
12780 /*
12781   MovePlayerOneStep()
12782   -----------------------------------------------------------------------------
12783   dx, dy:               direction (non-diagonal) to try to move the player to
12784   real_dx, real_dy:     direction as read from input device (can be diagonal)
12785 */
12786
12787 boolean MovePlayerOneStep(struct PlayerInfo *player,
12788                           int dx, int dy, int real_dx, int real_dy)
12789 {
12790   int jx = player->jx, jy = player->jy;
12791   int new_jx = jx + dx, new_jy = jy + dy;
12792   int can_move;
12793   boolean player_can_move = !player->cannot_move;
12794
12795   if (!player->active || (!dx && !dy))
12796     return MP_NO_ACTION;
12797
12798   player->MovDir = (dx < 0 ? MV_LEFT :
12799                     dx > 0 ? MV_RIGHT :
12800                     dy < 0 ? MV_UP :
12801                     dy > 0 ? MV_DOWN :  MV_NONE);
12802
12803   if (!IN_LEV_FIELD(new_jx, new_jy))
12804     return MP_NO_ACTION;
12805
12806   if (!player_can_move)
12807   {
12808     if (player->MovPos == 0)
12809     {
12810       player->is_moving = FALSE;
12811       player->is_digging = FALSE;
12812       player->is_collecting = FALSE;
12813       player->is_snapping = FALSE;
12814       player->is_pushing = FALSE;
12815     }
12816   }
12817
12818   if (!network.enabled && game.centered_player_nr == -1 &&
12819       !AllPlayersInSight(player, new_jx, new_jy))
12820     return MP_NO_ACTION;
12821
12822   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12823   if (can_move != MP_MOVING)
12824     return can_move;
12825
12826   // check if DigField() has caused relocation of the player
12827   if (player->jx != jx || player->jy != jy)
12828     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12829
12830   StorePlayer[jx][jy] = 0;
12831   player->last_jx = jx;
12832   player->last_jy = jy;
12833   player->jx = new_jx;
12834   player->jy = new_jy;
12835   StorePlayer[new_jx][new_jy] = player->element_nr;
12836
12837   if (player->move_delay_value_next != -1)
12838   {
12839     player->move_delay_value = player->move_delay_value_next;
12840     player->move_delay_value_next = -1;
12841   }
12842
12843   player->MovPos =
12844     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12845
12846   player->step_counter++;
12847
12848   PlayerVisit[jx][jy] = FrameCounter;
12849
12850   player->is_moving = TRUE;
12851
12852 #if 1
12853   // should better be called in MovePlayer(), but this breaks some tapes
12854   ScrollPlayer(player, SCROLL_INIT);
12855 #endif
12856
12857   return MP_MOVING;
12858 }
12859
12860 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12861 {
12862   int jx = player->jx, jy = player->jy;
12863   int old_jx = jx, old_jy = jy;
12864   int moved = MP_NO_ACTION;
12865
12866   if (!player->active)
12867     return FALSE;
12868
12869   if (!dx && !dy)
12870   {
12871     if (player->MovPos == 0)
12872     {
12873       player->is_moving = FALSE;
12874       player->is_digging = FALSE;
12875       player->is_collecting = FALSE;
12876       player->is_snapping = FALSE;
12877       player->is_pushing = FALSE;
12878     }
12879
12880     return FALSE;
12881   }
12882
12883   if (player->move_delay > 0)
12884     return FALSE;
12885
12886   player->move_delay = -1;              // set to "uninitialized" value
12887
12888   // store if player is automatically moved to next field
12889   player->is_auto_moving = (player->programmed_action != MV_NONE);
12890
12891   // remove the last programmed player action
12892   player->programmed_action = 0;
12893
12894   if (player->MovPos)
12895   {
12896     // should only happen if pre-1.2 tape recordings are played
12897     // this is only for backward compatibility
12898
12899     int original_move_delay_value = player->move_delay_value;
12900
12901 #if DEBUG
12902     Debug("game:playing:MovePlayer",
12903           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12904           tape.counter);
12905 #endif
12906
12907     // scroll remaining steps with finest movement resolution
12908     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12909
12910     while (player->MovPos)
12911     {
12912       ScrollPlayer(player, SCROLL_GO_ON);
12913       ScrollScreen(NULL, SCROLL_GO_ON);
12914
12915       AdvanceFrameAndPlayerCounters(player->index_nr);
12916
12917       DrawAllPlayers();
12918       BackToFront_WithFrameDelay(0);
12919     }
12920
12921     player->move_delay_value = original_move_delay_value;
12922   }
12923
12924   player->is_active = FALSE;
12925
12926   if (player->last_move_dir & MV_HORIZONTAL)
12927   {
12928     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12929       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12930   }
12931   else
12932   {
12933     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12934       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12935   }
12936
12937   if (!moved && !player->is_active)
12938   {
12939     player->is_moving = FALSE;
12940     player->is_digging = FALSE;
12941     player->is_collecting = FALSE;
12942     player->is_snapping = FALSE;
12943     player->is_pushing = FALSE;
12944   }
12945
12946   jx = player->jx;
12947   jy = player->jy;
12948
12949   if (moved & MP_MOVING && !ScreenMovPos &&
12950       (player->index_nr == game.centered_player_nr ||
12951        game.centered_player_nr == -1))
12952   {
12953     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12954
12955     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12956     {
12957       // actual player has left the screen -- scroll in that direction
12958       if (jx != old_jx)         // player has moved horizontally
12959         scroll_x += (jx - old_jx);
12960       else                      // player has moved vertically
12961         scroll_y += (jy - old_jy);
12962     }
12963     else
12964     {
12965       int offset_raw = game.scroll_delay_value;
12966
12967       if (jx != old_jx)         // player has moved horizontally
12968       {
12969         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12970         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12971         int new_scroll_x = jx - MIDPOSX + offset_x;
12972
12973         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12974             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12975           scroll_x = new_scroll_x;
12976
12977         // don't scroll over playfield boundaries
12978         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12979
12980         // don't scroll more than one field at a time
12981         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12982
12983         // don't scroll against the player's moving direction
12984         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12985             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12986           scroll_x = old_scroll_x;
12987       }
12988       else                      // player has moved vertically
12989       {
12990         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12991         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12992         int new_scroll_y = jy - MIDPOSY + offset_y;
12993
12994         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12995             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12996           scroll_y = new_scroll_y;
12997
12998         // don't scroll over playfield boundaries
12999         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13000
13001         // don't scroll more than one field at a time
13002         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13003
13004         // don't scroll against the player's moving direction
13005         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13006             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13007           scroll_y = old_scroll_y;
13008       }
13009     }
13010
13011     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13012     {
13013       if (!network.enabled && game.centered_player_nr == -1 &&
13014           !AllPlayersInVisibleScreen())
13015       {
13016         scroll_x = old_scroll_x;
13017         scroll_y = old_scroll_y;
13018       }
13019       else
13020       {
13021         ScrollScreen(player, SCROLL_INIT);
13022         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13023       }
13024     }
13025   }
13026
13027   player->StepFrame = 0;
13028
13029   if (moved & MP_MOVING)
13030   {
13031     if (old_jx != jx && old_jy == jy)
13032       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13033     else if (old_jx == jx && old_jy != jy)
13034       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13035
13036     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13037
13038     player->last_move_dir = player->MovDir;
13039     player->is_moving = TRUE;
13040     player->is_snapping = FALSE;
13041     player->is_switching = FALSE;
13042     player->is_dropping = FALSE;
13043     player->is_dropping_pressed = FALSE;
13044     player->drop_pressed_delay = 0;
13045
13046 #if 0
13047     // should better be called here than above, but this breaks some tapes
13048     ScrollPlayer(player, SCROLL_INIT);
13049 #endif
13050   }
13051   else
13052   {
13053     CheckGravityMovementWhenNotMoving(player);
13054
13055     player->is_moving = FALSE;
13056
13057     /* at this point, the player is allowed to move, but cannot move right now
13058        (e.g. because of something blocking the way) -- ensure that the player
13059        is also allowed to move in the next frame (in old versions before 3.1.1,
13060        the player was forced to wait again for eight frames before next try) */
13061
13062     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13063       player->move_delay = 0;   // allow direct movement in the next frame
13064   }
13065
13066   if (player->move_delay == -1)         // not yet initialized by DigField()
13067     player->move_delay = player->move_delay_value;
13068
13069   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13070   {
13071     TestIfPlayerTouchesBadThing(jx, jy);
13072     TestIfPlayerTouchesCustomElement(jx, jy);
13073   }
13074
13075   if (!player->active)
13076     RemovePlayer(player);
13077
13078   return moved;
13079 }
13080
13081 void ScrollPlayer(struct PlayerInfo *player, int mode)
13082 {
13083   int jx = player->jx, jy = player->jy;
13084   int last_jx = player->last_jx, last_jy = player->last_jy;
13085   int move_stepsize = TILEX / player->move_delay_value;
13086
13087   if (!player->active)
13088     return;
13089
13090   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13091     return;
13092
13093   if (mode == SCROLL_INIT)
13094   {
13095     player->actual_frame_counter.count = FrameCounter;
13096     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13097
13098     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13099         Tile[last_jx][last_jy] == EL_EMPTY)
13100     {
13101       int last_field_block_delay = 0;   // start with no blocking at all
13102       int block_delay_adjustment = player->block_delay_adjustment;
13103
13104       // if player blocks last field, add delay for exactly one move
13105       if (player->block_last_field)
13106       {
13107         last_field_block_delay += player->move_delay_value;
13108
13109         // when blocking enabled, prevent moving up despite gravity
13110         if (player->gravity && player->MovDir == MV_UP)
13111           block_delay_adjustment = -1;
13112       }
13113
13114       // add block delay adjustment (also possible when not blocking)
13115       last_field_block_delay += block_delay_adjustment;
13116
13117       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13118       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13119     }
13120
13121     if (player->MovPos != 0)    // player has not yet reached destination
13122       return;
13123   }
13124   else if (!FrameReached(&player->actual_frame_counter))
13125     return;
13126
13127   if (player->MovPos != 0)
13128   {
13129     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13130     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13131
13132     // before DrawPlayer() to draw correct player graphic for this case
13133     if (player->MovPos == 0)
13134       CheckGravityMovement(player);
13135   }
13136
13137   if (player->MovPos == 0)      // player reached destination field
13138   {
13139     if (player->move_delay_reset_counter > 0)
13140     {
13141       player->move_delay_reset_counter--;
13142
13143       if (player->move_delay_reset_counter == 0)
13144       {
13145         // continue with normal speed after quickly moving through gate
13146         HALVE_PLAYER_SPEED(player);
13147
13148         // be able to make the next move without delay
13149         player->move_delay = 0;
13150       }
13151     }
13152
13153     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13154         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13155         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13156         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13157         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13158         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13159         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13160         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13161     {
13162       ExitPlayer(player);
13163
13164       if (game.players_still_needed == 0 &&
13165           (game.friends_still_needed == 0 ||
13166            IS_SP_ELEMENT(Tile[jx][jy])))
13167         LevelSolved();
13168     }
13169
13170     player->last_jx = jx;
13171     player->last_jy = jy;
13172
13173     // this breaks one level: "machine", level 000
13174     {
13175       int move_direction = player->MovDir;
13176       int enter_side = MV_DIR_OPPOSITE(move_direction);
13177       int leave_side = move_direction;
13178       int old_jx = last_jx;
13179       int old_jy = last_jy;
13180       int old_element = Tile[old_jx][old_jy];
13181       int new_element = Tile[jx][jy];
13182
13183       if (IS_CUSTOM_ELEMENT(old_element))
13184         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13185                                    CE_LEFT_BY_PLAYER,
13186                                    player->index_bit, leave_side);
13187
13188       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13189                                           CE_PLAYER_LEAVES_X,
13190                                           player->index_bit, leave_side);
13191
13192       if (IS_CUSTOM_ELEMENT(new_element))
13193         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13194                                    player->index_bit, enter_side);
13195
13196       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13197                                           CE_PLAYER_ENTERS_X,
13198                                           player->index_bit, enter_side);
13199
13200       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13201                                         CE_MOVE_OF_X, move_direction);
13202     }
13203
13204     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13205     {
13206       TestIfPlayerTouchesBadThing(jx, jy);
13207       TestIfPlayerTouchesCustomElement(jx, jy);
13208
13209       /* needed because pushed element has not yet reached its destination,
13210          so it would trigger a change event at its previous field location */
13211       if (!player->is_pushing)
13212         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13213
13214       if (level.finish_dig_collect &&
13215           (player->is_digging || player->is_collecting))
13216       {
13217         int last_element = player->last_removed_element;
13218         int move_direction = player->MovDir;
13219         int enter_side = MV_DIR_OPPOSITE(move_direction);
13220         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13221                             CE_PLAYER_COLLECTS_X);
13222
13223         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13224                                             player->index_bit, enter_side);
13225
13226         player->last_removed_element = EL_UNDEFINED;
13227       }
13228
13229       if (!player->active)
13230         RemovePlayer(player);
13231     }
13232
13233     if (level.use_step_counter)
13234       CheckLevelTime_StepCounter();
13235
13236     if (tape.single_step && tape.recording && !tape.pausing &&
13237         !player->programmed_action)
13238       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13239
13240     if (!player->programmed_action)
13241       CheckSaveEngineSnapshot(player);
13242   }
13243 }
13244
13245 void ScrollScreen(struct PlayerInfo *player, int mode)
13246 {
13247   static DelayCounter screen_frame_counter = { 0 };
13248
13249   if (mode == SCROLL_INIT)
13250   {
13251     // set scrolling step size according to actual player's moving speed
13252     ScrollStepSize = TILEX / player->move_delay_value;
13253
13254     screen_frame_counter.count = FrameCounter;
13255     screen_frame_counter.value = 1;
13256
13257     ScreenMovDir = player->MovDir;
13258     ScreenMovPos = player->MovPos;
13259     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13260     return;
13261   }
13262   else if (!FrameReached(&screen_frame_counter))
13263     return;
13264
13265   if (ScreenMovPos)
13266   {
13267     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13268     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13269     redraw_mask |= REDRAW_FIELD;
13270   }
13271   else
13272     ScreenMovDir = MV_NONE;
13273 }
13274
13275 void CheckNextToConditions(int x, int y)
13276 {
13277   int element = Tile[x][y];
13278
13279   if (IS_PLAYER(x, y))
13280     TestIfPlayerNextToCustomElement(x, y);
13281
13282   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13283       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13284     TestIfElementNextToCustomElement(x, y);
13285 }
13286
13287 void TestIfPlayerNextToCustomElement(int x, int y)
13288 {
13289   struct XY *xy = xy_topdown;
13290   static int trigger_sides[4][2] =
13291   {
13292     // center side       border side
13293     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13294     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13295     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13296     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13297   };
13298   int i;
13299
13300   if (!IS_PLAYER(x, y))
13301     return;
13302
13303   struct PlayerInfo *player = PLAYERINFO(x, y);
13304
13305   if (player->is_moving)
13306     return;
13307
13308   for (i = 0; i < NUM_DIRECTIONS; i++)
13309   {
13310     int xx = x + xy[i].x;
13311     int yy = y + xy[i].y;
13312     int border_side = trigger_sides[i][1];
13313     int border_element;
13314
13315     if (!IN_LEV_FIELD(xx, yy))
13316       continue;
13317
13318     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13319       continue;         // center and border element not connected
13320
13321     border_element = Tile[xx][yy];
13322
13323     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13324                                player->index_bit, border_side);
13325     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13326                                         CE_PLAYER_NEXT_TO_X,
13327                                         player->index_bit, border_side);
13328
13329     /* use player element that is initially defined in the level playfield,
13330        not the player element that corresponds to the runtime player number
13331        (example: a level that contains EL_PLAYER_3 as the only player would
13332        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13333
13334     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13335                              CE_NEXT_TO_X, border_side);
13336   }
13337 }
13338
13339 void TestIfPlayerTouchesCustomElement(int x, int y)
13340 {
13341   struct XY *xy = xy_topdown;
13342   static int trigger_sides[4][2] =
13343   {
13344     // center side       border side
13345     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13346     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13347     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13348     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13349   };
13350   static int touch_dir[4] =
13351   {
13352     MV_LEFT | MV_RIGHT,
13353     MV_UP   | MV_DOWN,
13354     MV_UP   | MV_DOWN,
13355     MV_LEFT | MV_RIGHT
13356   };
13357   int center_element = Tile[x][y];      // should always be non-moving!
13358   int i;
13359
13360   for (i = 0; i < NUM_DIRECTIONS; i++)
13361   {
13362     int xx = x + xy[i].x;
13363     int yy = y + xy[i].y;
13364     int center_side = trigger_sides[i][0];
13365     int border_side = trigger_sides[i][1];
13366     int border_element;
13367
13368     if (!IN_LEV_FIELD(xx, yy))
13369       continue;
13370
13371     if (IS_PLAYER(x, y))                // player found at center element
13372     {
13373       struct PlayerInfo *player = PLAYERINFO(x, y);
13374
13375       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13376         border_element = Tile[xx][yy];          // may be moving!
13377       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13378         border_element = Tile[xx][yy];
13379       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13380         border_element = MovingOrBlocked2Element(xx, yy);
13381       else
13382         continue;               // center and border element do not touch
13383
13384       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13385                                  player->index_bit, border_side);
13386       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13387                                           CE_PLAYER_TOUCHES_X,
13388                                           player->index_bit, border_side);
13389
13390       {
13391         /* use player element that is initially defined in the level playfield,
13392            not the player element that corresponds to the runtime player number
13393            (example: a level that contains EL_PLAYER_3 as the only player would
13394            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13395         int player_element = PLAYERINFO(x, y)->initial_element;
13396
13397         CheckElementChangeBySide(xx, yy, border_element, player_element,
13398                                  CE_TOUCHING_X, border_side);
13399       }
13400     }
13401     else if (IS_PLAYER(xx, yy))         // player found at border element
13402     {
13403       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13404
13405       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13406       {
13407         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13408           continue;             // center and border element do not touch
13409       }
13410
13411       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13412                                  player->index_bit, center_side);
13413       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13414                                           CE_PLAYER_TOUCHES_X,
13415                                           player->index_bit, center_side);
13416
13417       {
13418         /* use player element that is initially defined in the level playfield,
13419            not the player element that corresponds to the runtime player number
13420            (example: a level that contains EL_PLAYER_3 as the only player would
13421            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13422         int player_element = PLAYERINFO(xx, yy)->initial_element;
13423
13424         CheckElementChangeBySide(x, y, center_element, player_element,
13425                                  CE_TOUCHING_X, center_side);
13426       }
13427
13428       break;
13429     }
13430   }
13431 }
13432
13433 void TestIfElementNextToCustomElement(int x, int y)
13434 {
13435   struct XY *xy = xy_topdown;
13436   static int trigger_sides[4][2] =
13437   {
13438     // center side      border side
13439     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13440     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13441     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13442     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13443   };
13444   int center_element = Tile[x][y];      // should always be non-moving!
13445   int i;
13446
13447   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13448     return;
13449
13450   for (i = 0; i < NUM_DIRECTIONS; i++)
13451   {
13452     int xx = x + xy[i].x;
13453     int yy = y + xy[i].y;
13454     int border_side = trigger_sides[i][1];
13455     int border_element;
13456
13457     if (!IN_LEV_FIELD(xx, yy))
13458       continue;
13459
13460     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13461       continue;                 // center and border element not connected
13462
13463     border_element = Tile[xx][yy];
13464
13465     // check for change of center element (but change it only once)
13466     if (CheckElementChangeBySide(x, y, center_element, border_element,
13467                                  CE_NEXT_TO_X, border_side))
13468       break;
13469   }
13470 }
13471
13472 void TestIfElementTouchesCustomElement(int x, int y)
13473 {
13474   struct XY *xy = xy_topdown;
13475   static int trigger_sides[4][2] =
13476   {
13477     // center side      border side
13478     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13479     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13480     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13481     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13482   };
13483   static int touch_dir[4] =
13484   {
13485     MV_LEFT | MV_RIGHT,
13486     MV_UP   | MV_DOWN,
13487     MV_UP   | MV_DOWN,
13488     MV_LEFT | MV_RIGHT
13489   };
13490   boolean change_center_element = FALSE;
13491   int center_element = Tile[x][y];      // should always be non-moving!
13492   int border_element_old[NUM_DIRECTIONS];
13493   int i;
13494
13495   for (i = 0; i < NUM_DIRECTIONS; i++)
13496   {
13497     int xx = x + xy[i].x;
13498     int yy = y + xy[i].y;
13499     int border_element;
13500
13501     border_element_old[i] = -1;
13502
13503     if (!IN_LEV_FIELD(xx, yy))
13504       continue;
13505
13506     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13507       border_element = Tile[xx][yy];    // may be moving!
13508     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13509       border_element = Tile[xx][yy];
13510     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13511       border_element = MovingOrBlocked2Element(xx, yy);
13512     else
13513       continue;                 // center and border element do not touch
13514
13515     border_element_old[i] = border_element;
13516   }
13517
13518   for (i = 0; i < NUM_DIRECTIONS; i++)
13519   {
13520     int xx = x + xy[i].x;
13521     int yy = y + xy[i].y;
13522     int center_side = trigger_sides[i][0];
13523     int border_element = border_element_old[i];
13524
13525     if (border_element == -1)
13526       continue;
13527
13528     // check for change of border element
13529     CheckElementChangeBySide(xx, yy, border_element, center_element,
13530                              CE_TOUCHING_X, center_side);
13531
13532     // (center element cannot be player, so we dont have to check this here)
13533   }
13534
13535   for (i = 0; i < NUM_DIRECTIONS; i++)
13536   {
13537     int xx = x + xy[i].x;
13538     int yy = y + xy[i].y;
13539     int border_side = trigger_sides[i][1];
13540     int border_element = border_element_old[i];
13541
13542     if (border_element == -1)
13543       continue;
13544
13545     // check for change of center element (but change it only once)
13546     if (!change_center_element)
13547       change_center_element =
13548         CheckElementChangeBySide(x, y, center_element, border_element,
13549                                  CE_TOUCHING_X, border_side);
13550
13551     if (IS_PLAYER(xx, yy))
13552     {
13553       /* use player element that is initially defined in the level playfield,
13554          not the player element that corresponds to the runtime player number
13555          (example: a level that contains EL_PLAYER_3 as the only player would
13556          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13557       int player_element = PLAYERINFO(xx, yy)->initial_element;
13558
13559       CheckElementChangeBySide(x, y, center_element, player_element,
13560                                CE_TOUCHING_X, border_side);
13561     }
13562   }
13563 }
13564
13565 void TestIfElementHitsCustomElement(int x, int y, int direction)
13566 {
13567   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13568   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13569   int hitx = x + dx, hity = y + dy;
13570   int hitting_element = Tile[x][y];
13571   int touched_element;
13572
13573   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13574     return;
13575
13576   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13577                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13578
13579   if (IN_LEV_FIELD(hitx, hity))
13580   {
13581     int opposite_direction = MV_DIR_OPPOSITE(direction);
13582     int hitting_side = direction;
13583     int touched_side = opposite_direction;
13584     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13585                           MovDir[hitx][hity] != direction ||
13586                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13587
13588     object_hit = TRUE;
13589
13590     if (object_hit)
13591     {
13592       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13593                                CE_HITTING_X, touched_side);
13594
13595       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13596                                CE_HIT_BY_X, hitting_side);
13597
13598       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13599                                CE_HIT_BY_SOMETHING, opposite_direction);
13600
13601       if (IS_PLAYER(hitx, hity))
13602       {
13603         /* use player element that is initially defined in the level playfield,
13604            not the player element that corresponds to the runtime player number
13605            (example: a level that contains EL_PLAYER_3 as the only player would
13606            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13607         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13608
13609         CheckElementChangeBySide(x, y, hitting_element, player_element,
13610                                  CE_HITTING_X, touched_side);
13611       }
13612     }
13613   }
13614
13615   // "hitting something" is also true when hitting the playfield border
13616   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13617                            CE_HITTING_SOMETHING, direction);
13618 }
13619
13620 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13621 {
13622   int i, kill_x = -1, kill_y = -1;
13623
13624   int bad_element = -1;
13625   struct XY *test_xy = xy_topdown;
13626   static int test_dir[4] =
13627   {
13628     MV_UP,
13629     MV_LEFT,
13630     MV_RIGHT,
13631     MV_DOWN
13632   };
13633
13634   for (i = 0; i < NUM_DIRECTIONS; i++)
13635   {
13636     int test_x, test_y, test_move_dir, test_element;
13637
13638     test_x = good_x + test_xy[i].x;
13639     test_y = good_y + test_xy[i].y;
13640
13641     if (!IN_LEV_FIELD(test_x, test_y))
13642       continue;
13643
13644     test_move_dir =
13645       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13646
13647     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13648
13649     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13650        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13651     */
13652     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13653         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13654     {
13655       kill_x = test_x;
13656       kill_y = test_y;
13657       bad_element = test_element;
13658
13659       break;
13660     }
13661   }
13662
13663   if (kill_x != -1 || kill_y != -1)
13664   {
13665     if (IS_PLAYER(good_x, good_y))
13666     {
13667       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13668
13669       if (player->shield_deadly_time_left > 0 &&
13670           !IS_INDESTRUCTIBLE(bad_element))
13671         Bang(kill_x, kill_y);
13672       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13673         KillPlayer(player);
13674     }
13675     else
13676       Bang(good_x, good_y);
13677   }
13678 }
13679
13680 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13681 {
13682   int i, kill_x = -1, kill_y = -1;
13683   int bad_element = Tile[bad_x][bad_y];
13684   struct XY *test_xy = xy_topdown;
13685   static int touch_dir[4] =
13686   {
13687     MV_LEFT | MV_RIGHT,
13688     MV_UP   | MV_DOWN,
13689     MV_UP   | MV_DOWN,
13690     MV_LEFT | MV_RIGHT
13691   };
13692   static int test_dir[4] =
13693   {
13694     MV_UP,
13695     MV_LEFT,
13696     MV_RIGHT,
13697     MV_DOWN
13698   };
13699
13700   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13701     return;
13702
13703   for (i = 0; i < NUM_DIRECTIONS; i++)
13704   {
13705     int test_x, test_y, test_move_dir, test_element;
13706
13707     test_x = bad_x + test_xy[i].x;
13708     test_y = bad_y + test_xy[i].y;
13709
13710     if (!IN_LEV_FIELD(test_x, test_y))
13711       continue;
13712
13713     test_move_dir =
13714       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13715
13716     test_element = Tile[test_x][test_y];
13717
13718     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13719        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13720     */
13721     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13722         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13723     {
13724       // good thing is player or penguin that does not move away
13725       if (IS_PLAYER(test_x, test_y))
13726       {
13727         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13728
13729         if (bad_element == EL_ROBOT && player->is_moving)
13730           continue;     // robot does not kill player if he is moving
13731
13732         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13733         {
13734           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13735             continue;           // center and border element do not touch
13736         }
13737
13738         kill_x = test_x;
13739         kill_y = test_y;
13740
13741         break;
13742       }
13743       else if (test_element == EL_PENGUIN)
13744       {
13745         kill_x = test_x;
13746         kill_y = test_y;
13747
13748         break;
13749       }
13750     }
13751   }
13752
13753   if (kill_x != -1 || kill_y != -1)
13754   {
13755     if (IS_PLAYER(kill_x, kill_y))
13756     {
13757       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13758
13759       if (player->shield_deadly_time_left > 0 &&
13760           !IS_INDESTRUCTIBLE(bad_element))
13761         Bang(bad_x, bad_y);
13762       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13763         KillPlayer(player);
13764     }
13765     else
13766       Bang(kill_x, kill_y);
13767   }
13768 }
13769
13770 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13771 {
13772   int bad_element = Tile[bad_x][bad_y];
13773   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13774   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13775   int test_x = bad_x + dx, test_y = bad_y + dy;
13776   int test_move_dir, test_element;
13777   int kill_x = -1, kill_y = -1;
13778
13779   if (!IN_LEV_FIELD(test_x, test_y))
13780     return;
13781
13782   test_move_dir =
13783     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13784
13785   test_element = Tile[test_x][test_y];
13786
13787   if (test_move_dir != bad_move_dir)
13788   {
13789     // good thing can be player or penguin that does not move away
13790     if (IS_PLAYER(test_x, test_y))
13791     {
13792       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13793
13794       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13795          player as being hit when he is moving towards the bad thing, because
13796          the "get hit by" condition would be lost after the player stops) */
13797       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13798         return;         // player moves away from bad thing
13799
13800       kill_x = test_x;
13801       kill_y = test_y;
13802     }
13803     else if (test_element == EL_PENGUIN)
13804     {
13805       kill_x = test_x;
13806       kill_y = test_y;
13807     }
13808   }
13809
13810   if (kill_x != -1 || kill_y != -1)
13811   {
13812     if (IS_PLAYER(kill_x, kill_y))
13813     {
13814       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13815
13816       if (player->shield_deadly_time_left > 0 &&
13817           !IS_INDESTRUCTIBLE(bad_element))
13818         Bang(bad_x, bad_y);
13819       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13820         KillPlayer(player);
13821     }
13822     else
13823       Bang(kill_x, kill_y);
13824   }
13825 }
13826
13827 void TestIfPlayerTouchesBadThing(int x, int y)
13828 {
13829   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13830 }
13831
13832 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13833 {
13834   TestIfGoodThingHitsBadThing(x, y, move_dir);
13835 }
13836
13837 void TestIfBadThingTouchesPlayer(int x, int y)
13838 {
13839   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13840 }
13841
13842 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13843 {
13844   TestIfBadThingHitsGoodThing(x, y, move_dir);
13845 }
13846
13847 void TestIfFriendTouchesBadThing(int x, int y)
13848 {
13849   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13850 }
13851
13852 void TestIfBadThingTouchesFriend(int x, int y)
13853 {
13854   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13855 }
13856
13857 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13858 {
13859   int i, kill_x = bad_x, kill_y = bad_y;
13860   struct XY *xy = xy_topdown;
13861
13862   for (i = 0; i < NUM_DIRECTIONS; i++)
13863   {
13864     int x, y, element;
13865
13866     x = bad_x + xy[i].x;
13867     y = bad_y + xy[i].y;
13868     if (!IN_LEV_FIELD(x, y))
13869       continue;
13870
13871     element = Tile[x][y];
13872     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13873         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13874     {
13875       kill_x = x;
13876       kill_y = y;
13877       break;
13878     }
13879   }
13880
13881   if (kill_x != bad_x || kill_y != bad_y)
13882     Bang(bad_x, bad_y);
13883 }
13884
13885 void KillPlayer(struct PlayerInfo *player)
13886 {
13887   int jx = player->jx, jy = player->jy;
13888
13889   if (!player->active)
13890     return;
13891
13892 #if 0
13893   Debug("game:playing:KillPlayer",
13894         "0: killed == %d, active == %d, reanimated == %d",
13895         player->killed, player->active, player->reanimated);
13896 #endif
13897
13898   /* the following code was introduced to prevent an infinite loop when calling
13899      -> Bang()
13900      -> CheckTriggeredElementChangeExt()
13901      -> ExecuteCustomElementAction()
13902      -> KillPlayer()
13903      -> (infinitely repeating the above sequence of function calls)
13904      which occurs when killing the player while having a CE with the setting
13905      "kill player X when explosion of <player X>"; the solution using a new
13906      field "player->killed" was chosen for backwards compatibility, although
13907      clever use of the fields "player->active" etc. would probably also work */
13908 #if 1
13909   if (player->killed)
13910     return;
13911 #endif
13912
13913   player->killed = TRUE;
13914
13915   // remove accessible field at the player's position
13916   Tile[jx][jy] = EL_EMPTY;
13917
13918   // deactivate shield (else Bang()/Explode() would not work right)
13919   player->shield_normal_time_left = 0;
13920   player->shield_deadly_time_left = 0;
13921
13922 #if 0
13923   Debug("game:playing:KillPlayer",
13924         "1: killed == %d, active == %d, reanimated == %d",
13925         player->killed, player->active, player->reanimated);
13926 #endif
13927
13928   Bang(jx, jy);
13929
13930 #if 0
13931   Debug("game:playing:KillPlayer",
13932         "2: killed == %d, active == %d, reanimated == %d",
13933         player->killed, player->active, player->reanimated);
13934 #endif
13935
13936   if (player->reanimated)       // killed player may have been reanimated
13937     player->killed = player->reanimated = FALSE;
13938   else
13939     BuryPlayer(player);
13940 }
13941
13942 static void KillPlayerUnlessEnemyProtected(int x, int y)
13943 {
13944   if (!PLAYER_ENEMY_PROTECTED(x, y))
13945     KillPlayer(PLAYERINFO(x, y));
13946 }
13947
13948 static void KillPlayerUnlessExplosionProtected(int x, int y)
13949 {
13950   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13951     KillPlayer(PLAYERINFO(x, y));
13952 }
13953
13954 void BuryPlayer(struct PlayerInfo *player)
13955 {
13956   int jx = player->jx, jy = player->jy;
13957
13958   if (!player->active)
13959     return;
13960
13961   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13962   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13963
13964   RemovePlayer(player);
13965
13966   player->buried = TRUE;
13967
13968   if (game.all_players_gone)
13969     game.GameOver = TRUE;
13970 }
13971
13972 void RemovePlayer(struct PlayerInfo *player)
13973 {
13974   int jx = player->jx, jy = player->jy;
13975   int i, found = FALSE;
13976
13977   player->present = FALSE;
13978   player->active = FALSE;
13979
13980   // required for some CE actions (even if the player is not active anymore)
13981   player->MovPos = 0;
13982
13983   if (!ExplodeField[jx][jy])
13984     StorePlayer[jx][jy] = 0;
13985
13986   if (player->is_moving)
13987     TEST_DrawLevelField(player->last_jx, player->last_jy);
13988
13989   for (i = 0; i < MAX_PLAYERS; i++)
13990     if (stored_player[i].active)
13991       found = TRUE;
13992
13993   if (!found)
13994   {
13995     game.all_players_gone = TRUE;
13996     game.GameOver = TRUE;
13997   }
13998
13999   game.exit_x = game.robot_wheel_x = jx;
14000   game.exit_y = game.robot_wheel_y = jy;
14001 }
14002
14003 void ExitPlayer(struct PlayerInfo *player)
14004 {
14005   DrawPlayer(player);   // needed here only to cleanup last field
14006   RemovePlayer(player);
14007
14008   if (game.players_still_needed > 0)
14009     game.players_still_needed--;
14010 }
14011
14012 static void SetFieldForSnapping(int x, int y, int element, int direction,
14013                                 int player_index_bit)
14014 {
14015   struct ElementInfo *ei = &element_info[element];
14016   int direction_bit = MV_DIR_TO_BIT(direction);
14017   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14018   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14019                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14020
14021   Tile[x][y] = EL_ELEMENT_SNAPPING;
14022   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14023   MovDir[x][y] = direction;
14024   Store[x][y] = element;
14025   Store2[x][y] = player_index_bit;
14026
14027   ResetGfxAnimation(x, y);
14028
14029   GfxElement[x][y] = element;
14030   GfxAction[x][y] = action;
14031   GfxDir[x][y] = direction;
14032   GfxFrame[x][y] = -1;
14033 }
14034
14035 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14036                                    int player_index_bit)
14037 {
14038   TestIfElementTouchesCustomElement(x, y);      // for empty space
14039
14040   if (level.finish_dig_collect)
14041   {
14042     int dig_side = MV_DIR_OPPOSITE(direction);
14043     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14044                         CE_PLAYER_COLLECTS_X);
14045
14046     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14047                                         player_index_bit, dig_side);
14048     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14049                                         player_index_bit, dig_side);
14050   }
14051 }
14052
14053 /*
14054   =============================================================================
14055   checkDiagonalPushing()
14056   -----------------------------------------------------------------------------
14057   check if diagonal input device direction results in pushing of object
14058   (by checking if the alternative direction is walkable, diggable, ...)
14059   =============================================================================
14060 */
14061
14062 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14063                                     int x, int y, int real_dx, int real_dy)
14064 {
14065   int jx, jy, dx, dy, xx, yy;
14066
14067   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14068     return TRUE;
14069
14070   // diagonal direction: check alternative direction
14071   jx = player->jx;
14072   jy = player->jy;
14073   dx = x - jx;
14074   dy = y - jy;
14075   xx = jx + (dx == 0 ? real_dx : 0);
14076   yy = jy + (dy == 0 ? real_dy : 0);
14077
14078   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14079 }
14080
14081 /*
14082   =============================================================================
14083   DigField()
14084   -----------------------------------------------------------------------------
14085   x, y:                 field next to player (non-diagonal) to try to dig to
14086   real_dx, real_dy:     direction as read from input device (can be diagonal)
14087   =============================================================================
14088 */
14089
14090 static int DigField(struct PlayerInfo *player,
14091                     int oldx, int oldy, int x, int y,
14092                     int real_dx, int real_dy, int mode)
14093 {
14094   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14095   boolean player_was_pushing = player->is_pushing;
14096   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14097   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14098   int jx = oldx, jy = oldy;
14099   int dx = x - jx, dy = y - jy;
14100   int nextx = x + dx, nexty = y + dy;
14101   int move_direction = (dx == -1 ? MV_LEFT  :
14102                         dx == +1 ? MV_RIGHT :
14103                         dy == -1 ? MV_UP    :
14104                         dy == +1 ? MV_DOWN  : MV_NONE);
14105   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14106   int dig_side = MV_DIR_OPPOSITE(move_direction);
14107   int old_element = Tile[jx][jy];
14108   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14109   int collect_count;
14110
14111   if (is_player)                // function can also be called by EL_PENGUIN
14112   {
14113     if (player->MovPos == 0)
14114     {
14115       player->is_digging = FALSE;
14116       player->is_collecting = FALSE;
14117     }
14118
14119     if (player->MovPos == 0)    // last pushing move finished
14120       player->is_pushing = FALSE;
14121
14122     if (mode == DF_NO_PUSH)     // player just stopped pushing
14123     {
14124       player->is_switching = FALSE;
14125       player->push_delay = -1;
14126
14127       return MP_NO_ACTION;
14128     }
14129   }
14130   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14131     old_element = Back[jx][jy];
14132
14133   // in case of element dropped at player position, check background
14134   else if (Back[jx][jy] != EL_EMPTY &&
14135            game.engine_version >= VERSION_IDENT(2,2,0,0))
14136     old_element = Back[jx][jy];
14137
14138   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14139     return MP_NO_ACTION;        // field has no opening in this direction
14140
14141   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14142     return MP_NO_ACTION;        // field has no opening in this direction
14143
14144   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14145   {
14146     SplashAcid(x, y);
14147
14148     Tile[jx][jy] = player->artwork_element;
14149     InitMovingField(jx, jy, MV_DOWN);
14150     Store[jx][jy] = EL_ACID;
14151     ContinueMoving(jx, jy);
14152     BuryPlayer(player);
14153
14154     return MP_DONT_RUN_INTO;
14155   }
14156
14157   if (player_can_move && DONT_RUN_INTO(element))
14158   {
14159     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14160
14161     return MP_DONT_RUN_INTO;
14162   }
14163
14164   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14165     return MP_NO_ACTION;
14166
14167   collect_count = element_info[element].collect_count_initial;
14168
14169   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14170     return MP_NO_ACTION;
14171
14172   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14173     player_can_move = player_can_move_or_snap;
14174
14175   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14176       game.engine_version >= VERSION_IDENT(2,2,0,0))
14177   {
14178     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14179                                player->index_bit, dig_side);
14180     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14181                                         player->index_bit, dig_side);
14182
14183     if (element == EL_DC_LANDMINE)
14184       Bang(x, y);
14185
14186     if (Tile[x][y] != element)          // field changed by snapping
14187       return MP_ACTION;
14188
14189     return MP_NO_ACTION;
14190   }
14191
14192   if (player->gravity && is_player && !player->is_auto_moving &&
14193       canFallDown(player) && move_direction != MV_DOWN &&
14194       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14195     return MP_NO_ACTION;        // player cannot walk here due to gravity
14196
14197   if (player_can_move &&
14198       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14199   {
14200     int sound_element = SND_ELEMENT(element);
14201     int sound_action = ACTION_WALKING;
14202
14203     if (IS_RND_GATE(element))
14204     {
14205       if (!player->key[RND_GATE_NR(element)])
14206         return MP_NO_ACTION;
14207     }
14208     else if (IS_RND_GATE_GRAY(element))
14209     {
14210       if (!player->key[RND_GATE_GRAY_NR(element)])
14211         return MP_NO_ACTION;
14212     }
14213     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14214     {
14215       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14216         return MP_NO_ACTION;
14217     }
14218     else if (element == EL_EXIT_OPEN ||
14219              element == EL_EM_EXIT_OPEN ||
14220              element == EL_EM_EXIT_OPENING ||
14221              element == EL_STEEL_EXIT_OPEN ||
14222              element == EL_EM_STEEL_EXIT_OPEN ||
14223              element == EL_EM_STEEL_EXIT_OPENING ||
14224              element == EL_SP_EXIT_OPEN ||
14225              element == EL_SP_EXIT_OPENING)
14226     {
14227       sound_action = ACTION_PASSING;    // player is passing exit
14228     }
14229     else if (element == EL_EMPTY)
14230     {
14231       sound_action = ACTION_MOVING;             // nothing to walk on
14232     }
14233
14234     // play sound from background or player, whatever is available
14235     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14236       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14237     else
14238       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14239   }
14240   else if (player_can_move &&
14241            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14242   {
14243     if (!ACCESS_FROM(element, opposite_direction))
14244       return MP_NO_ACTION;      // field not accessible from this direction
14245
14246     if (CAN_MOVE(element))      // only fixed elements can be passed!
14247       return MP_NO_ACTION;
14248
14249     if (IS_EM_GATE(element))
14250     {
14251       if (!player->key[EM_GATE_NR(element)])
14252         return MP_NO_ACTION;
14253     }
14254     else if (IS_EM_GATE_GRAY(element))
14255     {
14256       if (!player->key[EM_GATE_GRAY_NR(element)])
14257         return MP_NO_ACTION;
14258     }
14259     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14260     {
14261       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14262         return MP_NO_ACTION;
14263     }
14264     else if (IS_EMC_GATE(element))
14265     {
14266       if (!player->key[EMC_GATE_NR(element)])
14267         return MP_NO_ACTION;
14268     }
14269     else if (IS_EMC_GATE_GRAY(element))
14270     {
14271       if (!player->key[EMC_GATE_GRAY_NR(element)])
14272         return MP_NO_ACTION;
14273     }
14274     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14275     {
14276       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14277         return MP_NO_ACTION;
14278     }
14279     else if (element == EL_DC_GATE_WHITE ||
14280              element == EL_DC_GATE_WHITE_GRAY ||
14281              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14282     {
14283       if (player->num_white_keys == 0)
14284         return MP_NO_ACTION;
14285
14286       player->num_white_keys--;
14287     }
14288     else if (IS_SP_PORT(element))
14289     {
14290       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14291           element == EL_SP_GRAVITY_PORT_RIGHT ||
14292           element == EL_SP_GRAVITY_PORT_UP ||
14293           element == EL_SP_GRAVITY_PORT_DOWN)
14294         player->gravity = !player->gravity;
14295       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14296                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14297                element == EL_SP_GRAVITY_ON_PORT_UP ||
14298                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14299         player->gravity = TRUE;
14300       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14301                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14302                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14303                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14304         player->gravity = FALSE;
14305     }
14306
14307     // automatically move to the next field with double speed
14308     player->programmed_action = move_direction;
14309
14310     if (player->move_delay_reset_counter == 0)
14311     {
14312       player->move_delay_reset_counter = 2;     // two double speed steps
14313
14314       DOUBLE_PLAYER_SPEED(player);
14315     }
14316
14317     PlayLevelSoundAction(x, y, ACTION_PASSING);
14318   }
14319   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14320   {
14321     RemoveField(x, y);
14322
14323     if (mode != DF_SNAP)
14324     {
14325       GfxElement[x][y] = GFX_ELEMENT(element);
14326       player->is_digging = TRUE;
14327     }
14328
14329     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14330
14331     // use old behaviour for old levels (digging)
14332     if (!level.finish_dig_collect)
14333     {
14334       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14335                                           player->index_bit, dig_side);
14336
14337       // if digging triggered player relocation, finish digging tile
14338       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14339         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14340     }
14341
14342     if (mode == DF_SNAP)
14343     {
14344       if (level.block_snap_field)
14345         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14346       else
14347         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14348
14349       // use old behaviour for old levels (snapping)
14350       if (!level.finish_dig_collect)
14351         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14352                                             player->index_bit, dig_side);
14353     }
14354   }
14355   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14356   {
14357     RemoveField(x, y);
14358
14359     if (is_player && mode != DF_SNAP)
14360     {
14361       GfxElement[x][y] = element;
14362       player->is_collecting = TRUE;
14363     }
14364
14365     if (element == EL_SPEED_PILL)
14366     {
14367       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14368     }
14369     else if (element == EL_EXTRA_TIME && level.time > 0)
14370     {
14371       TimeLeft += level.extra_time;
14372
14373       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14374
14375       DisplayGameControlValues();
14376     }
14377     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14378     {
14379       int shield_time = (element == EL_SHIELD_DEADLY ?
14380                          level.shield_deadly_time :
14381                          level.shield_normal_time);
14382
14383       player->shield_normal_time_left += shield_time;
14384       if (element == EL_SHIELD_DEADLY)
14385         player->shield_deadly_time_left += shield_time;
14386     }
14387     else if (element == EL_DYNAMITE ||
14388              element == EL_EM_DYNAMITE ||
14389              element == EL_SP_DISK_RED)
14390     {
14391       if (player->inventory_size < MAX_INVENTORY_SIZE)
14392         player->inventory_element[player->inventory_size++] = element;
14393
14394       DrawGameDoorValues();
14395     }
14396     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14397     {
14398       player->dynabomb_count++;
14399       player->dynabombs_left++;
14400     }
14401     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14402     {
14403       player->dynabomb_size++;
14404     }
14405     else if (element == EL_DYNABOMB_INCREASE_POWER)
14406     {
14407       player->dynabomb_xl = TRUE;
14408     }
14409     else if (IS_KEY(element))
14410     {
14411       player->key[KEY_NR(element)] = TRUE;
14412
14413       DrawGameDoorValues();
14414     }
14415     else if (element == EL_DC_KEY_WHITE)
14416     {
14417       player->num_white_keys++;
14418
14419       // display white keys?
14420       // DrawGameDoorValues();
14421     }
14422     else if (IS_ENVELOPE(element))
14423     {
14424       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14425
14426       if (!wait_for_snapping)
14427         player->show_envelope = element;
14428     }
14429     else if (element == EL_EMC_LENSES)
14430     {
14431       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14432
14433       RedrawAllInvisibleElementsForLenses();
14434     }
14435     else if (element == EL_EMC_MAGNIFIER)
14436     {
14437       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14438
14439       RedrawAllInvisibleElementsForMagnifier();
14440     }
14441     else if (IS_DROPPABLE(element) ||
14442              IS_THROWABLE(element))     // can be collected and dropped
14443     {
14444       int i;
14445
14446       if (collect_count == 0)
14447         player->inventory_infinite_element = element;
14448       else
14449         for (i = 0; i < collect_count; i++)
14450           if (player->inventory_size < MAX_INVENTORY_SIZE)
14451             player->inventory_element[player->inventory_size++] = element;
14452
14453       DrawGameDoorValues();
14454     }
14455     else if (collect_count > 0)
14456     {
14457       game.gems_still_needed -= collect_count;
14458       if (game.gems_still_needed < 0)
14459         game.gems_still_needed = 0;
14460
14461       game.snapshot.collected_item = TRUE;
14462
14463       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14464
14465       DisplayGameControlValues();
14466     }
14467
14468     RaiseScoreElement(element);
14469     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14470
14471     // use old behaviour for old levels (collecting)
14472     if (!level.finish_dig_collect && is_player)
14473     {
14474       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14475                                           player->index_bit, dig_side);
14476
14477       // if collecting triggered player relocation, finish collecting tile
14478       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14479         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14480     }
14481
14482     if (mode == DF_SNAP)
14483     {
14484       if (level.block_snap_field)
14485         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14486       else
14487         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14488
14489       // use old behaviour for old levels (snapping)
14490       if (!level.finish_dig_collect)
14491         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14492                                             player->index_bit, dig_side);
14493     }
14494   }
14495   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14496   {
14497     if (mode == DF_SNAP && element != EL_BD_ROCK)
14498       return MP_NO_ACTION;
14499
14500     if (CAN_FALL(element) && dy)
14501       return MP_NO_ACTION;
14502
14503     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14504         !(element == EL_SPRING && level.use_spring_bug))
14505       return MP_NO_ACTION;
14506
14507     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14508         ((move_direction & MV_VERTICAL &&
14509           ((element_info[element].move_pattern & MV_LEFT &&
14510             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14511            (element_info[element].move_pattern & MV_RIGHT &&
14512             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14513          (move_direction & MV_HORIZONTAL &&
14514           ((element_info[element].move_pattern & MV_UP &&
14515             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14516            (element_info[element].move_pattern & MV_DOWN &&
14517             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14518       return MP_NO_ACTION;
14519
14520     // do not push elements already moving away faster than player
14521     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14522         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14523       return MP_NO_ACTION;
14524
14525     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14526     {
14527       if (player->push_delay_value == -1 || !player_was_pushing)
14528         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14529     }
14530     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14531     {
14532       if (player->push_delay_value == -1)
14533         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14534     }
14535     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14536     {
14537       if (!player->is_pushing)
14538         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14539     }
14540
14541     player->is_pushing = TRUE;
14542     player->is_active = TRUE;
14543
14544     if (!(IN_LEV_FIELD(nextx, nexty) &&
14545           (IS_FREE(nextx, nexty) ||
14546            (IS_SB_ELEMENT(element) &&
14547             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14548            (IS_CUSTOM_ELEMENT(element) &&
14549             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14550       return MP_NO_ACTION;
14551
14552     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14553       return MP_NO_ACTION;
14554
14555     if (player->push_delay == -1)       // new pushing; restart delay
14556       player->push_delay = 0;
14557
14558     if (player->push_delay < player->push_delay_value &&
14559         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14560         element != EL_SPRING && element != EL_BALLOON)
14561     {
14562       // make sure that there is no move delay before next try to push
14563       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14564         player->move_delay = 0;
14565
14566       return MP_NO_ACTION;
14567     }
14568
14569     if (IS_CUSTOM_ELEMENT(element) &&
14570         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14571     {
14572       if (!DigFieldByCE(nextx, nexty, element))
14573         return MP_NO_ACTION;
14574     }
14575
14576     if (IS_SB_ELEMENT(element))
14577     {
14578       boolean sokoban_task_solved = FALSE;
14579
14580       if (element == EL_SOKOBAN_FIELD_FULL)
14581       {
14582         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14583
14584         IncrementSokobanFieldsNeeded();
14585         IncrementSokobanObjectsNeeded();
14586       }
14587
14588       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14589       {
14590         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14591
14592         DecrementSokobanFieldsNeeded();
14593         DecrementSokobanObjectsNeeded();
14594
14595         // sokoban object was pushed from empty field to sokoban field
14596         if (Back[x][y] == EL_EMPTY)
14597           sokoban_task_solved = TRUE;
14598       }
14599
14600       Tile[x][y] = EL_SOKOBAN_OBJECT;
14601
14602       if (Back[x][y] == Back[nextx][nexty])
14603         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14604       else if (Back[x][y] != 0)
14605         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14606                                     ACTION_EMPTYING);
14607       else
14608         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14609                                     ACTION_FILLING);
14610
14611       if (sokoban_task_solved &&
14612           game.sokoban_fields_still_needed == 0 &&
14613           game.sokoban_objects_still_needed == 0 &&
14614           level.auto_exit_sokoban)
14615       {
14616         game.players_still_needed = 0;
14617
14618         LevelSolved();
14619
14620         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14621       }
14622     }
14623     else
14624       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14625
14626     InitMovingField(x, y, move_direction);
14627     GfxAction[x][y] = ACTION_PUSHING;
14628
14629     if (mode == DF_SNAP)
14630       ContinueMoving(x, y);
14631     else
14632       MovPos[x][y] = (dx != 0 ? dx : dy);
14633
14634     Pushed[x][y] = TRUE;
14635     Pushed[nextx][nexty] = TRUE;
14636
14637     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14638       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14639     else
14640       player->push_delay_value = -1;    // get new value later
14641
14642     // check for element change _after_ element has been pushed
14643     if (game.use_change_when_pushing_bug)
14644     {
14645       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14646                                  player->index_bit, dig_side);
14647       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14648                                           player->index_bit, dig_side);
14649     }
14650   }
14651   else if (IS_SWITCHABLE(element))
14652   {
14653     if (PLAYER_SWITCHING(player, x, y))
14654     {
14655       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14656                                           player->index_bit, dig_side);
14657
14658       return MP_ACTION;
14659     }
14660
14661     player->is_switching = TRUE;
14662     player->switch_x = x;
14663     player->switch_y = y;
14664
14665     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14666
14667     if (element == EL_ROBOT_WHEEL)
14668     {
14669       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14670
14671       game.robot_wheel_x = x;
14672       game.robot_wheel_y = y;
14673       game.robot_wheel_active = TRUE;
14674
14675       TEST_DrawLevelField(x, y);
14676     }
14677     else if (element == EL_SP_TERMINAL)
14678     {
14679       int xx, yy;
14680
14681       SCAN_PLAYFIELD(xx, yy)
14682       {
14683         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14684         {
14685           Bang(xx, yy);
14686         }
14687         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14688         {
14689           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14690
14691           ResetGfxAnimation(xx, yy);
14692           TEST_DrawLevelField(xx, yy);
14693         }
14694       }
14695     }
14696     else if (IS_BELT_SWITCH(element))
14697     {
14698       ToggleBeltSwitch(x, y);
14699     }
14700     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14701              element == EL_SWITCHGATE_SWITCH_DOWN ||
14702              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14703              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14704     {
14705       ToggleSwitchgateSwitch();
14706     }
14707     else if (element == EL_LIGHT_SWITCH ||
14708              element == EL_LIGHT_SWITCH_ACTIVE)
14709     {
14710       ToggleLightSwitch(x, y);
14711     }
14712     else if (element == EL_TIMEGATE_SWITCH ||
14713              element == EL_DC_TIMEGATE_SWITCH)
14714     {
14715       ActivateTimegateSwitch(x, y);
14716     }
14717     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14718              element == EL_BALLOON_SWITCH_RIGHT ||
14719              element == EL_BALLOON_SWITCH_UP    ||
14720              element == EL_BALLOON_SWITCH_DOWN  ||
14721              element == EL_BALLOON_SWITCH_NONE  ||
14722              element == EL_BALLOON_SWITCH_ANY)
14723     {
14724       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14725                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14726                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14727                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14728                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14729                              move_direction);
14730     }
14731     else if (element == EL_LAMP)
14732     {
14733       Tile[x][y] = EL_LAMP_ACTIVE;
14734       game.lights_still_needed--;
14735
14736       ResetGfxAnimation(x, y);
14737       TEST_DrawLevelField(x, y);
14738     }
14739     else if (element == EL_TIME_ORB_FULL)
14740     {
14741       Tile[x][y] = EL_TIME_ORB_EMPTY;
14742
14743       if (level.time > 0 || level.use_time_orb_bug)
14744       {
14745         TimeLeft += level.time_orb_time;
14746         game.no_level_time_limit = FALSE;
14747
14748         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14749
14750         DisplayGameControlValues();
14751       }
14752
14753       ResetGfxAnimation(x, y);
14754       TEST_DrawLevelField(x, y);
14755     }
14756     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14757              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14758     {
14759       int xx, yy;
14760
14761       game.ball_active = !game.ball_active;
14762
14763       SCAN_PLAYFIELD(xx, yy)
14764       {
14765         int e = Tile[xx][yy];
14766
14767         if (game.ball_active)
14768         {
14769           if (e == EL_EMC_MAGIC_BALL)
14770             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14771           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14772             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14773         }
14774         else
14775         {
14776           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14777             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14778           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14779             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14780         }
14781       }
14782     }
14783
14784     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14785                                         player->index_bit, dig_side);
14786
14787     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14788                                         player->index_bit, dig_side);
14789
14790     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14791                                         player->index_bit, dig_side);
14792
14793     return MP_ACTION;
14794   }
14795   else
14796   {
14797     if (!PLAYER_SWITCHING(player, x, y))
14798     {
14799       player->is_switching = TRUE;
14800       player->switch_x = x;
14801       player->switch_y = y;
14802
14803       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14804                                  player->index_bit, dig_side);
14805       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14806                                           player->index_bit, dig_side);
14807
14808       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14809                                  player->index_bit, dig_side);
14810       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14811                                           player->index_bit, dig_side);
14812     }
14813
14814     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14815                                player->index_bit, dig_side);
14816     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14817                                         player->index_bit, dig_side);
14818
14819     return MP_NO_ACTION;
14820   }
14821
14822   player->push_delay = -1;
14823
14824   if (is_player)                // function can also be called by EL_PENGUIN
14825   {
14826     if (Tile[x][y] != element)          // really digged/collected something
14827     {
14828       player->is_collecting = !player->is_digging;
14829       player->is_active = TRUE;
14830
14831       player->last_removed_element = element;
14832     }
14833   }
14834
14835   return MP_MOVING;
14836 }
14837
14838 static boolean DigFieldByCE(int x, int y, int digging_element)
14839 {
14840   int element = Tile[x][y];
14841
14842   if (!IS_FREE(x, y))
14843   {
14844     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14845                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14846                   ACTION_BREAKING);
14847
14848     // no element can dig solid indestructible elements
14849     if (IS_INDESTRUCTIBLE(element) &&
14850         !IS_DIGGABLE(element) &&
14851         !IS_COLLECTIBLE(element))
14852       return FALSE;
14853
14854     if (AmoebaNr[x][y] &&
14855         (element == EL_AMOEBA_FULL ||
14856          element == EL_BD_AMOEBA ||
14857          element == EL_AMOEBA_GROWING))
14858     {
14859       AmoebaCnt[AmoebaNr[x][y]]--;
14860       AmoebaCnt2[AmoebaNr[x][y]]--;
14861     }
14862
14863     if (IS_MOVING(x, y))
14864       RemoveMovingField(x, y);
14865     else
14866     {
14867       RemoveField(x, y);
14868       TEST_DrawLevelField(x, y);
14869     }
14870
14871     // if digged element was about to explode, prevent the explosion
14872     ExplodeField[x][y] = EX_TYPE_NONE;
14873
14874     PlayLevelSoundAction(x, y, action);
14875   }
14876
14877   Store[x][y] = EL_EMPTY;
14878
14879   // this makes it possible to leave the removed element again
14880   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14881     Store[x][y] = element;
14882
14883   return TRUE;
14884 }
14885
14886 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14887 {
14888   int jx = player->jx, jy = player->jy;
14889   int x = jx + dx, y = jy + dy;
14890   int snap_direction = (dx == -1 ? MV_LEFT  :
14891                         dx == +1 ? MV_RIGHT :
14892                         dy == -1 ? MV_UP    :
14893                         dy == +1 ? MV_DOWN  : MV_NONE);
14894   boolean can_continue_snapping = (level.continuous_snapping &&
14895                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14896
14897   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14898     return FALSE;
14899
14900   if (!player->active || !IN_LEV_FIELD(x, y))
14901     return FALSE;
14902
14903   if (dx && dy)
14904     return FALSE;
14905
14906   if (!dx && !dy)
14907   {
14908     if (player->MovPos == 0)
14909       player->is_pushing = FALSE;
14910
14911     player->is_snapping = FALSE;
14912
14913     if (player->MovPos == 0)
14914     {
14915       player->is_moving = FALSE;
14916       player->is_digging = FALSE;
14917       player->is_collecting = FALSE;
14918     }
14919
14920     return FALSE;
14921   }
14922
14923   // prevent snapping with already pressed snap key when not allowed
14924   if (player->is_snapping && !can_continue_snapping)
14925     return FALSE;
14926
14927   player->MovDir = snap_direction;
14928
14929   if (player->MovPos == 0)
14930   {
14931     player->is_moving = FALSE;
14932     player->is_digging = FALSE;
14933     player->is_collecting = FALSE;
14934   }
14935
14936   player->is_dropping = FALSE;
14937   player->is_dropping_pressed = FALSE;
14938   player->drop_pressed_delay = 0;
14939
14940   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14941     return FALSE;
14942
14943   player->is_snapping = TRUE;
14944   player->is_active = TRUE;
14945
14946   if (player->MovPos == 0)
14947   {
14948     player->is_moving = FALSE;
14949     player->is_digging = FALSE;
14950     player->is_collecting = FALSE;
14951   }
14952
14953   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14954     TEST_DrawLevelField(player->last_jx, player->last_jy);
14955
14956   TEST_DrawLevelField(x, y);
14957
14958   return TRUE;
14959 }
14960
14961 static boolean DropElement(struct PlayerInfo *player)
14962 {
14963   int old_element, new_element;
14964   int dropx = player->jx, dropy = player->jy;
14965   int drop_direction = player->MovDir;
14966   int drop_side = drop_direction;
14967   int drop_element = get_next_dropped_element(player);
14968
14969   /* do not drop an element on top of another element; when holding drop key
14970      pressed without moving, dropped element must move away before the next
14971      element can be dropped (this is especially important if the next element
14972      is dynamite, which can be placed on background for historical reasons) */
14973   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14974     return MP_ACTION;
14975
14976   if (IS_THROWABLE(drop_element))
14977   {
14978     dropx += GET_DX_FROM_DIR(drop_direction);
14979     dropy += GET_DY_FROM_DIR(drop_direction);
14980
14981     if (!IN_LEV_FIELD(dropx, dropy))
14982       return FALSE;
14983   }
14984
14985   old_element = Tile[dropx][dropy];     // old element at dropping position
14986   new_element = drop_element;           // default: no change when dropping
14987
14988   // check if player is active, not moving and ready to drop
14989   if (!player->active || player->MovPos || player->drop_delay > 0)
14990     return FALSE;
14991
14992   // check if player has anything that can be dropped
14993   if (new_element == EL_UNDEFINED)
14994     return FALSE;
14995
14996   // only set if player has anything that can be dropped
14997   player->is_dropping_pressed = TRUE;
14998
14999   // check if drop key was pressed long enough for EM style dynamite
15000   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15001     return FALSE;
15002
15003   // check if anything can be dropped at the current position
15004   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15005     return FALSE;
15006
15007   // collected custom elements can only be dropped on empty fields
15008   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15009     return FALSE;
15010
15011   if (old_element != EL_EMPTY)
15012     Back[dropx][dropy] = old_element;   // store old element on this field
15013
15014   ResetGfxAnimation(dropx, dropy);
15015   ResetRandomAnimationValue(dropx, dropy);
15016
15017   if (player->inventory_size > 0 ||
15018       player->inventory_infinite_element != EL_UNDEFINED)
15019   {
15020     if (player->inventory_size > 0)
15021     {
15022       player->inventory_size--;
15023
15024       DrawGameDoorValues();
15025
15026       if (new_element == EL_DYNAMITE)
15027         new_element = EL_DYNAMITE_ACTIVE;
15028       else if (new_element == EL_EM_DYNAMITE)
15029         new_element = EL_EM_DYNAMITE_ACTIVE;
15030       else if (new_element == EL_SP_DISK_RED)
15031         new_element = EL_SP_DISK_RED_ACTIVE;
15032     }
15033
15034     Tile[dropx][dropy] = new_element;
15035
15036     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15037       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15038                           el2img(Tile[dropx][dropy]), 0);
15039
15040     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15041
15042     // needed if previous element just changed to "empty" in the last frame
15043     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15044
15045     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15046                                player->index_bit, drop_side);
15047     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15048                                         CE_PLAYER_DROPS_X,
15049                                         player->index_bit, drop_side);
15050
15051     TestIfElementTouchesCustomElement(dropx, dropy);
15052   }
15053   else          // player is dropping a dyna bomb
15054   {
15055     player->dynabombs_left--;
15056
15057     Tile[dropx][dropy] = new_element;
15058
15059     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15060       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15061                           el2img(Tile[dropx][dropy]), 0);
15062
15063     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15064   }
15065
15066   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15067     InitField_WithBug1(dropx, dropy, FALSE);
15068
15069   new_element = Tile[dropx][dropy];     // element might have changed
15070
15071   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15072       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15073   {
15074     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15075       MovDir[dropx][dropy] = drop_direction;
15076
15077     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15078
15079     // do not cause impact style collision by dropping elements that can fall
15080     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15081   }
15082
15083   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15084   player->is_dropping = TRUE;
15085
15086   player->drop_pressed_delay = 0;
15087   player->is_dropping_pressed = FALSE;
15088
15089   player->drop_x = dropx;
15090   player->drop_y = dropy;
15091
15092   return TRUE;
15093 }
15094
15095 // ----------------------------------------------------------------------------
15096 // game sound playing functions
15097 // ----------------------------------------------------------------------------
15098
15099 static int *loop_sound_frame = NULL;
15100 static int *loop_sound_volume = NULL;
15101
15102 void InitPlayLevelSound(void)
15103 {
15104   int num_sounds = getSoundListSize();
15105
15106   checked_free(loop_sound_frame);
15107   checked_free(loop_sound_volume);
15108
15109   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15110   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15111 }
15112
15113 static void PlayLevelSound(int x, int y, int nr)
15114 {
15115   int sx = SCREENX(x), sy = SCREENY(y);
15116   int volume, stereo_position;
15117   int max_distance = 8;
15118   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15119
15120   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15121       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15122     return;
15123
15124   if (!IN_LEV_FIELD(x, y) ||
15125       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15126       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15127     return;
15128
15129   volume = SOUND_MAX_VOLUME;
15130
15131   if (!IN_SCR_FIELD(sx, sy))
15132   {
15133     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15134     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15135
15136     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15137   }
15138
15139   stereo_position = (SOUND_MAX_LEFT +
15140                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15141                      (SCR_FIELDX + 2 * max_distance));
15142
15143   if (IS_LOOP_SOUND(nr))
15144   {
15145     /* This assures that quieter loop sounds do not overwrite louder ones,
15146        while restarting sound volume comparison with each new game frame. */
15147
15148     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15149       return;
15150
15151     loop_sound_volume[nr] = volume;
15152     loop_sound_frame[nr] = FrameCounter;
15153   }
15154
15155   PlaySoundExt(nr, volume, stereo_position, type);
15156 }
15157
15158 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15159 {
15160   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15161                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15162                  y < LEVELY(BY1) ? LEVELY(BY1) :
15163                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15164                  sound_action);
15165 }
15166
15167 static void PlayLevelSoundAction(int x, int y, int action)
15168 {
15169   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15170 }
15171
15172 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15173 {
15174   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15175
15176   if (sound_effect != SND_UNDEFINED)
15177     PlayLevelSound(x, y, sound_effect);
15178 }
15179
15180 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15181                                               int action)
15182 {
15183   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15184
15185   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15186     PlayLevelSound(x, y, sound_effect);
15187 }
15188
15189 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15190 {
15191   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15192
15193   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15194     PlayLevelSound(x, y, sound_effect);
15195 }
15196
15197 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15198 {
15199   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15200
15201   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15202     StopSound(sound_effect);
15203 }
15204
15205 static int getLevelMusicNr(void)
15206 {
15207   if (levelset.music[level_nr] != MUS_UNDEFINED)
15208     return levelset.music[level_nr];            // from config file
15209   else
15210     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15211 }
15212
15213 static void FadeLevelSounds(void)
15214 {
15215   FadeSounds();
15216 }
15217
15218 static void FadeLevelMusic(void)
15219 {
15220   int music_nr = getLevelMusicNr();
15221   char *curr_music = getCurrentlyPlayingMusicFilename();
15222   char *next_music = getMusicInfoEntryFilename(music_nr);
15223
15224   if (!strEqual(curr_music, next_music))
15225     FadeMusic();
15226 }
15227
15228 void FadeLevelSoundsAndMusic(void)
15229 {
15230   FadeLevelSounds();
15231   FadeLevelMusic();
15232 }
15233
15234 static void PlayLevelMusic(void)
15235 {
15236   int music_nr = getLevelMusicNr();
15237   char *curr_music = getCurrentlyPlayingMusicFilename();
15238   char *next_music = getMusicInfoEntryFilename(music_nr);
15239
15240   if (!strEqual(curr_music, next_music))
15241     PlayMusicLoop(music_nr);
15242 }
15243
15244 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15245 {
15246   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15247   int offset = 0;
15248   int x = xx - offset;
15249   int y = yy - offset;
15250
15251   switch (sample)
15252   {
15253     case SOUND_blank:
15254       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15255       break;
15256
15257     case SOUND_roll:
15258       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15259       break;
15260
15261     case SOUND_stone:
15262       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15263       break;
15264
15265     case SOUND_nut:
15266       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15267       break;
15268
15269     case SOUND_crack:
15270       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15271       break;
15272
15273     case SOUND_bug:
15274       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15275       break;
15276
15277     case SOUND_tank:
15278       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15279       break;
15280
15281     case SOUND_android_clone:
15282       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15283       break;
15284
15285     case SOUND_android_move:
15286       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15287       break;
15288
15289     case SOUND_spring:
15290       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15291       break;
15292
15293     case SOUND_slurp:
15294       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15295       break;
15296
15297     case SOUND_eater:
15298       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15299       break;
15300
15301     case SOUND_eater_eat:
15302       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15303       break;
15304
15305     case SOUND_alien:
15306       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15307       break;
15308
15309     case SOUND_collect:
15310       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15311       break;
15312
15313     case SOUND_diamond:
15314       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15315       break;
15316
15317     case SOUND_squash:
15318       // !!! CHECK THIS !!!
15319 #if 1
15320       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15321 #else
15322       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15323 #endif
15324       break;
15325
15326     case SOUND_wonderfall:
15327       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15328       break;
15329
15330     case SOUND_drip:
15331       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15332       break;
15333
15334     case SOUND_push:
15335       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15336       break;
15337
15338     case SOUND_dirt:
15339       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15340       break;
15341
15342     case SOUND_acid:
15343       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15344       break;
15345
15346     case SOUND_ball:
15347       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15348       break;
15349
15350     case SOUND_slide:
15351       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15352       break;
15353
15354     case SOUND_wonder:
15355       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15356       break;
15357
15358     case SOUND_door:
15359       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15360       break;
15361
15362     case SOUND_exit_open:
15363       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15364       break;
15365
15366     case SOUND_exit_leave:
15367       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15368       break;
15369
15370     case SOUND_dynamite:
15371       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15372       break;
15373
15374     case SOUND_tick:
15375       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15376       break;
15377
15378     case SOUND_press:
15379       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15380       break;
15381
15382     case SOUND_wheel:
15383       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15384       break;
15385
15386     case SOUND_boom:
15387       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15388       break;
15389
15390     case SOUND_die:
15391       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15392       break;
15393
15394     case SOUND_time:
15395       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15396       break;
15397
15398     default:
15399       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15400       break;
15401   }
15402 }
15403
15404 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15405 {
15406   int element = map_element_SP_to_RND(element_sp);
15407   int action = map_action_SP_to_RND(action_sp);
15408   int offset = (setup.sp_show_border_elements ? 0 : 1);
15409   int x = xx - offset;
15410   int y = yy - offset;
15411
15412   PlayLevelSoundElementAction(x, y, element, action);
15413 }
15414
15415 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15416 {
15417   int element = map_element_MM_to_RND(element_mm);
15418   int action = map_action_MM_to_RND(action_mm);
15419   int offset = 0;
15420   int x = xx - offset;
15421   int y = yy - offset;
15422
15423   if (!IS_MM_ELEMENT(element))
15424     element = EL_MM_DEFAULT;
15425
15426   PlayLevelSoundElementAction(x, y, element, action);
15427 }
15428
15429 void PlaySound_MM(int sound_mm)
15430 {
15431   int sound = map_sound_MM_to_RND(sound_mm);
15432
15433   if (sound == SND_UNDEFINED)
15434     return;
15435
15436   PlaySound(sound);
15437 }
15438
15439 void PlaySoundLoop_MM(int sound_mm)
15440 {
15441   int sound = map_sound_MM_to_RND(sound_mm);
15442
15443   if (sound == SND_UNDEFINED)
15444     return;
15445
15446   PlaySoundLoop(sound);
15447 }
15448
15449 void StopSound_MM(int sound_mm)
15450 {
15451   int sound = map_sound_MM_to_RND(sound_mm);
15452
15453   if (sound == SND_UNDEFINED)
15454     return;
15455
15456   StopSound(sound);
15457 }
15458
15459 void RaiseScore(int value)
15460 {
15461   game.score += value;
15462
15463   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15464
15465   DisplayGameControlValues();
15466 }
15467
15468 void RaiseScoreElement(int element)
15469 {
15470   switch (element)
15471   {
15472     case EL_EMERALD:
15473     case EL_BD_DIAMOND:
15474     case EL_EMERALD_YELLOW:
15475     case EL_EMERALD_RED:
15476     case EL_EMERALD_PURPLE:
15477     case EL_SP_INFOTRON:
15478       RaiseScore(level.score[SC_EMERALD]);
15479       break;
15480     case EL_DIAMOND:
15481       RaiseScore(level.score[SC_DIAMOND]);
15482       break;
15483     case EL_CRYSTAL:
15484       RaiseScore(level.score[SC_CRYSTAL]);
15485       break;
15486     case EL_PEARL:
15487       RaiseScore(level.score[SC_PEARL]);
15488       break;
15489     case EL_BUG:
15490     case EL_BD_BUTTERFLY:
15491     case EL_SP_ELECTRON:
15492       RaiseScore(level.score[SC_BUG]);
15493       break;
15494     case EL_SPACESHIP:
15495     case EL_BD_FIREFLY:
15496     case EL_SP_SNIKSNAK:
15497       RaiseScore(level.score[SC_SPACESHIP]);
15498       break;
15499     case EL_YAMYAM:
15500     case EL_DARK_YAMYAM:
15501       RaiseScore(level.score[SC_YAMYAM]);
15502       break;
15503     case EL_ROBOT:
15504       RaiseScore(level.score[SC_ROBOT]);
15505       break;
15506     case EL_PACMAN:
15507       RaiseScore(level.score[SC_PACMAN]);
15508       break;
15509     case EL_NUT:
15510       RaiseScore(level.score[SC_NUT]);
15511       break;
15512     case EL_DYNAMITE:
15513     case EL_EM_DYNAMITE:
15514     case EL_SP_DISK_RED:
15515     case EL_DYNABOMB_INCREASE_NUMBER:
15516     case EL_DYNABOMB_INCREASE_SIZE:
15517     case EL_DYNABOMB_INCREASE_POWER:
15518       RaiseScore(level.score[SC_DYNAMITE]);
15519       break;
15520     case EL_SHIELD_NORMAL:
15521     case EL_SHIELD_DEADLY:
15522       RaiseScore(level.score[SC_SHIELD]);
15523       break;
15524     case EL_EXTRA_TIME:
15525       RaiseScore(level.extra_time_score);
15526       break;
15527     case EL_KEY_1:
15528     case EL_KEY_2:
15529     case EL_KEY_3:
15530     case EL_KEY_4:
15531     case EL_EM_KEY_1:
15532     case EL_EM_KEY_2:
15533     case EL_EM_KEY_3:
15534     case EL_EM_KEY_4:
15535     case EL_EMC_KEY_5:
15536     case EL_EMC_KEY_6:
15537     case EL_EMC_KEY_7:
15538     case EL_EMC_KEY_8:
15539     case EL_DC_KEY_WHITE:
15540       RaiseScore(level.score[SC_KEY]);
15541       break;
15542     default:
15543       RaiseScore(element_info[element].collect_score);
15544       break;
15545   }
15546 }
15547
15548 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15549 {
15550   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15551   {
15552     if (!quick_quit)
15553     {
15554       // prevent short reactivation of overlay buttons while closing door
15555       SetOverlayActive(FALSE);
15556       UnmapGameButtons();
15557
15558       // door may still be open due to skipped or envelope style request
15559       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15560     }
15561
15562     if (network.enabled)
15563       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15564     else
15565     {
15566       if (quick_quit)
15567         FadeSkipNextFadeIn();
15568
15569       SetGameStatus(GAME_MODE_MAIN);
15570
15571       DrawMainMenu();
15572     }
15573   }
15574   else          // continue playing the game
15575   {
15576     if (tape.playing && tape.deactivate_display)
15577       TapeDeactivateDisplayOff(TRUE);
15578
15579     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15580
15581     if (tape.playing && tape.deactivate_display)
15582       TapeDeactivateDisplayOn();
15583   }
15584 }
15585
15586 void RequestQuitGame(boolean escape_key_pressed)
15587 {
15588   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15589   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15590                         level_editor_test_game);
15591   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15592                           quick_quit || score_info_tape_play);
15593
15594   RequestQuitGameExt(skip_request, quick_quit,
15595                      "Do you really want to quit the game?");
15596 }
15597
15598 void RequestRestartGame(char *message)
15599 {
15600   game.restart_game_message = NULL;
15601
15602   boolean has_started_game = hasStartedNetworkGame();
15603   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15604
15605   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15606   {
15607     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15608   }
15609   else
15610   {
15611     // needed in case of envelope request to close game panel
15612     CloseDoor(DOOR_CLOSE_1);
15613
15614     SetGameStatus(GAME_MODE_MAIN);
15615
15616     DrawMainMenu();
15617   }
15618 }
15619
15620 void CheckGameOver(void)
15621 {
15622   static boolean last_game_over = FALSE;
15623   static int game_over_delay = 0;
15624   int game_over_delay_value = 50;
15625   boolean game_over = checkGameFailed();
15626
15627   // do not handle game over if request dialog is already active
15628   if (game.request_active)
15629     return;
15630
15631   // do not ask to play again if game was never actually played
15632   if (!game.GamePlayed)
15633     return;
15634
15635   if (!game_over)
15636   {
15637     last_game_over = FALSE;
15638     game_over_delay = game_over_delay_value;
15639
15640     return;
15641   }
15642
15643   if (game_over_delay > 0)
15644   {
15645     game_over_delay--;
15646
15647     return;
15648   }
15649
15650   if (last_game_over != game_over)
15651     game.restart_game_message = (hasStartedNetworkGame() ?
15652                                  "Game over! Play it again?" :
15653                                  "Game over!");
15654
15655   last_game_over = game_over;
15656 }
15657
15658 boolean checkGameSolved(void)
15659 {
15660   // set for all game engines if level was solved
15661   return game.LevelSolved_GameEnd;
15662 }
15663
15664 boolean checkGameFailed(void)
15665 {
15666   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15667     return (game_em.game_over && !game_em.level_solved);
15668   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15669     return (game_sp.game_over && !game_sp.level_solved);
15670   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15671     return (game_mm.game_over && !game_mm.level_solved);
15672   else                          // GAME_ENGINE_TYPE_RND
15673     return (game.GameOver && !game.LevelSolved);
15674 }
15675
15676 boolean checkGameEnded(void)
15677 {
15678   return (checkGameSolved() || checkGameFailed());
15679 }
15680
15681
15682 // ----------------------------------------------------------------------------
15683 // random generator functions
15684 // ----------------------------------------------------------------------------
15685
15686 unsigned int InitEngineRandom_RND(int seed)
15687 {
15688   game.num_random_calls = 0;
15689
15690   return InitEngineRandom(seed);
15691 }
15692
15693 unsigned int RND(int max)
15694 {
15695   if (max > 0)
15696   {
15697     game.num_random_calls++;
15698
15699     return GetEngineRandom(max);
15700   }
15701
15702   return 0;
15703 }
15704
15705
15706 // ----------------------------------------------------------------------------
15707 // game engine snapshot handling functions
15708 // ----------------------------------------------------------------------------
15709
15710 struct EngineSnapshotInfo
15711 {
15712   // runtime values for custom element collect score
15713   int collect_score[NUM_CUSTOM_ELEMENTS];
15714
15715   // runtime values for group element choice position
15716   int choice_pos[NUM_GROUP_ELEMENTS];
15717
15718   // runtime values for belt position animations
15719   int belt_graphic[4][NUM_BELT_PARTS];
15720   int belt_anim_mode[4][NUM_BELT_PARTS];
15721 };
15722
15723 static struct EngineSnapshotInfo engine_snapshot_rnd;
15724 static char *snapshot_level_identifier = NULL;
15725 static int snapshot_level_nr = -1;
15726
15727 static void SaveEngineSnapshotValues_RND(void)
15728 {
15729   static int belt_base_active_element[4] =
15730   {
15731     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15732     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15733     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15734     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15735   };
15736   int i, j;
15737
15738   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15739   {
15740     int element = EL_CUSTOM_START + i;
15741
15742     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15743   }
15744
15745   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15746   {
15747     int element = EL_GROUP_START + i;
15748
15749     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15750   }
15751
15752   for (i = 0; i < 4; i++)
15753   {
15754     for (j = 0; j < NUM_BELT_PARTS; j++)
15755     {
15756       int element = belt_base_active_element[i] + j;
15757       int graphic = el2img(element);
15758       int anim_mode = graphic_info[graphic].anim_mode;
15759
15760       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15761       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15762     }
15763   }
15764 }
15765
15766 static void LoadEngineSnapshotValues_RND(void)
15767 {
15768   unsigned int num_random_calls = game.num_random_calls;
15769   int i, j;
15770
15771   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15772   {
15773     int element = EL_CUSTOM_START + i;
15774
15775     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15776   }
15777
15778   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15779   {
15780     int element = EL_GROUP_START + i;
15781
15782     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15783   }
15784
15785   for (i = 0; i < 4; i++)
15786   {
15787     for (j = 0; j < NUM_BELT_PARTS; j++)
15788     {
15789       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15790       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15791
15792       graphic_info[graphic].anim_mode = anim_mode;
15793     }
15794   }
15795
15796   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15797   {
15798     InitRND(tape.random_seed);
15799     for (i = 0; i < num_random_calls; i++)
15800       RND(1);
15801   }
15802
15803   if (game.num_random_calls != num_random_calls)
15804   {
15805     Error("number of random calls out of sync");
15806     Error("number of random calls should be %d", num_random_calls);
15807     Error("number of random calls is %d", game.num_random_calls);
15808
15809     Fail("this should not happen -- please debug");
15810   }
15811 }
15812
15813 void FreeEngineSnapshotSingle(void)
15814 {
15815   FreeSnapshotSingle();
15816
15817   setString(&snapshot_level_identifier, NULL);
15818   snapshot_level_nr = -1;
15819 }
15820
15821 void FreeEngineSnapshotList(void)
15822 {
15823   FreeSnapshotList();
15824 }
15825
15826 static ListNode *SaveEngineSnapshotBuffers(void)
15827 {
15828   ListNode *buffers = NULL;
15829
15830   // copy some special values to a structure better suited for the snapshot
15831
15832   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15833     SaveEngineSnapshotValues_RND();
15834   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15835     SaveEngineSnapshotValues_EM();
15836   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15837     SaveEngineSnapshotValues_SP(&buffers);
15838   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15839     SaveEngineSnapshotValues_MM();
15840
15841   // save values stored in special snapshot structure
15842
15843   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15844     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15845   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15846     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15847   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15848     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15849   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15850     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15851
15852   // save further RND engine values
15853
15854   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15855   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15856   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15857
15858   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15859   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15860   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15861   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15862   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15863
15864   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15865   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15866   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15867
15868   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15869
15870   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15871   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15872
15873   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15874   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15875   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15876   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15877   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15878   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15879   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15880   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15881   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15882   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15883   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15884   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15885   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15886   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15887   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15888   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15889   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15890   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15891
15892   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15893   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15894
15895   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15896   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15897   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15898
15899   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15900   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15901
15902   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15903   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15904   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15905   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15906   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15907   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15908
15909   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15910   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15911
15912 #if 0
15913   ListNode *node = engine_snapshot_list_rnd;
15914   int num_bytes = 0;
15915
15916   while (node != NULL)
15917   {
15918     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15919
15920     node = node->next;
15921   }
15922
15923   Debug("game:playing:SaveEngineSnapshotBuffers",
15924         "size of engine snapshot: %d bytes", num_bytes);
15925 #endif
15926
15927   return buffers;
15928 }
15929
15930 void SaveEngineSnapshotSingle(void)
15931 {
15932   ListNode *buffers = SaveEngineSnapshotBuffers();
15933
15934   // finally save all snapshot buffers to single snapshot
15935   SaveSnapshotSingle(buffers);
15936
15937   // save level identification information
15938   setString(&snapshot_level_identifier, leveldir_current->identifier);
15939   snapshot_level_nr = level_nr;
15940 }
15941
15942 boolean CheckSaveEngineSnapshotToList(void)
15943 {
15944   boolean save_snapshot =
15945     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15946      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15947       game.snapshot.changed_action) ||
15948      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15949       game.snapshot.collected_item));
15950
15951   game.snapshot.changed_action = FALSE;
15952   game.snapshot.collected_item = FALSE;
15953   game.snapshot.save_snapshot = save_snapshot;
15954
15955   return save_snapshot;
15956 }
15957
15958 void SaveEngineSnapshotToList(void)
15959 {
15960   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15961       tape.quick_resume)
15962     return;
15963
15964   ListNode *buffers = SaveEngineSnapshotBuffers();
15965
15966   // finally save all snapshot buffers to snapshot list
15967   SaveSnapshotToList(buffers);
15968 }
15969
15970 void SaveEngineSnapshotToListInitial(void)
15971 {
15972   FreeEngineSnapshotList();
15973
15974   SaveEngineSnapshotToList();
15975 }
15976
15977 static void LoadEngineSnapshotValues(void)
15978 {
15979   // restore special values from snapshot structure
15980
15981   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15982     LoadEngineSnapshotValues_RND();
15983   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15984     LoadEngineSnapshotValues_EM();
15985   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15986     LoadEngineSnapshotValues_SP();
15987   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15988     LoadEngineSnapshotValues_MM();
15989 }
15990
15991 void LoadEngineSnapshotSingle(void)
15992 {
15993   LoadSnapshotSingle();
15994
15995   LoadEngineSnapshotValues();
15996 }
15997
15998 static void LoadEngineSnapshot_Undo(int steps)
15999 {
16000   LoadSnapshotFromList_Older(steps);
16001
16002   LoadEngineSnapshotValues();
16003 }
16004
16005 static void LoadEngineSnapshot_Redo(int steps)
16006 {
16007   LoadSnapshotFromList_Newer(steps);
16008
16009   LoadEngineSnapshotValues();
16010 }
16011
16012 boolean CheckEngineSnapshotSingle(void)
16013 {
16014   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16015           snapshot_level_nr == level_nr);
16016 }
16017
16018 boolean CheckEngineSnapshotList(void)
16019 {
16020   return CheckSnapshotList();
16021 }
16022
16023
16024 // ---------- new game button stuff -------------------------------------------
16025
16026 static struct
16027 {
16028   int graphic;
16029   struct XY *pos;
16030   int gadget_id;
16031   boolean *setup_value;
16032   boolean allowed_on_tape;
16033   boolean is_touch_button;
16034   char *infotext;
16035 } gamebutton_info[NUM_GAME_BUTTONS] =
16036 {
16037   {
16038     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16039     GAME_CTRL_ID_STOP,                          NULL,
16040     TRUE, FALSE,                                "stop game"
16041   },
16042   {
16043     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16044     GAME_CTRL_ID_PAUSE,                         NULL,
16045     TRUE, FALSE,                                "pause game"
16046   },
16047   {
16048     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16049     GAME_CTRL_ID_PLAY,                          NULL,
16050     TRUE, FALSE,                                "play game"
16051   },
16052   {
16053     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16054     GAME_CTRL_ID_UNDO,                          NULL,
16055     TRUE, FALSE,                                "undo step"
16056   },
16057   {
16058     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16059     GAME_CTRL_ID_REDO,                          NULL,
16060     TRUE, FALSE,                                "redo step"
16061   },
16062   {
16063     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16064     GAME_CTRL_ID_SAVE,                          NULL,
16065     TRUE, FALSE,                                "save game"
16066   },
16067   {
16068     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16069     GAME_CTRL_ID_PAUSE2,                        NULL,
16070     TRUE, FALSE,                                "pause game"
16071   },
16072   {
16073     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16074     GAME_CTRL_ID_LOAD,                          NULL,
16075     TRUE, FALSE,                                "load game"
16076   },
16077   {
16078     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16079     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16080     FALSE, FALSE,                               "stop game"
16081   },
16082   {
16083     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16084     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16085     FALSE, FALSE,                               "pause game"
16086   },
16087   {
16088     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16089     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16090     FALSE, FALSE,                               "play game"
16091   },
16092   {
16093     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16094     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16095     FALSE, TRUE,                                "stop game"
16096   },
16097   {
16098     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16099     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16100     FALSE, TRUE,                                "pause game"
16101   },
16102   {
16103     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16104     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16105     TRUE, FALSE,                                "background music on/off"
16106   },
16107   {
16108     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16109     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16110     TRUE, FALSE,                                "sound loops on/off"
16111   },
16112   {
16113     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16114     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16115     TRUE, FALSE,                                "normal sounds on/off"
16116   },
16117   {
16118     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16119     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16120     FALSE, FALSE,                               "background music on/off"
16121   },
16122   {
16123     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16124     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16125     FALSE, FALSE,                               "sound loops on/off"
16126   },
16127   {
16128     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16129     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16130     FALSE, FALSE,                               "normal sounds on/off"
16131   }
16132 };
16133
16134 void CreateGameButtons(void)
16135 {
16136   int i;
16137
16138   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16139   {
16140     int graphic = gamebutton_info[i].graphic;
16141     struct GraphicInfo *gfx = &graphic_info[graphic];
16142     struct XY *pos = gamebutton_info[i].pos;
16143     struct GadgetInfo *gi;
16144     int button_type;
16145     boolean checked;
16146     unsigned int event_mask;
16147     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16148     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16149     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16150     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16151     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16152     int gd_x   = gfx->src_x;
16153     int gd_y   = gfx->src_y;
16154     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16155     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16156     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16157     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16158     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16159     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16160     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16161     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16162     int id = i;
16163
16164     // do not use touch buttons if overlay touch buttons are disabled
16165     if (is_touch_button && !setup.touch.overlay_buttons)
16166       continue;
16167
16168     if (gfx->bitmap == NULL)
16169     {
16170       game_gadget[id] = NULL;
16171
16172       continue;
16173     }
16174
16175     if (id == GAME_CTRL_ID_STOP ||
16176         id == GAME_CTRL_ID_PANEL_STOP ||
16177         id == GAME_CTRL_ID_TOUCH_STOP ||
16178         id == GAME_CTRL_ID_PLAY ||
16179         id == GAME_CTRL_ID_PANEL_PLAY ||
16180         id == GAME_CTRL_ID_SAVE ||
16181         id == GAME_CTRL_ID_LOAD)
16182     {
16183       button_type = GD_TYPE_NORMAL_BUTTON;
16184       checked = FALSE;
16185       event_mask = GD_EVENT_RELEASED;
16186     }
16187     else if (id == GAME_CTRL_ID_UNDO ||
16188              id == GAME_CTRL_ID_REDO)
16189     {
16190       button_type = GD_TYPE_NORMAL_BUTTON;
16191       checked = FALSE;
16192       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16193     }
16194     else
16195     {
16196       button_type = GD_TYPE_CHECK_BUTTON;
16197       checked = (gamebutton_info[i].setup_value != NULL ?
16198                  *gamebutton_info[i].setup_value : FALSE);
16199       event_mask = GD_EVENT_PRESSED;
16200     }
16201
16202     gi = CreateGadget(GDI_CUSTOM_ID, id,
16203                       GDI_IMAGE_ID, graphic,
16204                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16205                       GDI_X, base_x + x,
16206                       GDI_Y, base_y + y,
16207                       GDI_WIDTH, gfx->width,
16208                       GDI_HEIGHT, gfx->height,
16209                       GDI_TYPE, button_type,
16210                       GDI_STATE, GD_BUTTON_UNPRESSED,
16211                       GDI_CHECKED, checked,
16212                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16213                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16214                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16215                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16216                       GDI_DIRECT_DRAW, FALSE,
16217                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16218                       GDI_EVENT_MASK, event_mask,
16219                       GDI_CALLBACK_ACTION, HandleGameButtons,
16220                       GDI_END);
16221
16222     if (gi == NULL)
16223       Fail("cannot create gadget");
16224
16225     game_gadget[id] = gi;
16226   }
16227 }
16228
16229 void FreeGameButtons(void)
16230 {
16231   int i;
16232
16233   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16234     FreeGadget(game_gadget[i]);
16235 }
16236
16237 static void UnmapGameButtonsAtSamePosition(int id)
16238 {
16239   int i;
16240
16241   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16242     if (i != id &&
16243         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16244         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16245       UnmapGadget(game_gadget[i]);
16246 }
16247
16248 static void UnmapGameButtonsAtSamePosition_All(void)
16249 {
16250   if (setup.show_load_save_buttons)
16251   {
16252     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16253     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16254     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16255   }
16256   else if (setup.show_undo_redo_buttons)
16257   {
16258     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16259     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16260     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16261   }
16262   else
16263   {
16264     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16265     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16266     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16267
16268     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16269     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16270     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16271   }
16272 }
16273
16274 void MapLoadSaveButtons(void)
16275 {
16276   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16277   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16278
16279   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16280   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16281 }
16282
16283 void MapUndoRedoButtons(void)
16284 {
16285   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16286   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16287
16288   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16289   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16290 }
16291
16292 void ModifyPauseButtons(void)
16293 {
16294   static int ids[] =
16295   {
16296     GAME_CTRL_ID_PAUSE,
16297     GAME_CTRL_ID_PAUSE2,
16298     GAME_CTRL_ID_PANEL_PAUSE,
16299     GAME_CTRL_ID_TOUCH_PAUSE,
16300     -1
16301   };
16302   int i;
16303
16304   for (i = 0; ids[i] > -1; i++)
16305     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16306 }
16307
16308 static void MapGameButtonsExt(boolean on_tape)
16309 {
16310   int i;
16311
16312   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16313   {
16314     if ((i == GAME_CTRL_ID_UNDO ||
16315          i == GAME_CTRL_ID_REDO) &&
16316         game_status != GAME_MODE_PLAYING)
16317       continue;
16318
16319     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16320       MapGadget(game_gadget[i]);
16321   }
16322
16323   UnmapGameButtonsAtSamePosition_All();
16324
16325   RedrawGameButtons();
16326 }
16327
16328 static void UnmapGameButtonsExt(boolean on_tape)
16329 {
16330   int i;
16331
16332   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16333     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16334       UnmapGadget(game_gadget[i]);
16335 }
16336
16337 static void RedrawGameButtonsExt(boolean on_tape)
16338 {
16339   int i;
16340
16341   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16342     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16343       RedrawGadget(game_gadget[i]);
16344 }
16345
16346 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16347 {
16348   if (gi == NULL)
16349     return;
16350
16351   gi->checked = state;
16352 }
16353
16354 static void RedrawSoundButtonGadget(int id)
16355 {
16356   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16357              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16358              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16359              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16360              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16361              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16362              id);
16363
16364   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16365   RedrawGadget(game_gadget[id2]);
16366 }
16367
16368 void MapGameButtons(void)
16369 {
16370   MapGameButtonsExt(FALSE);
16371 }
16372
16373 void UnmapGameButtons(void)
16374 {
16375   UnmapGameButtonsExt(FALSE);
16376 }
16377
16378 void RedrawGameButtons(void)
16379 {
16380   RedrawGameButtonsExt(FALSE);
16381 }
16382
16383 void MapGameButtonsOnTape(void)
16384 {
16385   MapGameButtonsExt(TRUE);
16386 }
16387
16388 void UnmapGameButtonsOnTape(void)
16389 {
16390   UnmapGameButtonsExt(TRUE);
16391 }
16392
16393 void RedrawGameButtonsOnTape(void)
16394 {
16395   RedrawGameButtonsExt(TRUE);
16396 }
16397
16398 static void GameUndoRedoExt(void)
16399 {
16400   ClearPlayerAction();
16401
16402   tape.pausing = TRUE;
16403
16404   RedrawPlayfield();
16405   UpdateAndDisplayGameControlValues();
16406
16407   DrawCompleteVideoDisplay();
16408   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16409   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16410   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16411
16412   ModifyPauseButtons();
16413
16414   BackToFront();
16415 }
16416
16417 static void GameUndo(int steps)
16418 {
16419   if (!CheckEngineSnapshotList())
16420     return;
16421
16422   int tape_property_bits = tape.property_bits;
16423
16424   LoadEngineSnapshot_Undo(steps);
16425
16426   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16427
16428   GameUndoRedoExt();
16429 }
16430
16431 static void GameRedo(int steps)
16432 {
16433   if (!CheckEngineSnapshotList())
16434     return;
16435
16436   int tape_property_bits = tape.property_bits;
16437
16438   LoadEngineSnapshot_Redo(steps);
16439
16440   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16441
16442   GameUndoRedoExt();
16443 }
16444
16445 static void HandleGameButtonsExt(int id, int button)
16446 {
16447   static boolean game_undo_executed = FALSE;
16448   int steps = BUTTON_STEPSIZE(button);
16449   boolean handle_game_buttons =
16450     (game_status == GAME_MODE_PLAYING ||
16451      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16452
16453   if (!handle_game_buttons)
16454     return;
16455
16456   switch (id)
16457   {
16458     case GAME_CTRL_ID_STOP:
16459     case GAME_CTRL_ID_PANEL_STOP:
16460     case GAME_CTRL_ID_TOUCH_STOP:
16461       TapeStopGame();
16462
16463       break;
16464
16465     case GAME_CTRL_ID_PAUSE:
16466     case GAME_CTRL_ID_PAUSE2:
16467     case GAME_CTRL_ID_PANEL_PAUSE:
16468     case GAME_CTRL_ID_TOUCH_PAUSE:
16469       if (network.enabled && game_status == GAME_MODE_PLAYING)
16470       {
16471         if (tape.pausing)
16472           SendToServer_ContinuePlaying();
16473         else
16474           SendToServer_PausePlaying();
16475       }
16476       else
16477         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16478
16479       game_undo_executed = FALSE;
16480
16481       break;
16482
16483     case GAME_CTRL_ID_PLAY:
16484     case GAME_CTRL_ID_PANEL_PLAY:
16485       if (game_status == GAME_MODE_MAIN)
16486       {
16487         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16488       }
16489       else if (tape.pausing)
16490       {
16491         if (network.enabled)
16492           SendToServer_ContinuePlaying();
16493         else
16494           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16495       }
16496       break;
16497
16498     case GAME_CTRL_ID_UNDO:
16499       // Important: When using "save snapshot when collecting an item" mode,
16500       // load last (current) snapshot for first "undo" after pressing "pause"
16501       // (else the last-but-one snapshot would be loaded, because the snapshot
16502       // pointer already points to the last snapshot when pressing "pause",
16503       // which is fine for "every step/move" mode, but not for "every collect")
16504       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16505           !game_undo_executed)
16506         steps--;
16507
16508       game_undo_executed = TRUE;
16509
16510       GameUndo(steps);
16511       break;
16512
16513     case GAME_CTRL_ID_REDO:
16514       GameRedo(steps);
16515       break;
16516
16517     case GAME_CTRL_ID_SAVE:
16518       TapeQuickSave();
16519       break;
16520
16521     case GAME_CTRL_ID_LOAD:
16522       TapeQuickLoad();
16523       break;
16524
16525     case SOUND_CTRL_ID_MUSIC:
16526     case SOUND_CTRL_ID_PANEL_MUSIC:
16527       if (setup.sound_music)
16528       { 
16529         setup.sound_music = FALSE;
16530
16531         FadeMusic();
16532       }
16533       else if (audio.music_available)
16534       { 
16535         setup.sound = setup.sound_music = TRUE;
16536
16537         SetAudioMode(setup.sound);
16538
16539         if (game_status == GAME_MODE_PLAYING)
16540           PlayLevelMusic();
16541       }
16542
16543       RedrawSoundButtonGadget(id);
16544
16545       break;
16546
16547     case SOUND_CTRL_ID_LOOPS:
16548     case SOUND_CTRL_ID_PANEL_LOOPS:
16549       if (setup.sound_loops)
16550         setup.sound_loops = FALSE;
16551       else if (audio.loops_available)
16552       {
16553         setup.sound = setup.sound_loops = TRUE;
16554
16555         SetAudioMode(setup.sound);
16556       }
16557
16558       RedrawSoundButtonGadget(id);
16559
16560       break;
16561
16562     case SOUND_CTRL_ID_SIMPLE:
16563     case SOUND_CTRL_ID_PANEL_SIMPLE:
16564       if (setup.sound_simple)
16565         setup.sound_simple = FALSE;
16566       else if (audio.sound_available)
16567       {
16568         setup.sound = setup.sound_simple = TRUE;
16569
16570         SetAudioMode(setup.sound);
16571       }
16572
16573       RedrawSoundButtonGadget(id);
16574
16575       break;
16576
16577     default:
16578       break;
16579   }
16580 }
16581
16582 static void HandleGameButtons(struct GadgetInfo *gi)
16583 {
16584   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16585 }
16586
16587 void HandleSoundButtonKeys(Key key)
16588 {
16589   if (key == setup.shortcut.sound_simple)
16590     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16591   else if (key == setup.shortcut.sound_loops)
16592     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16593   else if (key == setup.shortcut.sound_music)
16594     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16595 }