fixed regression bug with growing steel wall turning into normal wall
[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 :
2570                                graphic_info[graphic].anim_global_anim_sync ?
2571                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2572
2573         if (gpc->value != gpc->last_value)
2574         {
2575           gpc->gfx_frame = 0;
2576           gpc->gfx_random = init_gfx_random;
2577         }
2578         else
2579         {
2580           gpc->gfx_frame++;
2581
2582           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2583               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2584             gpc->gfx_random = init_gfx_random;
2585         }
2586
2587         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2588           gfx.anim_random_frame = gpc->gfx_random;
2589
2590         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2591           gpc->gfx_frame = element_info[element].collect_score;
2592
2593         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2594
2595         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2596           gfx.anim_random_frame = last_anim_random_frame;
2597       }
2598     }
2599     else if (gpc->type == TYPE_GRAPHIC)
2600     {
2601       if (gpc->graphic != IMG_UNDEFINED)
2602       {
2603         int last_anim_random_frame = gfx.anim_random_frame;
2604         int graphic = gpc->graphic;
2605         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2606                                sync_random_frame :
2607                                graphic_info[graphic].anim_global_anim_sync ?
2608                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2609
2610         if (gpc->value != gpc->last_value)
2611         {
2612           gpc->gfx_frame = 0;
2613           gpc->gfx_random = init_gfx_random;
2614         }
2615         else
2616         {
2617           gpc->gfx_frame++;
2618
2619           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2620               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2621             gpc->gfx_random = init_gfx_random;
2622         }
2623
2624         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2625           gfx.anim_random_frame = gpc->gfx_random;
2626
2627         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2628
2629         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2630           gfx.anim_random_frame = last_anim_random_frame;
2631       }
2632     }
2633   }
2634 }
2635
2636 static void DisplayGameControlValues(void)
2637 {
2638   boolean redraw_panel = FALSE;
2639   int i;
2640
2641   for (i = 0; game_panel_controls[i].nr != -1; i++)
2642   {
2643     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2644
2645     if (PANEL_DEACTIVATED(gpc->pos))
2646       continue;
2647
2648     if (gpc->value == gpc->last_value &&
2649         gpc->frame == gpc->last_frame)
2650       continue;
2651
2652     redraw_panel = TRUE;
2653   }
2654
2655   if (!redraw_panel)
2656     return;
2657
2658   // copy default game door content to main double buffer
2659
2660   // !!! CHECK AGAIN !!!
2661   SetPanelBackground();
2662   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2663   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2664
2665   // redraw game control buttons
2666   RedrawGameButtons();
2667
2668   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2669
2670   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2671   {
2672     int nr = game_panel_order[i].nr;
2673     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2674     struct TextPosInfo *pos = gpc->pos;
2675     int type = gpc->type;
2676     int value = gpc->value;
2677     int frame = gpc->frame;
2678     int size = pos->size;
2679     int font = pos->font;
2680     boolean draw_masked = pos->draw_masked;
2681     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2682
2683     if (PANEL_DEACTIVATED(pos))
2684       continue;
2685
2686     if (pos->class == get_hash_from_key("extra_panel_items") &&
2687         !setup.prefer_extra_panel_items)
2688       continue;
2689
2690     gpc->last_value = value;
2691     gpc->last_frame = frame;
2692
2693     if (type == TYPE_INTEGER)
2694     {
2695       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2696           nr == GAME_PANEL_INVENTORY_COUNT ||
2697           nr == GAME_PANEL_SCORE ||
2698           nr == GAME_PANEL_HIGHSCORE ||
2699           nr == GAME_PANEL_TIME)
2700       {
2701         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2702
2703         if (use_dynamic_size)           // use dynamic number of digits
2704         {
2705           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2706                               nr == GAME_PANEL_INVENTORY_COUNT ||
2707                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2708           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2709                           nr == GAME_PANEL_INVENTORY_COUNT ||
2710                           nr == GAME_PANEL_TIME ? 1 : 2);
2711           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2712                        nr == GAME_PANEL_INVENTORY_COUNT ||
2713                        nr == GAME_PANEL_TIME ? 3 : 5);
2714           int size2 = size1 + size_add;
2715           int font1 = pos->font;
2716           int font2 = pos->font_alt;
2717
2718           size = (value < value_change ? size1 : size2);
2719           font = (value < value_change ? font1 : font2);
2720         }
2721       }
2722
2723       // correct text size if "digits" is zero or less
2724       if (size <= 0)
2725         size = strlen(int2str(value, size));
2726
2727       // dynamically correct text alignment
2728       pos->width = size * getFontWidth(font);
2729
2730       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                   int2str(value, size), font, mask_mode);
2732     }
2733     else if (type == TYPE_ELEMENT)
2734     {
2735       int element, graphic;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741
2742       if (value != EL_UNDEFINED && value != EL_EMPTY)
2743       {
2744         element = value;
2745         graphic = el2panelimg(value);
2746
2747 #if 0
2748         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2749               element, EL_NAME(element), size);
2750 #endif
2751
2752         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2753           size = TILESIZE;
2754
2755         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2756                               &src_x, &src_y);
2757
2758         width  = graphic_info[graphic].width  * size / TILESIZE;
2759         height = graphic_info[graphic].height * size / TILESIZE;
2760
2761         if (draw_masked)
2762           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2763                            dst_x, dst_y);
2764         else
2765           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2766                      dst_x, dst_y);
2767       }
2768     }
2769     else if (type == TYPE_GRAPHIC)
2770     {
2771       int graphic        = gpc->graphic;
2772       int graphic_active = gpc->graphic_active;
2773       Bitmap *src_bitmap;
2774       int src_x, src_y;
2775       int width, height;
2776       int dst_x = PANEL_XPOS(pos);
2777       int dst_y = PANEL_YPOS(pos);
2778       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2779                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2780
2781       if (graphic != IMG_UNDEFINED && !skip)
2782       {
2783         if (pos->style == STYLE_REVERSE)
2784           value = 100 - value;
2785
2786         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2787
2788         if (pos->direction & MV_HORIZONTAL)
2789         {
2790           width  = graphic_info[graphic_active].width * value / 100;
2791           height = graphic_info[graphic_active].height;
2792
2793           if (pos->direction == MV_LEFT)
2794           {
2795             src_x += graphic_info[graphic_active].width - width;
2796             dst_x += graphic_info[graphic_active].width - width;
2797           }
2798         }
2799         else
2800         {
2801           width  = graphic_info[graphic_active].width;
2802           height = graphic_info[graphic_active].height * value / 100;
2803
2804           if (pos->direction == MV_UP)
2805           {
2806             src_y += graphic_info[graphic_active].height - height;
2807             dst_y += graphic_info[graphic_active].height - height;
2808           }
2809         }
2810
2811         if (draw_masked)
2812           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2813                            dst_x, dst_y);
2814         else
2815           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2816                      dst_x, dst_y);
2817
2818         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2819
2820         if (pos->direction & MV_HORIZONTAL)
2821         {
2822           if (pos->direction == MV_RIGHT)
2823           {
2824             src_x += width;
2825             dst_x += width;
2826           }
2827           else
2828           {
2829             dst_x = PANEL_XPOS(pos);
2830           }
2831
2832           width = graphic_info[graphic].width - width;
2833         }
2834         else
2835         {
2836           if (pos->direction == MV_DOWN)
2837           {
2838             src_y += height;
2839             dst_y += height;
2840           }
2841           else
2842           {
2843             dst_y = PANEL_YPOS(pos);
2844           }
2845
2846           height = graphic_info[graphic].height - height;
2847         }
2848
2849         if (draw_masked)
2850           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2851                            dst_x, dst_y);
2852         else
2853           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2854                      dst_x, dst_y);
2855       }
2856     }
2857     else if (type == TYPE_STRING)
2858     {
2859       boolean active = (value != 0);
2860       char *state_normal = "off";
2861       char *state_active = "on";
2862       char *state = (active ? state_active : state_normal);
2863       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2864                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2865                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2866                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2867
2868       if (nr == GAME_PANEL_GRAVITY_STATE)
2869       {
2870         int font1 = pos->font;          // (used for normal state)
2871         int font2 = pos->font_alt;      // (used for active state)
2872
2873         font = (active ? font2 : font1);
2874       }
2875
2876       if (s != NULL)
2877       {
2878         char *s_cut;
2879
2880         if (size <= 0)
2881         {
2882           // don't truncate output if "chars" is zero or less
2883           size = strlen(s);
2884
2885           // dynamically correct text alignment
2886           pos->width = size * getFontWidth(font);
2887         }
2888
2889         s_cut = getStringCopyN(s, size);
2890
2891         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2892                     s_cut, font, mask_mode);
2893
2894         free(s_cut);
2895       }
2896     }
2897
2898     redraw_mask |= REDRAW_DOOR_1;
2899   }
2900
2901   SetGameStatus(GAME_MODE_PLAYING);
2902 }
2903
2904 void UpdateAndDisplayGameControlValues(void)
2905 {
2906   if (tape.deactivate_display)
2907     return;
2908
2909   UpdateGameControlValues();
2910   DisplayGameControlValues();
2911 }
2912
2913 void UpdateGameDoorValues(void)
2914 {
2915   UpdateGameControlValues();
2916 }
2917
2918 void DrawGameDoorValues(void)
2919 {
2920   DisplayGameControlValues();
2921 }
2922
2923
2924 // ============================================================================
2925 // InitGameEngine()
2926 // ----------------------------------------------------------------------------
2927 // initialize game engine due to level / tape version number
2928 // ============================================================================
2929
2930 static void InitGameEngine(void)
2931 {
2932   int i, j, k, l, x, y;
2933
2934   // set game engine from tape file when re-playing, else from level file
2935   game.engine_version = (tape.playing ? tape.engine_version :
2936                          level.game_version);
2937
2938   // set single or multi-player game mode (needed for re-playing tapes)
2939   game.team_mode = setup.team_mode;
2940
2941   if (tape.playing)
2942   {
2943     int num_players = 0;
2944
2945     for (i = 0; i < MAX_PLAYERS; i++)
2946       if (tape.player_participates[i])
2947         num_players++;
2948
2949     // multi-player tapes contain input data for more than one player
2950     game.team_mode = (num_players > 1);
2951   }
2952
2953 #if 0
2954   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2955         level.game_version);
2956   Debug("game:init:level", "          tape.file_version   == %06d",
2957         tape.file_version);
2958   Debug("game:init:level", "          tape.game_version   == %06d",
2959         tape.game_version);
2960   Debug("game:init:level", "          tape.engine_version == %06d",
2961         tape.engine_version);
2962   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2963         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2964 #endif
2965
2966   // --------------------------------------------------------------------------
2967   // set flags for bugs and changes according to active game engine version
2968   // --------------------------------------------------------------------------
2969
2970   /*
2971     Summary of bugfix:
2972     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2973
2974     Bug was introduced in version:
2975     2.0.1
2976
2977     Bug was fixed in version:
2978     4.2.0.0
2979
2980     Description:
2981     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2982     but the property "can fall" was missing, which caused some levels to be
2983     unsolvable. This was fixed in version 4.2.0.0.
2984
2985     Affected levels/tapes:
2986     An example for a tape that was fixed by this bugfix is tape 029 from the
2987     level set "rnd_sam_bateman".
2988     The wrong behaviour will still be used for all levels or tapes that were
2989     created/recorded with it. An example for this is tape 023 from the level
2990     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2991   */
2992
2993   boolean use_amoeba_dropping_cannot_fall_bug =
2994     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2995       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2996      (tape.playing &&
2997       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2998       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed move speed of elements entering or leaving magic wall.
3003
3004     Fixed/changed in version:
3005     2.0.1
3006
3007     Description:
3008     Before 2.0.1, move speed of elements entering or leaving magic wall was
3009     twice as fast as it is now.
3010     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3011
3012     Affected levels/tapes:
3013     The first condition is generally needed for all levels/tapes before version
3014     2.0.1, which might use the old behaviour before it was changed; known tapes
3015     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3018     above, but before it was known that this change would break tapes like the
3019     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3020     although the engine version while recording maybe was before 2.0.1. There
3021     are a lot of tapes that are affected by this exception, like tape 006 from
3022     the level set "rnd_conor_mancone".
3023   */
3024
3025   boolean use_old_move_stepsize_for_magic_wall =
3026     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3027      !(tape.playing &&
3028        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3029        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3030
3031   /*
3032     Summary of bugfix/change:
3033     Fixed handling for custom elements that change when pushed by the player.
3034
3035     Fixed/changed in version:
3036     3.1.0
3037
3038     Description:
3039     Before 3.1.0, custom elements that "change when pushing" changed directly
3040     after the player started pushing them (until then handled in "DigField()").
3041     Since 3.1.0, these custom elements are not changed until the "pushing"
3042     move of the element is finished (now handled in "ContinueMoving()").
3043
3044     Affected levels/tapes:
3045     The first condition is generally needed for all levels/tapes before version
3046     3.1.0, which might use the old behaviour before it was changed; known tapes
3047     that are affected are some tapes from the level set "Walpurgis Gardens" by
3048     Jamie Cullen.
3049     The second condition is an exception from the above case and is needed for
3050     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3051     above (including some development versions of 3.1.0), but before it was
3052     known that this change would break tapes like the above and was fixed in
3053     3.1.1, so that the changed behaviour was active although the engine version
3054     while recording maybe was before 3.1.0. There is at least one tape that is
3055     affected by this exception, which is the tape for the one-level set "Bug
3056     Machine" by Juergen Bonhagen.
3057   */
3058
3059   game.use_change_when_pushing_bug =
3060     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061      !(tape.playing &&
3062        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3063        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3064
3065   /*
3066     Summary of bugfix/change:
3067     Fixed handling for blocking the field the player leaves when moving.
3068
3069     Fixed/changed in version:
3070     3.1.1
3071
3072     Description:
3073     Before 3.1.1, when "block last field when moving" was enabled, the field
3074     the player is leaving when moving was blocked for the time of the move,
3075     and was directly unblocked afterwards. This resulted in the last field
3076     being blocked for exactly one less than the number of frames of one player
3077     move. Additionally, even when blocking was disabled, the last field was
3078     blocked for exactly one frame.
3079     Since 3.1.1, due to changes in player movement handling, the last field
3080     is not blocked at all when blocking is disabled. When blocking is enabled,
3081     the last field is blocked for exactly the number of frames of one player
3082     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3083     last field is blocked for exactly one more than the number of frames of
3084     one player move.
3085
3086     Affected levels/tapes:
3087     (!!! yet to be determined -- probably many !!!)
3088   */
3089
3090   game.use_block_last_field_bug =
3091     (game.engine_version < VERSION_IDENT(3,1,1,0));
3092
3093   /* various special flags and settings for native Emerald Mine game engine */
3094
3095   game_em.use_single_button =
3096     (game.engine_version > VERSION_IDENT(4,0,0,2));
3097
3098   game_em.use_snap_key_bug =
3099     (game.engine_version < VERSION_IDENT(4,0,1,0));
3100
3101   game_em.use_random_bug =
3102     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3103
3104   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3105
3106   game_em.use_old_explosions            = use_old_em_engine;
3107   game_em.use_old_android               = use_old_em_engine;
3108   game_em.use_old_push_elements         = use_old_em_engine;
3109   game_em.use_old_push_into_acid        = use_old_em_engine;
3110
3111   game_em.use_wrap_around               = !use_old_em_engine;
3112
3113   // --------------------------------------------------------------------------
3114
3115   // set maximal allowed number of custom element changes per game frame
3116   game.max_num_changes_per_frame = 1;
3117
3118   // default scan direction: scan playfield from top/left to bottom/right
3119   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3120
3121   // dynamically adjust element properties according to game engine version
3122   InitElementPropertiesEngine(game.engine_version);
3123
3124   // ---------- initialize special element properties -------------------------
3125
3126   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3127   if (use_amoeba_dropping_cannot_fall_bug)
3128     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3129
3130   // ---------- initialize player's initial move delay ------------------------
3131
3132   // dynamically adjust player properties according to level information
3133   for (i = 0; i < MAX_PLAYERS; i++)
3134     game.initial_move_delay_value[i] =
3135       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3136
3137   // dynamically adjust player properties according to game engine version
3138   for (i = 0; i < MAX_PLAYERS; i++)
3139     game.initial_move_delay[i] =
3140       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3141        game.initial_move_delay_value[i] : 0);
3142
3143   // ---------- initialize player's initial push delay ------------------------
3144
3145   // dynamically adjust player properties according to game engine version
3146   game.initial_push_delay_value =
3147     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3148
3149   // ---------- initialize changing elements ----------------------------------
3150
3151   // initialize changing elements information
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153   {
3154     struct ElementInfo *ei = &element_info[i];
3155
3156     // this pointer might have been changed in the level editor
3157     ei->change = &ei->change_page[0];
3158
3159     if (!IS_CUSTOM_ELEMENT(i))
3160     {
3161       ei->change->target_element = EL_EMPTY_SPACE;
3162       ei->change->delay_fixed = 0;
3163       ei->change->delay_random = 0;
3164       ei->change->delay_frames = 1;
3165     }
3166
3167     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3168     {
3169       ei->has_change_event[j] = FALSE;
3170
3171       ei->event_page_nr[j] = 0;
3172       ei->event_page[j] = &ei->change_page[0];
3173     }
3174   }
3175
3176   // add changing elements from pre-defined list
3177   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3178   {
3179     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3180     struct ElementInfo *ei = &element_info[ch_delay->element];
3181
3182     ei->change->target_element       = ch_delay->target_element;
3183     ei->change->delay_fixed          = ch_delay->change_delay;
3184
3185     ei->change->pre_change_function  = ch_delay->pre_change_function;
3186     ei->change->change_function      = ch_delay->change_function;
3187     ei->change->post_change_function = ch_delay->post_change_function;
3188
3189     ei->change->can_change = TRUE;
3190     ei->change->can_change_or_has_action = TRUE;
3191
3192     ei->has_change_event[CE_DELAY] = TRUE;
3193
3194     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3195     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3196   }
3197
3198   // ---------- initialize internal run-time variables ------------------------
3199
3200   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3201   {
3202     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3203
3204     for (j = 0; j < ei->num_change_pages; j++)
3205     {
3206       ei->change_page[j].can_change_or_has_action =
3207         (ei->change_page[j].can_change |
3208          ei->change_page[j].has_action);
3209     }
3210   }
3211
3212   // add change events from custom element configuration
3213   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3214   {
3215     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3216
3217     for (j = 0; j < ei->num_change_pages; j++)
3218     {
3219       if (!ei->change_page[j].can_change_or_has_action)
3220         continue;
3221
3222       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3223       {
3224         // only add event page for the first page found with this event
3225         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3226         {
3227           ei->has_change_event[k] = TRUE;
3228
3229           ei->event_page_nr[k] = j;
3230           ei->event_page[k] = &ei->change_page[j];
3231         }
3232       }
3233     }
3234   }
3235
3236   // ---------- initialize reference elements in change conditions ------------
3237
3238   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3239   {
3240     int element = EL_CUSTOM_START + i;
3241     struct ElementInfo *ei = &element_info[element];
3242
3243     for (j = 0; j < ei->num_change_pages; j++)
3244     {
3245       int trigger_element = ei->change_page[j].initial_trigger_element;
3246
3247       if (trigger_element >= EL_PREV_CE_8 &&
3248           trigger_element <= EL_NEXT_CE_8)
3249         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3250
3251       ei->change_page[j].trigger_element = trigger_element;
3252     }
3253   }
3254
3255   // ---------- initialize run-time trigger player and element ----------------
3256
3257   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3258   {
3259     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3260
3261     for (j = 0; j < ei->num_change_pages; j++)
3262     {
3263       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3264       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3265       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3266       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3267       ei->change_page[j].actual_trigger_ce_value = 0;
3268       ei->change_page[j].actual_trigger_ce_score = 0;
3269     }
3270   }
3271
3272   // ---------- initialize trigger events -------------------------------------
3273
3274   // initialize trigger events information
3275   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3276     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3277       trigger_events[i][j] = FALSE;
3278
3279   // add trigger events from element change event properties
3280   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3281   {
3282     struct ElementInfo *ei = &element_info[i];
3283
3284     for (j = 0; j < ei->num_change_pages; j++)
3285     {
3286       if (!ei->change_page[j].can_change_or_has_action)
3287         continue;
3288
3289       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3290       {
3291         int trigger_element = ei->change_page[j].trigger_element;
3292
3293         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3294         {
3295           if (ei->change_page[j].has_event[k])
3296           {
3297             if (IS_GROUP_ELEMENT(trigger_element))
3298             {
3299               struct ElementGroupInfo *group =
3300                 element_info[trigger_element].group;
3301
3302               for (l = 0; l < group->num_elements_resolved; l++)
3303                 trigger_events[group->element_resolved[l]][k] = TRUE;
3304             }
3305             else if (trigger_element == EL_ANY_ELEMENT)
3306               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3307                 trigger_events[l][k] = TRUE;
3308             else
3309               trigger_events[trigger_element][k] = TRUE;
3310           }
3311         }
3312       }
3313     }
3314   }
3315
3316   // ---------- initialize push delay -----------------------------------------
3317
3318   // initialize push delay values to default
3319   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3320   {
3321     if (!IS_CUSTOM_ELEMENT(i))
3322     {
3323       // set default push delay values (corrected since version 3.0.7-1)
3324       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3325       {
3326         element_info[i].push_delay_fixed = 2;
3327         element_info[i].push_delay_random = 8;
3328       }
3329       else
3330       {
3331         element_info[i].push_delay_fixed = 8;
3332         element_info[i].push_delay_random = 8;
3333       }
3334     }
3335   }
3336
3337   // set push delay value for certain elements from pre-defined list
3338   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3339   {
3340     int e = push_delay_list[i].element;
3341
3342     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3343     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3344   }
3345
3346   // set push delay value for Supaplex elements for newer engine versions
3347   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3348   {
3349     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3350     {
3351       if (IS_SP_ELEMENT(i))
3352       {
3353         // set SP push delay to just enough to push under a falling zonk
3354         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3355
3356         element_info[i].push_delay_fixed  = delay;
3357         element_info[i].push_delay_random = 0;
3358       }
3359     }
3360   }
3361
3362   // ---------- initialize move stepsize --------------------------------------
3363
3364   // initialize move stepsize values to default
3365   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3366     if (!IS_CUSTOM_ELEMENT(i))
3367       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3368
3369   // set move stepsize value for certain elements from pre-defined list
3370   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3371   {
3372     int e = move_stepsize_list[i].element;
3373
3374     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3375
3376     // set move stepsize value for certain elements for older engine versions
3377     if (use_old_move_stepsize_for_magic_wall)
3378     {
3379       if (e == EL_MAGIC_WALL_FILLING ||
3380           e == EL_MAGIC_WALL_EMPTYING ||
3381           e == EL_BD_MAGIC_WALL_FILLING ||
3382           e == EL_BD_MAGIC_WALL_EMPTYING)
3383         element_info[e].move_stepsize *= 2;
3384     }
3385   }
3386
3387   // ---------- initialize collect score --------------------------------------
3388
3389   // initialize collect score values for custom elements from initial value
3390   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3391     if (IS_CUSTOM_ELEMENT(i))
3392       element_info[i].collect_score = element_info[i].collect_score_initial;
3393
3394   // ---------- initialize collect count --------------------------------------
3395
3396   // initialize collect count values for non-custom elements
3397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3398     if (!IS_CUSTOM_ELEMENT(i))
3399       element_info[i].collect_count_initial = 0;
3400
3401   // add collect count values for all elements from pre-defined list
3402   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3403     element_info[collect_count_list[i].element].collect_count_initial =
3404       collect_count_list[i].count;
3405
3406   // ---------- initialize access direction -----------------------------------
3407
3408   // initialize access direction values to default (access from every side)
3409   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3410     if (!IS_CUSTOM_ELEMENT(i))
3411       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3412
3413   // set access direction value for certain elements from pre-defined list
3414   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3415     element_info[access_direction_list[i].element].access_direction =
3416       access_direction_list[i].direction;
3417
3418   // ---------- initialize explosion content ----------------------------------
3419   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3420   {
3421     if (IS_CUSTOM_ELEMENT(i))
3422       continue;
3423
3424     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3425     {
3426       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3427
3428       element_info[i].content.e[x][y] =
3429         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3430          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3431          i == EL_PLAYER_3 ? EL_EMERALD :
3432          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3433          i == EL_MOLE ? EL_EMERALD_RED :
3434          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3435          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3436          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3437          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3438          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3439          i == EL_WALL_EMERALD ? EL_EMERALD :
3440          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3441          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3442          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3443          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3444          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3445          i == EL_WALL_PEARL ? EL_PEARL :
3446          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3447          EL_EMPTY);
3448     }
3449   }
3450
3451   // ---------- initialize recursion detection --------------------------------
3452   recursion_loop_depth = 0;
3453   recursion_loop_detected = FALSE;
3454   recursion_loop_element = EL_UNDEFINED;
3455
3456   // ---------- initialize graphics engine ------------------------------------
3457   game.scroll_delay_value =
3458     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3459      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3460      !setup.forced_scroll_delay           ? 0 :
3461      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3462   game.scroll_delay_value =
3463     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3464
3465   // ---------- initialize game engine snapshots ------------------------------
3466   for (i = 0; i < MAX_PLAYERS; i++)
3467     game.snapshot.last_action[i] = 0;
3468   game.snapshot.changed_action = FALSE;
3469   game.snapshot.collected_item = FALSE;
3470   game.snapshot.mode =
3471     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3472      SNAPSHOT_MODE_EVERY_STEP :
3473      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3474      SNAPSHOT_MODE_EVERY_MOVE :
3475      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3476      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3477   game.snapshot.save_snapshot = FALSE;
3478
3479   // ---------- initialize level time for Supaplex engine ---------------------
3480   // Supaplex levels with time limit currently unsupported -- should be added
3481   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3482     level.time = 0;
3483
3484   // ---------- initialize flags for handling game actions --------------------
3485
3486   // set flags for game actions to default values
3487   game.use_key_actions = TRUE;
3488   game.use_mouse_actions = FALSE;
3489
3490   // when using Mirror Magic game engine, handle mouse events only
3491   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3492   {
3493     game.use_key_actions = FALSE;
3494     game.use_mouse_actions = TRUE;
3495   }
3496
3497   // check for custom elements with mouse click events
3498   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3499   {
3500     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3501     {
3502       int element = EL_CUSTOM_START + i;
3503
3504       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3505           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3506           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3507           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3508         game.use_mouse_actions = TRUE;
3509     }
3510   }
3511 }
3512
3513 static int get_num_special_action(int element, int action_first,
3514                                   int action_last)
3515 {
3516   int num_special_action = 0;
3517   int i, j;
3518
3519   for (i = action_first; i <= action_last; i++)
3520   {
3521     boolean found = FALSE;
3522
3523     for (j = 0; j < NUM_DIRECTIONS; j++)
3524       if (el_act_dir2img(element, i, j) !=
3525           el_act_dir2img(element, ACTION_DEFAULT, j))
3526         found = TRUE;
3527
3528     if (found)
3529       num_special_action++;
3530     else
3531       break;
3532   }
3533
3534   return num_special_action;
3535 }
3536
3537
3538 // ============================================================================
3539 // InitGame()
3540 // ----------------------------------------------------------------------------
3541 // initialize and start new game
3542 // ============================================================================
3543
3544 #if DEBUG_INIT_PLAYER
3545 static void DebugPrintPlayerStatus(char *message)
3546 {
3547   int i;
3548
3549   if (!options.debug)
3550     return;
3551
3552   Debug("game:init:player", "%s:", message);
3553
3554   for (i = 0; i < MAX_PLAYERS; i++)
3555   {
3556     struct PlayerInfo *player = &stored_player[i];
3557
3558     Debug("game:init:player",
3559           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3560           i + 1,
3561           player->present,
3562           player->connected,
3563           player->connected_locally,
3564           player->connected_network,
3565           player->active,
3566           (local_player == player ? " (local player)" : ""));
3567   }
3568 }
3569 #endif
3570
3571 void InitGame(void)
3572 {
3573   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3574   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3575   int fade_mask = REDRAW_FIELD;
3576
3577   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3578   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3579   int initial_move_dir = MV_DOWN;
3580   int i, j, x, y;
3581
3582   // required here to update video display before fading (FIX THIS)
3583   DrawMaskedBorder(REDRAW_DOOR_2);
3584
3585   if (!game.restart_level)
3586     CloseDoor(DOOR_CLOSE_1);
3587
3588   SetGameStatus(GAME_MODE_PLAYING);
3589
3590   if (level_editor_test_game)
3591     FadeSkipNextFadeOut();
3592   else
3593     FadeSetEnterScreen();
3594
3595   if (CheckFadeAll())
3596     fade_mask = REDRAW_ALL;
3597
3598   FadeLevelSoundsAndMusic();
3599
3600   ExpireSoundLoops(TRUE);
3601
3602   FadeOut(fade_mask);
3603
3604   if (level_editor_test_game)
3605     FadeSkipNextFadeIn();
3606
3607   // needed if different viewport properties defined for playing
3608   ChangeViewportPropertiesIfNeeded();
3609
3610   ClearField();
3611
3612   DrawCompleteVideoDisplay();
3613
3614   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3615
3616   InitGameEngine();
3617   InitGameControlValues();
3618
3619   if (tape.recording)
3620   {
3621     // initialize tape actions from game when recording tape
3622     tape.use_key_actions   = game.use_key_actions;
3623     tape.use_mouse_actions = game.use_mouse_actions;
3624
3625     // initialize visible playfield size when recording tape (for team mode)
3626     tape.scr_fieldx = SCR_FIELDX;
3627     tape.scr_fieldy = SCR_FIELDY;
3628   }
3629
3630   // don't play tapes over network
3631   network_playing = (network.enabled && !tape.playing);
3632
3633   for (i = 0; i < MAX_PLAYERS; i++)
3634   {
3635     struct PlayerInfo *player = &stored_player[i];
3636
3637     player->index_nr = i;
3638     player->index_bit = (1 << i);
3639     player->element_nr = EL_PLAYER_1 + i;
3640
3641     player->present = FALSE;
3642     player->active = FALSE;
3643     player->mapped = FALSE;
3644
3645     player->killed = FALSE;
3646     player->reanimated = FALSE;
3647     player->buried = FALSE;
3648
3649     player->action = 0;
3650     player->effective_action = 0;
3651     player->programmed_action = 0;
3652     player->snap_action = 0;
3653
3654     player->mouse_action.lx = 0;
3655     player->mouse_action.ly = 0;
3656     player->mouse_action.button = 0;
3657     player->mouse_action.button_hint = 0;
3658
3659     player->effective_mouse_action.lx = 0;
3660     player->effective_mouse_action.ly = 0;
3661     player->effective_mouse_action.button = 0;
3662     player->effective_mouse_action.button_hint = 0;
3663
3664     for (j = 0; j < MAX_NUM_KEYS; j++)
3665       player->key[j] = FALSE;
3666
3667     player->num_white_keys = 0;
3668
3669     player->dynabomb_count = 0;
3670     player->dynabomb_size = 1;
3671     player->dynabombs_left = 0;
3672     player->dynabomb_xl = FALSE;
3673
3674     player->MovDir = initial_move_dir;
3675     player->MovPos = 0;
3676     player->GfxPos = 0;
3677     player->GfxDir = initial_move_dir;
3678     player->GfxAction = ACTION_DEFAULT;
3679     player->Frame = 0;
3680     player->StepFrame = 0;
3681
3682     player->initial_element = player->element_nr;
3683     player->artwork_element =
3684       (level.use_artwork_element[i] ? level.artwork_element[i] :
3685        player->element_nr);
3686     player->use_murphy = FALSE;
3687
3688     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3689     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3690
3691     player->gravity = level.initial_player_gravity[i];
3692
3693     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3694
3695     player->actual_frame_counter.count = 0;
3696     player->actual_frame_counter.value = 1;
3697
3698     player->step_counter = 0;
3699
3700     player->last_move_dir = initial_move_dir;
3701
3702     player->is_active = FALSE;
3703
3704     player->is_waiting = FALSE;
3705     player->is_moving = FALSE;
3706     player->is_auto_moving = FALSE;
3707     player->is_digging = FALSE;
3708     player->is_snapping = FALSE;
3709     player->is_collecting = FALSE;
3710     player->is_pushing = FALSE;
3711     player->is_switching = FALSE;
3712     player->is_dropping = FALSE;
3713     player->is_dropping_pressed = FALSE;
3714
3715     player->is_bored = FALSE;
3716     player->is_sleeping = FALSE;
3717
3718     player->was_waiting = TRUE;
3719     player->was_moving = FALSE;
3720     player->was_snapping = FALSE;
3721     player->was_dropping = FALSE;
3722
3723     player->force_dropping = FALSE;
3724
3725     player->frame_counter_bored = -1;
3726     player->frame_counter_sleeping = -1;
3727
3728     player->anim_delay_counter = 0;
3729     player->post_delay_counter = 0;
3730
3731     player->dir_waiting = initial_move_dir;
3732     player->action_waiting = ACTION_DEFAULT;
3733     player->last_action_waiting = ACTION_DEFAULT;
3734     player->special_action_bored = ACTION_DEFAULT;
3735     player->special_action_sleeping = ACTION_DEFAULT;
3736
3737     player->switch_x = -1;
3738     player->switch_y = -1;
3739
3740     player->drop_x = -1;
3741     player->drop_y = -1;
3742
3743     player->show_envelope = 0;
3744
3745     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3746
3747     player->push_delay       = -1;      // initialized when pushing starts
3748     player->push_delay_value = game.initial_push_delay_value;
3749
3750     player->drop_delay = 0;
3751     player->drop_pressed_delay = 0;
3752
3753     player->last_jx = -1;
3754     player->last_jy = -1;
3755     player->jx = -1;
3756     player->jy = -1;
3757
3758     player->shield_normal_time_left = 0;
3759     player->shield_deadly_time_left = 0;
3760
3761     player->last_removed_element = EL_UNDEFINED;
3762
3763     player->inventory_infinite_element = EL_UNDEFINED;
3764     player->inventory_size = 0;
3765
3766     if (level.use_initial_inventory[i])
3767     {
3768       for (j = 0; j < level.initial_inventory_size[i]; j++)
3769       {
3770         int element = level.initial_inventory_content[i][j];
3771         int collect_count = element_info[element].collect_count_initial;
3772         int k;
3773
3774         if (!IS_CUSTOM_ELEMENT(element))
3775           collect_count = 1;
3776
3777         if (collect_count == 0)
3778           player->inventory_infinite_element = element;
3779         else
3780           for (k = 0; k < collect_count; k++)
3781             if (player->inventory_size < MAX_INVENTORY_SIZE)
3782               player->inventory_element[player->inventory_size++] = element;
3783       }
3784     }
3785
3786     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3787     SnapField(player, 0, 0);
3788
3789     map_player_action[i] = i;
3790   }
3791
3792   network_player_action_received = FALSE;
3793
3794   // initial null action
3795   if (network_playing)
3796     SendToServer_MovePlayer(MV_NONE);
3797
3798   FrameCounter = 0;
3799   TimeFrames = 0;
3800   TimePlayed = 0;
3801   TimeLeft = level.time;
3802   TapeTime = 0;
3803
3804   ScreenMovDir = MV_NONE;
3805   ScreenMovPos = 0;
3806   ScreenGfxPos = 0;
3807
3808   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3809
3810   game.robot_wheel_x = -1;
3811   game.robot_wheel_y = -1;
3812
3813   game.exit_x = -1;
3814   game.exit_y = -1;
3815
3816   game.all_players_gone = FALSE;
3817
3818   game.LevelSolved = FALSE;
3819   game.GameOver = FALSE;
3820
3821   game.GamePlayed = !tape.playing;
3822
3823   game.LevelSolved_GameWon = FALSE;
3824   game.LevelSolved_GameEnd = FALSE;
3825   game.LevelSolved_SaveTape = FALSE;
3826   game.LevelSolved_SaveScore = FALSE;
3827
3828   game.LevelSolved_CountingTime = 0;
3829   game.LevelSolved_CountingScore = 0;
3830   game.LevelSolved_CountingHealth = 0;
3831
3832   game.panel.active = TRUE;
3833
3834   game.no_level_time_limit = (level.time == 0);
3835   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3836
3837   game.yamyam_content_nr = 0;
3838   game.robot_wheel_active = FALSE;
3839   game.magic_wall_active = FALSE;
3840   game.magic_wall_time_left = 0;
3841   game.light_time_left = 0;
3842   game.timegate_time_left = 0;
3843   game.switchgate_pos = 0;
3844   game.wind_direction = level.wind_direction_initial;
3845
3846   game.time_final = 0;
3847   game.score_time_final = 0;
3848
3849   game.score = 0;
3850   game.score_final = 0;
3851
3852   game.health = MAX_HEALTH;
3853   game.health_final = MAX_HEALTH;
3854
3855   game.gems_still_needed = level.gems_needed;
3856   game.sokoban_fields_still_needed = 0;
3857   game.sokoban_objects_still_needed = 0;
3858   game.lights_still_needed = 0;
3859   game.players_still_needed = 0;
3860   game.friends_still_needed = 0;
3861
3862   game.lenses_time_left = 0;
3863   game.magnify_time_left = 0;
3864
3865   game.ball_active = level.ball_active_initial;
3866   game.ball_content_nr = 0;
3867
3868   game.explosions_delayed = TRUE;
3869
3870   game.envelope_active = FALSE;
3871
3872   // special case: set custom artwork setting to initial value
3873   game.use_masked_elements = game.use_masked_elements_initial;
3874
3875   for (i = 0; i < NUM_BELTS; i++)
3876   {
3877     game.belt_dir[i] = MV_NONE;
3878     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3879   }
3880
3881   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3882     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3883
3884 #if DEBUG_INIT_PLAYER
3885   DebugPrintPlayerStatus("Player status at level initialization");
3886 #endif
3887
3888   SCAN_PLAYFIELD(x, y)
3889   {
3890     Tile[x][y] = Last[x][y] = level.field[x][y];
3891     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3892     ChangeDelay[x][y] = 0;
3893     ChangePage[x][y] = -1;
3894     CustomValue[x][y] = 0;              // initialized in InitField()
3895     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3896     AmoebaNr[x][y] = 0;
3897     WasJustMoving[x][y] = 0;
3898     WasJustFalling[x][y] = 0;
3899     CheckCollision[x][y] = 0;
3900     CheckImpact[x][y] = 0;
3901     Stop[x][y] = FALSE;
3902     Pushed[x][y] = FALSE;
3903
3904     ChangeCount[x][y] = 0;
3905     ChangeEvent[x][y] = -1;
3906
3907     ExplodePhase[x][y] = 0;
3908     ExplodeDelay[x][y] = 0;
3909     ExplodeField[x][y] = EX_TYPE_NONE;
3910
3911     RunnerVisit[x][y] = 0;
3912     PlayerVisit[x][y] = 0;
3913
3914     GfxFrame[x][y] = 0;
3915     GfxRandom[x][y] = INIT_GFX_RANDOM();
3916     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3917     GfxElement[x][y] = EL_UNDEFINED;
3918     GfxElementEmpty[x][y] = EL_EMPTY;
3919     GfxAction[x][y] = ACTION_DEFAULT;
3920     GfxDir[x][y] = MV_NONE;
3921     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3922   }
3923
3924   SCAN_PLAYFIELD(x, y)
3925   {
3926     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3927       emulate_bd = FALSE;
3928     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3929       emulate_sp = FALSE;
3930
3931     InitField(x, y, TRUE);
3932
3933     ResetGfxAnimation(x, y);
3934   }
3935
3936   InitBeltMovement();
3937
3938   for (i = 0; i < MAX_PLAYERS; i++)
3939   {
3940     struct PlayerInfo *player = &stored_player[i];
3941
3942     // set number of special actions for bored and sleeping animation
3943     player->num_special_action_bored =
3944       get_num_special_action(player->artwork_element,
3945                              ACTION_BORING_1, ACTION_BORING_LAST);
3946     player->num_special_action_sleeping =
3947       get_num_special_action(player->artwork_element,
3948                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3949   }
3950
3951   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3952                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3953
3954   // initialize type of slippery elements
3955   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3956   {
3957     if (!IS_CUSTOM_ELEMENT(i))
3958     {
3959       // default: elements slip down either to the left or right randomly
3960       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3961
3962       // SP style elements prefer to slip down on the left side
3963       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3964         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3965
3966       // BD style elements prefer to slip down on the left side
3967       if (game.emulation == EMU_BOULDERDASH)
3968         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3969     }
3970   }
3971
3972   // initialize explosion and ignition delay
3973   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3974   {
3975     if (!IS_CUSTOM_ELEMENT(i))
3976     {
3977       int num_phase = 8;
3978       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3979                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3980                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3981       int last_phase = (num_phase + 1) * delay;
3982       int half_phase = (num_phase / 2) * delay;
3983
3984       element_info[i].explosion_delay = last_phase - 1;
3985       element_info[i].ignition_delay = half_phase;
3986
3987       if (i == EL_BLACK_ORB)
3988         element_info[i].ignition_delay = 1;
3989     }
3990   }
3991
3992   // correct non-moving belts to start moving left
3993   for (i = 0; i < NUM_BELTS; i++)
3994     if (game.belt_dir[i] == MV_NONE)
3995       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3996
3997 #if USE_NEW_PLAYER_ASSIGNMENTS
3998   // use preferred player also in local single-player mode
3999   if (!network.enabled && !game.team_mode)
4000   {
4001     int new_index_nr = setup.network_player_nr;
4002
4003     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4004     {
4005       for (i = 0; i < MAX_PLAYERS; i++)
4006         stored_player[i].connected_locally = FALSE;
4007
4008       stored_player[new_index_nr].connected_locally = TRUE;
4009     }
4010   }
4011
4012   for (i = 0; i < MAX_PLAYERS; i++)
4013   {
4014     stored_player[i].connected = FALSE;
4015
4016     // in network game mode, the local player might not be the first player
4017     if (stored_player[i].connected_locally)
4018       local_player = &stored_player[i];
4019   }
4020
4021   if (!network.enabled)
4022     local_player->connected = TRUE;
4023
4024   if (tape.playing)
4025   {
4026     for (i = 0; i < MAX_PLAYERS; i++)
4027       stored_player[i].connected = tape.player_participates[i];
4028   }
4029   else if (network.enabled)
4030   {
4031     // add team mode players connected over the network (needed for correct
4032     // assignment of player figures from level to locally playing players)
4033
4034     for (i = 0; i < MAX_PLAYERS; i++)
4035       if (stored_player[i].connected_network)
4036         stored_player[i].connected = TRUE;
4037   }
4038   else if (game.team_mode)
4039   {
4040     // try to guess locally connected team mode players (needed for correct
4041     // assignment of player figures from level to locally playing players)
4042
4043     for (i = 0; i < MAX_PLAYERS; i++)
4044       if (setup.input[i].use_joystick ||
4045           setup.input[i].key.left != KSYM_UNDEFINED)
4046         stored_player[i].connected = TRUE;
4047   }
4048
4049 #if DEBUG_INIT_PLAYER
4050   DebugPrintPlayerStatus("Player status after level initialization");
4051 #endif
4052
4053 #if DEBUG_INIT_PLAYER
4054   Debug("game:init:player", "Reassigning players ...");
4055 #endif
4056
4057   // check if any connected player was not found in playfield
4058   for (i = 0; i < MAX_PLAYERS; i++)
4059   {
4060     struct PlayerInfo *player = &stored_player[i];
4061
4062     if (player->connected && !player->present)
4063     {
4064       struct PlayerInfo *field_player = NULL;
4065
4066 #if DEBUG_INIT_PLAYER
4067       Debug("game:init:player",
4068             "- looking for field player for player %d ...", i + 1);
4069 #endif
4070
4071       // assign first free player found that is present in the playfield
4072
4073       // first try: look for unmapped playfield player that is not connected
4074       for (j = 0; j < MAX_PLAYERS; j++)
4075         if (field_player == NULL &&
4076             stored_player[j].present &&
4077             !stored_player[j].mapped &&
4078             !stored_player[j].connected)
4079           field_player = &stored_player[j];
4080
4081       // second try: look for *any* unmapped playfield player
4082       for (j = 0; j < MAX_PLAYERS; j++)
4083         if (field_player == NULL &&
4084             stored_player[j].present &&
4085             !stored_player[j].mapped)
4086           field_player = &stored_player[j];
4087
4088       if (field_player != NULL)
4089       {
4090         int jx = field_player->jx, jy = field_player->jy;
4091
4092 #if DEBUG_INIT_PLAYER
4093         Debug("game:init:player", "- found player %d",
4094               field_player->index_nr + 1);
4095 #endif
4096
4097         player->present = FALSE;
4098         player->active = FALSE;
4099
4100         field_player->present = TRUE;
4101         field_player->active = TRUE;
4102
4103         /*
4104         player->initial_element = field_player->initial_element;
4105         player->artwork_element = field_player->artwork_element;
4106
4107         player->block_last_field       = field_player->block_last_field;
4108         player->block_delay_adjustment = field_player->block_delay_adjustment;
4109         */
4110
4111         StorePlayer[jx][jy] = field_player->element_nr;
4112
4113         field_player->jx = field_player->last_jx = jx;
4114         field_player->jy = field_player->last_jy = jy;
4115
4116         if (local_player == player)
4117           local_player = field_player;
4118
4119         map_player_action[field_player->index_nr] = i;
4120
4121         field_player->mapped = TRUE;
4122
4123 #if DEBUG_INIT_PLAYER
4124         Debug("game:init:player", "- map_player_action[%d] == %d",
4125               field_player->index_nr + 1, i + 1);
4126 #endif
4127       }
4128     }
4129
4130     if (player->connected && player->present)
4131       player->mapped = TRUE;
4132   }
4133
4134 #if DEBUG_INIT_PLAYER
4135   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4136 #endif
4137
4138 #else
4139
4140   // check if any connected player was not found in playfield
4141   for (i = 0; i < MAX_PLAYERS; i++)
4142   {
4143     struct PlayerInfo *player = &stored_player[i];
4144
4145     if (player->connected && !player->present)
4146     {
4147       for (j = 0; j < MAX_PLAYERS; j++)
4148       {
4149         struct PlayerInfo *field_player = &stored_player[j];
4150         int jx = field_player->jx, jy = field_player->jy;
4151
4152         // assign first free player found that is present in the playfield
4153         if (field_player->present && !field_player->connected)
4154         {
4155           player->present = TRUE;
4156           player->active = TRUE;
4157
4158           field_player->present = FALSE;
4159           field_player->active = FALSE;
4160
4161           player->initial_element = field_player->initial_element;
4162           player->artwork_element = field_player->artwork_element;
4163
4164           player->block_last_field       = field_player->block_last_field;
4165           player->block_delay_adjustment = field_player->block_delay_adjustment;
4166
4167           StorePlayer[jx][jy] = player->element_nr;
4168
4169           player->jx = player->last_jx = jx;
4170           player->jy = player->last_jy = jy;
4171
4172           break;
4173         }
4174       }
4175     }
4176   }
4177 #endif
4178
4179 #if 0
4180   Debug("game:init:player", "local_player->present == %d",
4181         local_player->present);
4182 #endif
4183
4184   // set focus to local player for network games, else to all players
4185   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4186   game.centered_player_nr_next = game.centered_player_nr;
4187   game.set_centered_player = FALSE;
4188   game.set_centered_player_wrap = FALSE;
4189
4190   if (network_playing && tape.recording)
4191   {
4192     // store client dependent player focus when recording network games
4193     tape.centered_player_nr_next = game.centered_player_nr_next;
4194     tape.set_centered_player = TRUE;
4195   }
4196
4197   if (tape.playing)
4198   {
4199     // when playing a tape, eliminate all players who do not participate
4200
4201 #if USE_NEW_PLAYER_ASSIGNMENTS
4202
4203     if (!game.team_mode)
4204     {
4205       for (i = 0; i < MAX_PLAYERS; i++)
4206       {
4207         if (stored_player[i].active &&
4208             !tape.player_participates[map_player_action[i]])
4209         {
4210           struct PlayerInfo *player = &stored_player[i];
4211           int jx = player->jx, jy = player->jy;
4212
4213 #if DEBUG_INIT_PLAYER
4214           Debug("game:init:player", "Removing player %d at (%d, %d)",
4215                 i + 1, jx, jy);
4216 #endif
4217
4218           player->active = FALSE;
4219           StorePlayer[jx][jy] = 0;
4220           Tile[jx][jy] = EL_EMPTY;
4221         }
4222       }
4223     }
4224
4225 #else
4226
4227     for (i = 0; i < MAX_PLAYERS; i++)
4228     {
4229       if (stored_player[i].active &&
4230           !tape.player_participates[i])
4231       {
4232         struct PlayerInfo *player = &stored_player[i];
4233         int jx = player->jx, jy = player->jy;
4234
4235         player->active = FALSE;
4236         StorePlayer[jx][jy] = 0;
4237         Tile[jx][jy] = EL_EMPTY;
4238       }
4239     }
4240 #endif
4241   }
4242   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4243   {
4244     // when in single player mode, eliminate all but the local player
4245
4246     for (i = 0; i < MAX_PLAYERS; i++)
4247     {
4248       struct PlayerInfo *player = &stored_player[i];
4249
4250       if (player->active && player != local_player)
4251       {
4252         int jx = player->jx, jy = player->jy;
4253
4254         player->active = FALSE;
4255         player->present = FALSE;
4256
4257         StorePlayer[jx][jy] = 0;
4258         Tile[jx][jy] = EL_EMPTY;
4259       }
4260     }
4261   }
4262
4263   for (i = 0; i < MAX_PLAYERS; i++)
4264     if (stored_player[i].active)
4265       game.players_still_needed++;
4266
4267   if (level.solved_by_one_player)
4268     game.players_still_needed = 1;
4269
4270   // when recording the game, store which players take part in the game
4271   if (tape.recording)
4272   {
4273 #if USE_NEW_PLAYER_ASSIGNMENTS
4274     for (i = 0; i < MAX_PLAYERS; i++)
4275       if (stored_player[i].connected)
4276         tape.player_participates[i] = TRUE;
4277 #else
4278     for (i = 0; i < MAX_PLAYERS; i++)
4279       if (stored_player[i].active)
4280         tape.player_participates[i] = TRUE;
4281 #endif
4282   }
4283
4284 #if DEBUG_INIT_PLAYER
4285   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4286 #endif
4287
4288   if (BorderElement == EL_EMPTY)
4289   {
4290     SBX_Left = 0;
4291     SBX_Right = lev_fieldx - SCR_FIELDX;
4292     SBY_Upper = 0;
4293     SBY_Lower = lev_fieldy - SCR_FIELDY;
4294   }
4295   else
4296   {
4297     SBX_Left = -1;
4298     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4299     SBY_Upper = -1;
4300     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4301   }
4302
4303   if (full_lev_fieldx <= SCR_FIELDX)
4304     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4305   if (full_lev_fieldy <= SCR_FIELDY)
4306     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4307
4308   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4309     SBX_Left--;
4310   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4311     SBY_Upper--;
4312
4313   // if local player not found, look for custom element that might create
4314   // the player (make some assumptions about the right custom element)
4315   if (!local_player->present)
4316   {
4317     int start_x = 0, start_y = 0;
4318     int found_rating = 0;
4319     int found_element = EL_UNDEFINED;
4320     int player_nr = local_player->index_nr;
4321
4322     SCAN_PLAYFIELD(x, y)
4323     {
4324       int element = Tile[x][y];
4325       int content;
4326       int xx, yy;
4327       boolean is_player;
4328
4329       if (level.use_start_element[player_nr] &&
4330           level.start_element[player_nr] == element &&
4331           found_rating < 4)
4332       {
4333         start_x = x;
4334         start_y = y;
4335
4336         found_rating = 4;
4337         found_element = element;
4338       }
4339
4340       if (!IS_CUSTOM_ELEMENT(element))
4341         continue;
4342
4343       if (CAN_CHANGE(element))
4344       {
4345         for (i = 0; i < element_info[element].num_change_pages; i++)
4346         {
4347           // check for player created from custom element as single target
4348           content = element_info[element].change_page[i].target_element;
4349           is_player = IS_PLAYER_ELEMENT(content);
4350
4351           if (is_player && (found_rating < 3 ||
4352                             (found_rating == 3 && element < found_element)))
4353           {
4354             start_x = x;
4355             start_y = y;
4356
4357             found_rating = 3;
4358             found_element = element;
4359           }
4360         }
4361       }
4362
4363       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4364       {
4365         // check for player created from custom element as explosion content
4366         content = element_info[element].content.e[xx][yy];
4367         is_player = IS_PLAYER_ELEMENT(content);
4368
4369         if (is_player && (found_rating < 2 ||
4370                           (found_rating == 2 && element < found_element)))
4371         {
4372           start_x = x + xx - 1;
4373           start_y = y + yy - 1;
4374
4375           found_rating = 2;
4376           found_element = element;
4377         }
4378
4379         if (!CAN_CHANGE(element))
4380           continue;
4381
4382         for (i = 0; i < element_info[element].num_change_pages; i++)
4383         {
4384           // check for player created from custom element as extended target
4385           content =
4386             element_info[element].change_page[i].target_content.e[xx][yy];
4387
4388           is_player = IS_PLAYER_ELEMENT(content);
4389
4390           if (is_player && (found_rating < 1 ||
4391                             (found_rating == 1 && element < found_element)))
4392           {
4393             start_x = x + xx - 1;
4394             start_y = y + yy - 1;
4395
4396             found_rating = 1;
4397             found_element = element;
4398           }
4399         }
4400       }
4401     }
4402
4403     scroll_x = SCROLL_POSITION_X(start_x);
4404     scroll_y = SCROLL_POSITION_Y(start_y);
4405   }
4406   else
4407   {
4408     scroll_x = SCROLL_POSITION_X(local_player->jx);
4409     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4410   }
4411
4412   // !!! FIX THIS (START) !!!
4413   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4414   {
4415     InitGameEngine_EM();
4416   }
4417   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4418   {
4419     InitGameEngine_SP();
4420   }
4421   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4422   {
4423     InitGameEngine_MM();
4424   }
4425   else
4426   {
4427     DrawLevel(REDRAW_FIELD);
4428     DrawAllPlayers();
4429
4430     // after drawing the level, correct some elements
4431     if (game.timegate_time_left == 0)
4432       CloseAllOpenTimegates();
4433   }
4434
4435   // blit playfield from scroll buffer to normal back buffer for fading in
4436   BlitScreenToBitmap(backbuffer);
4437   // !!! FIX THIS (END) !!!
4438
4439   DrawMaskedBorder(fade_mask);
4440
4441   FadeIn(fade_mask);
4442
4443 #if 1
4444   // full screen redraw is required at this point in the following cases:
4445   // - special editor door undrawn when game was started from level editor
4446   // - drawing area (playfield) was changed and has to be removed completely
4447   redraw_mask = REDRAW_ALL;
4448   BackToFront();
4449 #endif
4450
4451   if (!game.restart_level)
4452   {
4453     // copy default game door content to main double buffer
4454
4455     // !!! CHECK AGAIN !!!
4456     SetPanelBackground();
4457     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4458     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4459   }
4460
4461   SetPanelBackground();
4462   SetDrawBackgroundMask(REDRAW_DOOR_1);
4463
4464   UpdateAndDisplayGameControlValues();
4465
4466   if (!game.restart_level)
4467   {
4468     UnmapGameButtons();
4469     UnmapTapeButtons();
4470
4471     FreeGameButtons();
4472     CreateGameButtons();
4473
4474     MapGameButtons();
4475     MapTapeButtons();
4476
4477     // copy actual game door content to door double buffer for OpenDoor()
4478     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4479
4480     OpenDoor(DOOR_OPEN_ALL);
4481
4482     KeyboardAutoRepeatOffUnlessAutoplay();
4483
4484 #if DEBUG_INIT_PLAYER
4485     DebugPrintPlayerStatus("Player status (final)");
4486 #endif
4487   }
4488
4489   UnmapAllGadgets();
4490
4491   MapGameButtons();
4492   MapTapeButtons();
4493
4494   if (!game.restart_level && !tape.playing)
4495   {
4496     LevelStats_incPlayed(level_nr);
4497
4498     SaveLevelSetup_SeriesInfo();
4499   }
4500
4501   game.restart_level = FALSE;
4502   game.restart_game_message = NULL;
4503
4504   game.request_active = FALSE;
4505   game.request_active_or_moving = FALSE;
4506
4507   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4508     InitGameActions_MM();
4509
4510   SaveEngineSnapshotToListInitial();
4511
4512   if (!game.restart_level)
4513   {
4514     PlaySound(SND_GAME_STARTING);
4515
4516     if (setup.sound_music)
4517       PlayLevelMusic();
4518   }
4519
4520   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4521 }
4522
4523 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4524                         int actual_player_x, int actual_player_y)
4525 {
4526   // this is used for non-R'n'D game engines to update certain engine values
4527
4528   // needed to determine if sounds are played within the visible screen area
4529   scroll_x = actual_scroll_x;
4530   scroll_y = actual_scroll_y;
4531
4532   // needed to get player position for "follow finger" playing input method
4533   local_player->jx = actual_player_x;
4534   local_player->jy = actual_player_y;
4535 }
4536
4537 void InitMovDir(int x, int y)
4538 {
4539   int i, element = Tile[x][y];
4540   static int xy[4][2] =
4541   {
4542     {  0, +1 },
4543     { +1,  0 },
4544     {  0, -1 },
4545     { -1,  0 }
4546   };
4547   static int direction[3][4] =
4548   {
4549     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4550     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4551     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4552   };
4553
4554   switch (element)
4555   {
4556     case EL_BUG_RIGHT:
4557     case EL_BUG_UP:
4558     case EL_BUG_LEFT:
4559     case EL_BUG_DOWN:
4560       Tile[x][y] = EL_BUG;
4561       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4562       break;
4563
4564     case EL_SPACESHIP_RIGHT:
4565     case EL_SPACESHIP_UP:
4566     case EL_SPACESHIP_LEFT:
4567     case EL_SPACESHIP_DOWN:
4568       Tile[x][y] = EL_SPACESHIP;
4569       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4570       break;
4571
4572     case EL_BD_BUTTERFLY_RIGHT:
4573     case EL_BD_BUTTERFLY_UP:
4574     case EL_BD_BUTTERFLY_LEFT:
4575     case EL_BD_BUTTERFLY_DOWN:
4576       Tile[x][y] = EL_BD_BUTTERFLY;
4577       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4578       break;
4579
4580     case EL_BD_FIREFLY_RIGHT:
4581     case EL_BD_FIREFLY_UP:
4582     case EL_BD_FIREFLY_LEFT:
4583     case EL_BD_FIREFLY_DOWN:
4584       Tile[x][y] = EL_BD_FIREFLY;
4585       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4586       break;
4587
4588     case EL_PACMAN_RIGHT:
4589     case EL_PACMAN_UP:
4590     case EL_PACMAN_LEFT:
4591     case EL_PACMAN_DOWN:
4592       Tile[x][y] = EL_PACMAN;
4593       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4594       break;
4595
4596     case EL_YAMYAM_LEFT:
4597     case EL_YAMYAM_RIGHT:
4598     case EL_YAMYAM_UP:
4599     case EL_YAMYAM_DOWN:
4600       Tile[x][y] = EL_YAMYAM;
4601       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4602       break;
4603
4604     case EL_SP_SNIKSNAK:
4605       MovDir[x][y] = MV_UP;
4606       break;
4607
4608     case EL_SP_ELECTRON:
4609       MovDir[x][y] = MV_LEFT;
4610       break;
4611
4612     case EL_MOLE_LEFT:
4613     case EL_MOLE_RIGHT:
4614     case EL_MOLE_UP:
4615     case EL_MOLE_DOWN:
4616       Tile[x][y] = EL_MOLE;
4617       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4618       break;
4619
4620     case EL_SPRING_LEFT:
4621     case EL_SPRING_RIGHT:
4622       Tile[x][y] = EL_SPRING;
4623       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4624       break;
4625
4626     default:
4627       if (IS_CUSTOM_ELEMENT(element))
4628       {
4629         struct ElementInfo *ei = &element_info[element];
4630         int move_direction_initial = ei->move_direction_initial;
4631         int move_pattern = ei->move_pattern;
4632
4633         if (move_direction_initial == MV_START_PREVIOUS)
4634         {
4635           if (MovDir[x][y] != MV_NONE)
4636             return;
4637
4638           move_direction_initial = MV_START_AUTOMATIC;
4639         }
4640
4641         if (move_direction_initial == MV_START_RANDOM)
4642           MovDir[x][y] = 1 << RND(4);
4643         else if (move_direction_initial & MV_ANY_DIRECTION)
4644           MovDir[x][y] = move_direction_initial;
4645         else if (move_pattern == MV_ALL_DIRECTIONS ||
4646                  move_pattern == MV_TURNING_LEFT ||
4647                  move_pattern == MV_TURNING_RIGHT ||
4648                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4649                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4650                  move_pattern == MV_TURNING_RANDOM)
4651           MovDir[x][y] = 1 << RND(4);
4652         else if (move_pattern == MV_HORIZONTAL)
4653           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4654         else if (move_pattern == MV_VERTICAL)
4655           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4656         else if (move_pattern & MV_ANY_DIRECTION)
4657           MovDir[x][y] = element_info[element].move_pattern;
4658         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4659                  move_pattern == MV_ALONG_RIGHT_SIDE)
4660         {
4661           // use random direction as default start direction
4662           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4663             MovDir[x][y] = 1 << RND(4);
4664
4665           for (i = 0; i < NUM_DIRECTIONS; i++)
4666           {
4667             int x1 = x + xy[i][0];
4668             int y1 = y + xy[i][1];
4669
4670             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4671             {
4672               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4673                 MovDir[x][y] = direction[0][i];
4674               else
4675                 MovDir[x][y] = direction[1][i];
4676
4677               break;
4678             }
4679           }
4680         }                
4681       }
4682       else
4683       {
4684         MovDir[x][y] = 1 << RND(4);
4685
4686         if (element != EL_BUG &&
4687             element != EL_SPACESHIP &&
4688             element != EL_BD_BUTTERFLY &&
4689             element != EL_BD_FIREFLY)
4690           break;
4691
4692         for (i = 0; i < NUM_DIRECTIONS; i++)
4693         {
4694           int x1 = x + xy[i][0];
4695           int y1 = y + xy[i][1];
4696
4697           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4698           {
4699             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4700             {
4701               MovDir[x][y] = direction[0][i];
4702               break;
4703             }
4704             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4705                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4706             {
4707               MovDir[x][y] = direction[1][i];
4708               break;
4709             }
4710           }
4711         }
4712       }
4713       break;
4714   }
4715
4716   GfxDir[x][y] = MovDir[x][y];
4717 }
4718
4719 void InitAmoebaNr(int x, int y)
4720 {
4721   int i;
4722   int group_nr = AmoebaNeighbourNr(x, y);
4723
4724   if (group_nr == 0)
4725   {
4726     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4727     {
4728       if (AmoebaCnt[i] == 0)
4729       {
4730         group_nr = i;
4731         break;
4732       }
4733     }
4734   }
4735
4736   AmoebaNr[x][y] = group_nr;
4737   AmoebaCnt[group_nr]++;
4738   AmoebaCnt2[group_nr]++;
4739 }
4740
4741 static void LevelSolved_SetFinalGameValues(void)
4742 {
4743   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4744   game.score_time_final = (level.use_step_counter ? TimePlayed :
4745                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4746
4747   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4748                       game_em.lev->score :
4749                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4750                       game_mm.score :
4751                       game.score);
4752
4753   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4754                        MM_HEALTH(game_mm.laser_overload_value) :
4755                        game.health);
4756
4757   game.LevelSolved_CountingTime = game.time_final;
4758   game.LevelSolved_CountingScore = game.score_final;
4759   game.LevelSolved_CountingHealth = game.health_final;
4760 }
4761
4762 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4763 {
4764   game.LevelSolved_CountingTime = time;
4765   game.LevelSolved_CountingScore = score;
4766   game.LevelSolved_CountingHealth = health;
4767
4768   game_panel_controls[GAME_PANEL_TIME].value = time;
4769   game_panel_controls[GAME_PANEL_SCORE].value = score;
4770   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4771
4772   DisplayGameControlValues();
4773 }
4774
4775 static void LevelSolved(void)
4776 {
4777   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4778       game.players_still_needed > 0)
4779     return;
4780
4781   game.LevelSolved = TRUE;
4782   game.GameOver = TRUE;
4783
4784   tape.solved = TRUE;
4785
4786   // needed here to display correct panel values while player walks into exit
4787   LevelSolved_SetFinalGameValues();
4788 }
4789
4790 void GameWon(void)
4791 {
4792   static int time_count_steps;
4793   static int time, time_final;
4794   static float score, score_final; // needed for time score < 10 for 10 seconds
4795   static int health, health_final;
4796   static int game_over_delay_1 = 0;
4797   static int game_over_delay_2 = 0;
4798   static int game_over_delay_3 = 0;
4799   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4800   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4801
4802   if (!game.LevelSolved_GameWon)
4803   {
4804     int i;
4805
4806     // do not start end game actions before the player stops moving (to exit)
4807     if (local_player->active && local_player->MovPos)
4808       return;
4809
4810     // calculate final game values after player finished walking into exit
4811     LevelSolved_SetFinalGameValues();
4812
4813     game.LevelSolved_GameWon = TRUE;
4814     game.LevelSolved_SaveTape = tape.recording;
4815     game.LevelSolved_SaveScore = !tape.playing;
4816
4817     if (!tape.playing)
4818     {
4819       LevelStats_incSolved(level_nr);
4820
4821       SaveLevelSetup_SeriesInfo();
4822     }
4823
4824     if (tape.auto_play)         // tape might already be stopped here
4825       tape.auto_play_level_solved = TRUE;
4826
4827     TapeStop();
4828
4829     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4830     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4831     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4832
4833     time = time_final = game.time_final;
4834     score = score_final = game.score_final;
4835     health = health_final = game.health_final;
4836
4837     // update game panel values before (delayed) counting of score (if any)
4838     LevelSolved_DisplayFinalGameValues(time, score, health);
4839
4840     // if level has time score defined, calculate new final game values
4841     if (time_score > 0)
4842     {
4843       int time_final_max = 999;
4844       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4845       int time_frames = 0;
4846       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4847       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4848
4849       if (TimeLeft > 0)
4850       {
4851         time_final = 0;
4852         time_frames = time_frames_left;
4853       }
4854       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4855       {
4856         time_final = time_final_max;
4857         time_frames = time_frames_final_max - time_frames_played;
4858       }
4859
4860       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4861
4862       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4863
4864       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4865       {
4866         health_final = 0;
4867         score_final += health * time_score;
4868       }
4869
4870       game.score_final = score_final;
4871       game.health_final = health_final;
4872     }
4873
4874     // if not counting score after game, immediately update game panel values
4875     if (level_editor_test_game || !setup.count_score_after_game)
4876     {
4877       time = time_final;
4878       score = score_final;
4879
4880       LevelSolved_DisplayFinalGameValues(time, score, health);
4881     }
4882
4883     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4884     {
4885       // check if last player has left the level
4886       if (game.exit_x >= 0 &&
4887           game.exit_y >= 0)
4888       {
4889         int x = game.exit_x;
4890         int y = game.exit_y;
4891         int element = Tile[x][y];
4892
4893         // close exit door after last player
4894         if ((game.all_players_gone &&
4895              (element == EL_EXIT_OPEN ||
4896               element == EL_SP_EXIT_OPEN ||
4897               element == EL_STEEL_EXIT_OPEN)) ||
4898             element == EL_EM_EXIT_OPEN ||
4899             element == EL_EM_STEEL_EXIT_OPEN)
4900         {
4901
4902           Tile[x][y] =
4903             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4904              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4905              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4906              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4907              EL_EM_STEEL_EXIT_CLOSING);
4908
4909           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4910         }
4911
4912         // player disappears
4913         DrawLevelField(x, y);
4914       }
4915
4916       for (i = 0; i < MAX_PLAYERS; i++)
4917       {
4918         struct PlayerInfo *player = &stored_player[i];
4919
4920         if (player->present)
4921         {
4922           RemovePlayer(player);
4923
4924           // player disappears
4925           DrawLevelField(player->jx, player->jy);
4926         }
4927       }
4928     }
4929
4930     PlaySound(SND_GAME_WINNING);
4931   }
4932
4933   if (setup.count_score_after_game)
4934   {
4935     if (time != time_final)
4936     {
4937       if (game_over_delay_1 > 0)
4938       {
4939         game_over_delay_1--;
4940
4941         return;
4942       }
4943
4944       int time_to_go = ABS(time_final - time);
4945       int time_count_dir = (time < time_final ? +1 : -1);
4946
4947       if (time_to_go < time_count_steps)
4948         time_count_steps = 1;
4949
4950       time  += time_count_steps * time_count_dir;
4951       score += time_count_steps * time_score;
4952
4953       // set final score to correct rounding differences after counting score
4954       if (time == time_final)
4955         score = score_final;
4956
4957       LevelSolved_DisplayFinalGameValues(time, score, health);
4958
4959       if (time == time_final)
4960         StopSound(SND_GAME_LEVELTIME_BONUS);
4961       else if (setup.sound_loops)
4962         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4963       else
4964         PlaySound(SND_GAME_LEVELTIME_BONUS);
4965
4966       return;
4967     }
4968
4969     if (health != health_final)
4970     {
4971       if (game_over_delay_2 > 0)
4972       {
4973         game_over_delay_2--;
4974
4975         return;
4976       }
4977
4978       int health_count_dir = (health < health_final ? +1 : -1);
4979
4980       health += health_count_dir;
4981       score  += time_score;
4982
4983       LevelSolved_DisplayFinalGameValues(time, score, health);
4984
4985       if (health == health_final)
4986         StopSound(SND_GAME_LEVELTIME_BONUS);
4987       else if (setup.sound_loops)
4988         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4989       else
4990         PlaySound(SND_GAME_LEVELTIME_BONUS);
4991
4992       return;
4993     }
4994   }
4995
4996   game.panel.active = FALSE;
4997
4998   if (game_over_delay_3 > 0)
4999   {
5000     game_over_delay_3--;
5001
5002     return;
5003   }
5004
5005   GameEnd();
5006 }
5007
5008 void GameEnd(void)
5009 {
5010   // used instead of "level_nr" (needed for network games)
5011   int last_level_nr = levelset.level_nr;
5012   boolean tape_saved = FALSE;
5013
5014   game.LevelSolved_GameEnd = TRUE;
5015
5016   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5017   {
5018     // make sure that request dialog to save tape does not open door again
5019     if (!global.use_envelope_request)
5020       CloseDoor(DOOR_CLOSE_1);
5021
5022     // ask to save tape
5023     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5024
5025     // set unique basename for score tape (also saved in high score table)
5026     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5027   }
5028
5029   // if no tape is to be saved, close both doors simultaneously
5030   CloseDoor(DOOR_CLOSE_ALL);
5031
5032   if (level_editor_test_game || score_info_tape_play)
5033   {
5034     SetGameStatus(GAME_MODE_MAIN);
5035
5036     DrawMainMenu();
5037
5038     return;
5039   }
5040
5041   if (!game.LevelSolved_SaveScore)
5042   {
5043     SetGameStatus(GAME_MODE_MAIN);
5044
5045     DrawMainMenu();
5046
5047     return;
5048   }
5049
5050   if (level_nr == leveldir_current->handicap_level)
5051   {
5052     leveldir_current->handicap_level++;
5053
5054     SaveLevelSetup_SeriesInfo();
5055   }
5056
5057   // save score and score tape before potentially erasing tape below
5058   NewHighScore(last_level_nr, tape_saved);
5059
5060   if (setup.increment_levels &&
5061       level_nr < leveldir_current->last_level &&
5062       !network_playing)
5063   {
5064     level_nr++;         // advance to next level
5065     TapeErase();        // start with empty tape
5066
5067     if (setup.auto_play_next_level)
5068     {
5069       scores.continue_playing = TRUE;
5070       scores.next_level_nr = level_nr;
5071
5072       LoadLevel(level_nr);
5073
5074       SaveLevelSetup_SeriesInfo();
5075     }
5076   }
5077
5078   if (scores.last_added >= 0 && setup.show_scores_after_game)
5079   {
5080     SetGameStatus(GAME_MODE_SCORES);
5081
5082     DrawHallOfFame(last_level_nr);
5083   }
5084   else if (scores.continue_playing)
5085   {
5086     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5087   }
5088   else
5089   {
5090     SetGameStatus(GAME_MODE_MAIN);
5091
5092     DrawMainMenu();
5093   }
5094 }
5095
5096 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5097                          boolean one_score_entry_per_name)
5098 {
5099   int i;
5100
5101   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5102     return -1;
5103
5104   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5105   {
5106     struct ScoreEntry *entry = &list->entry[i];
5107     boolean score_is_better = (new_entry->score >  entry->score);
5108     boolean score_is_equal  = (new_entry->score == entry->score);
5109     boolean time_is_better  = (new_entry->time  <  entry->time);
5110     boolean time_is_equal   = (new_entry->time  == entry->time);
5111     boolean better_by_score = (score_is_better ||
5112                                (score_is_equal && time_is_better));
5113     boolean better_by_time  = (time_is_better ||
5114                                (time_is_equal && score_is_better));
5115     boolean is_better = (level.rate_time_over_score ? better_by_time :
5116                          better_by_score);
5117     boolean entry_is_empty = (entry->score == 0 &&
5118                               entry->time == 0);
5119
5120     // prevent adding server score entries if also existing in local score file
5121     // (special case: historic score entries have an empty tape basename entry)
5122     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5123         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5124     {
5125       // add fields from server score entry not stored in local score entry
5126       // (currently, this means setting platform, version and country fields;
5127       // in rare cases, this may also correct an invalid score value, as
5128       // historic scores might have been truncated to 16-bit values locally)
5129       *entry = *new_entry;
5130
5131       return -1;
5132     }
5133
5134     if (is_better || entry_is_empty)
5135     {
5136       // player has made it to the hall of fame
5137
5138       if (i < MAX_SCORE_ENTRIES - 1)
5139       {
5140         int m = MAX_SCORE_ENTRIES - 1;
5141         int l;
5142
5143         if (one_score_entry_per_name)
5144         {
5145           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5146             if (strEqual(list->entry[l].name, new_entry->name))
5147               m = l;
5148
5149           if (m == i)   // player's new highscore overwrites his old one
5150             goto put_into_list;
5151         }
5152
5153         for (l = m; l > i; l--)
5154           list->entry[l] = list->entry[l - 1];
5155       }
5156
5157       put_into_list:
5158
5159       *entry = *new_entry;
5160
5161       return i;
5162     }
5163     else if (one_score_entry_per_name &&
5164              strEqual(entry->name, new_entry->name))
5165     {
5166       // player already in high score list with better score or time
5167
5168       return -1;
5169     }
5170   }
5171
5172   // special case: new score is beyond the last high score list position
5173   return MAX_SCORE_ENTRIES;
5174 }
5175
5176 void NewHighScore(int level_nr, boolean tape_saved)
5177 {
5178   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5179   boolean one_per_name = FALSE;
5180
5181   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5182   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5183
5184   new_entry.score = game.score_final;
5185   new_entry.time = game.score_time_final;
5186
5187   LoadScore(level_nr);
5188
5189   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5190
5191   if (scores.last_added >= MAX_SCORE_ENTRIES)
5192   {
5193     scores.last_added = MAX_SCORE_ENTRIES - 1;
5194     scores.force_last_added = TRUE;
5195
5196     scores.entry[scores.last_added] = new_entry;
5197
5198     // store last added local score entry (before merging server scores)
5199     scores.last_added_local = scores.last_added;
5200
5201     return;
5202   }
5203
5204   if (scores.last_added < 0)
5205     return;
5206
5207   SaveScore(level_nr);
5208
5209   // store last added local score entry (before merging server scores)
5210   scores.last_added_local = scores.last_added;
5211
5212   if (!game.LevelSolved_SaveTape)
5213     return;
5214
5215   SaveScoreTape(level_nr);
5216
5217   if (setup.ask_for_using_api_server)
5218   {
5219     setup.use_api_server =
5220       Request("Upload your score and tape to the high score server?", REQ_ASK);
5221
5222     if (!setup.use_api_server)
5223       Request("Not using high score server! Use setup menu to enable again!",
5224               REQ_CONFIRM);
5225
5226     runtime.use_api_server = setup.use_api_server;
5227
5228     // after asking for using API server once, do not ask again
5229     setup.ask_for_using_api_server = FALSE;
5230
5231     SaveSetup_ServerSetup();
5232   }
5233
5234   SaveServerScore(level_nr, tape_saved);
5235 }
5236
5237 void MergeServerScore(void)
5238 {
5239   struct ScoreEntry last_added_entry;
5240   boolean one_per_name = FALSE;
5241   int i;
5242
5243   if (scores.last_added >= 0)
5244     last_added_entry = scores.entry[scores.last_added];
5245
5246   for (i = 0; i < server_scores.num_entries; i++)
5247   {
5248     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5249
5250     if (pos >= 0 && pos <= scores.last_added)
5251       scores.last_added++;
5252   }
5253
5254   if (scores.last_added >= MAX_SCORE_ENTRIES)
5255   {
5256     scores.last_added = MAX_SCORE_ENTRIES - 1;
5257     scores.force_last_added = TRUE;
5258
5259     scores.entry[scores.last_added] = last_added_entry;
5260   }
5261 }
5262
5263 static int getElementMoveStepsizeExt(int x, int y, int direction)
5264 {
5265   int element = Tile[x][y];
5266   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5267   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5268   int horiz_move = (dx != 0);
5269   int sign = (horiz_move ? dx : dy);
5270   int step = sign * element_info[element].move_stepsize;
5271
5272   // special values for move stepsize for spring and things on conveyor belt
5273   if (horiz_move)
5274   {
5275     if (CAN_FALL(element) &&
5276         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5277       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5278     else if (element == EL_SPRING)
5279       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5280   }
5281
5282   return step;
5283 }
5284
5285 static int getElementMoveStepsize(int x, int y)
5286 {
5287   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5288 }
5289
5290 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5291 {
5292   if (player->GfxAction != action || player->GfxDir != dir)
5293   {
5294     player->GfxAction = action;
5295     player->GfxDir = dir;
5296     player->Frame = 0;
5297     player->StepFrame = 0;
5298   }
5299 }
5300
5301 static void ResetGfxFrame(int x, int y)
5302 {
5303   // profiling showed that "autotest" spends 10~20% of its time in this function
5304   if (DrawingDeactivatedField())
5305     return;
5306
5307   int element = Tile[x][y];
5308   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5309
5310   if (graphic_info[graphic].anim_global_sync)
5311     GfxFrame[x][y] = FrameCounter;
5312   else if (graphic_info[graphic].anim_global_anim_sync)
5313     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5314   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5315     GfxFrame[x][y] = CustomValue[x][y];
5316   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5317     GfxFrame[x][y] = element_info[element].collect_score;
5318   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5319     GfxFrame[x][y] = ChangeDelay[x][y];
5320 }
5321
5322 static void ResetGfxAnimation(int x, int y)
5323 {
5324   GfxAction[x][y] = ACTION_DEFAULT;
5325   GfxDir[x][y] = MovDir[x][y];
5326   GfxFrame[x][y] = 0;
5327
5328   ResetGfxFrame(x, y);
5329 }
5330
5331 static void ResetRandomAnimationValue(int x, int y)
5332 {
5333   GfxRandom[x][y] = INIT_GFX_RANDOM();
5334 }
5335
5336 static void InitMovingField(int x, int y, int direction)
5337 {
5338   int element = Tile[x][y];
5339   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5340   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5341   int newx = x + dx;
5342   int newy = y + dy;
5343   boolean is_moving_before, is_moving_after;
5344
5345   // check if element was/is moving or being moved before/after mode change
5346   is_moving_before = (WasJustMoving[x][y] != 0);
5347   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5348
5349   // reset animation only for moving elements which change direction of moving
5350   // or which just started or stopped moving
5351   // (else CEs with property "can move" / "not moving" are reset each frame)
5352   if (is_moving_before != is_moving_after ||
5353       direction != MovDir[x][y])
5354     ResetGfxAnimation(x, y);
5355
5356   MovDir[x][y] = direction;
5357   GfxDir[x][y] = direction;
5358
5359   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5360                      direction == MV_DOWN && CAN_FALL(element) ?
5361                      ACTION_FALLING : ACTION_MOVING);
5362
5363   // this is needed for CEs with property "can move" / "not moving"
5364
5365   if (is_moving_after)
5366   {
5367     if (Tile[newx][newy] == EL_EMPTY)
5368       Tile[newx][newy] = EL_BLOCKED;
5369
5370     MovDir[newx][newy] = MovDir[x][y];
5371
5372     CustomValue[newx][newy] = CustomValue[x][y];
5373
5374     GfxFrame[newx][newy] = GfxFrame[x][y];
5375     GfxRandom[newx][newy] = GfxRandom[x][y];
5376     GfxAction[newx][newy] = GfxAction[x][y];
5377     GfxDir[newx][newy] = GfxDir[x][y];
5378   }
5379 }
5380
5381 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5382 {
5383   int direction = MovDir[x][y];
5384   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5385   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5386
5387   *goes_to_x = newx;
5388   *goes_to_y = newy;
5389 }
5390
5391 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5392 {
5393   int oldx = x, oldy = y;
5394   int direction = MovDir[x][y];
5395
5396   if (direction == MV_LEFT)
5397     oldx++;
5398   else if (direction == MV_RIGHT)
5399     oldx--;
5400   else if (direction == MV_UP)
5401     oldy++;
5402   else if (direction == MV_DOWN)
5403     oldy--;
5404
5405   *comes_from_x = oldx;
5406   *comes_from_y = oldy;
5407 }
5408
5409 static int MovingOrBlocked2Element(int x, int y)
5410 {
5411   int element = Tile[x][y];
5412
5413   if (element == EL_BLOCKED)
5414   {
5415     int oldx, oldy;
5416
5417     Blocked2Moving(x, y, &oldx, &oldy);
5418     return Tile[oldx][oldy];
5419   }
5420   else
5421     return element;
5422 }
5423
5424 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5425 {
5426   // like MovingOrBlocked2Element(), but if element is moving
5427   // and (x,y) is the field the moving element is just leaving,
5428   // return EL_BLOCKED instead of the element value
5429   int element = Tile[x][y];
5430
5431   if (IS_MOVING(x, y))
5432   {
5433     if (element == EL_BLOCKED)
5434     {
5435       int oldx, oldy;
5436
5437       Blocked2Moving(x, y, &oldx, &oldy);
5438       return Tile[oldx][oldy];
5439     }
5440     else
5441       return EL_BLOCKED;
5442   }
5443   else
5444     return element;
5445 }
5446
5447 static void RemoveField(int x, int y)
5448 {
5449   Tile[x][y] = EL_EMPTY;
5450
5451   MovPos[x][y] = 0;
5452   MovDir[x][y] = 0;
5453   MovDelay[x][y] = 0;
5454
5455   CustomValue[x][y] = 0;
5456
5457   AmoebaNr[x][y] = 0;
5458   ChangeDelay[x][y] = 0;
5459   ChangePage[x][y] = -1;
5460   Pushed[x][y] = FALSE;
5461
5462   GfxElement[x][y] = EL_UNDEFINED;
5463   GfxAction[x][y] = ACTION_DEFAULT;
5464   GfxDir[x][y] = MV_NONE;
5465 }
5466
5467 static void RemoveMovingField(int x, int y)
5468 {
5469   int oldx = x, oldy = y, newx = x, newy = y;
5470   int element = Tile[x][y];
5471   int next_element = EL_UNDEFINED;
5472
5473   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5474     return;
5475
5476   if (IS_MOVING(x, y))
5477   {
5478     Moving2Blocked(x, y, &newx, &newy);
5479
5480     if (Tile[newx][newy] != EL_BLOCKED)
5481     {
5482       // element is moving, but target field is not free (blocked), but
5483       // already occupied by something different (example: acid pool);
5484       // in this case, only remove the moving field, but not the target
5485
5486       RemoveField(oldx, oldy);
5487
5488       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5489
5490       TEST_DrawLevelField(oldx, oldy);
5491
5492       return;
5493     }
5494   }
5495   else if (element == EL_BLOCKED)
5496   {
5497     Blocked2Moving(x, y, &oldx, &oldy);
5498     if (!IS_MOVING(oldx, oldy))
5499       return;
5500   }
5501
5502   if (element == EL_BLOCKED &&
5503       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5504        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5505        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5506        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5507        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5508        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5509     next_element = get_next_element(Tile[oldx][oldy]);
5510
5511   RemoveField(oldx, oldy);
5512   RemoveField(newx, newy);
5513
5514   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5515
5516   if (next_element != EL_UNDEFINED)
5517     Tile[oldx][oldy] = next_element;
5518
5519   TEST_DrawLevelField(oldx, oldy);
5520   TEST_DrawLevelField(newx, newy);
5521 }
5522
5523 void DrawDynamite(int x, int y)
5524 {
5525   int sx = SCREENX(x), sy = SCREENY(y);
5526   int graphic = el2img(Tile[x][y]);
5527   int frame;
5528
5529   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5530     return;
5531
5532   if (IS_WALKABLE_INSIDE(Back[x][y]))
5533     return;
5534
5535   if (Back[x][y])
5536     DrawLevelElement(x, y, Back[x][y]);
5537   else if (Store[x][y])
5538     DrawLevelElement(x, y, Store[x][y]);
5539   else if (game.use_masked_elements)
5540     DrawLevelElement(x, y, EL_EMPTY);
5541
5542   frame = getGraphicAnimationFrameXY(graphic, x, y);
5543
5544   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5545     DrawGraphicThruMask(sx, sy, graphic, frame);
5546   else
5547     DrawGraphic(sx, sy, graphic, frame);
5548 }
5549
5550 static void CheckDynamite(int x, int y)
5551 {
5552   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5553   {
5554     MovDelay[x][y]--;
5555
5556     if (MovDelay[x][y] != 0)
5557     {
5558       DrawDynamite(x, y);
5559       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5560
5561       return;
5562     }
5563   }
5564
5565   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5566
5567   Bang(x, y);
5568 }
5569
5570 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5571 {
5572   boolean num_checked_players = 0;
5573   int i;
5574
5575   for (i = 0; i < MAX_PLAYERS; i++)
5576   {
5577     if (stored_player[i].active)
5578     {
5579       int sx = stored_player[i].jx;
5580       int sy = stored_player[i].jy;
5581
5582       if (num_checked_players == 0)
5583       {
5584         *sx1 = *sx2 = sx;
5585         *sy1 = *sy2 = sy;
5586       }
5587       else
5588       {
5589         *sx1 = MIN(*sx1, sx);
5590         *sy1 = MIN(*sy1, sy);
5591         *sx2 = MAX(*sx2, sx);
5592         *sy2 = MAX(*sy2, sy);
5593       }
5594
5595       num_checked_players++;
5596     }
5597   }
5598 }
5599
5600 static boolean checkIfAllPlayersFitToScreen_RND(void)
5601 {
5602   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5603
5604   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5605
5606   return (sx2 - sx1 < SCR_FIELDX &&
5607           sy2 - sy1 < SCR_FIELDY);
5608 }
5609
5610 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5611 {
5612   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5613
5614   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5615
5616   *sx = (sx1 + sx2) / 2;
5617   *sy = (sy1 + sy2) / 2;
5618 }
5619
5620 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5621                                boolean center_screen, boolean quick_relocation)
5622 {
5623   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5624   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5625   boolean no_delay = (tape.warp_forward);
5626   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5627   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5628   int new_scroll_x, new_scroll_y;
5629
5630   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5631   {
5632     // case 1: quick relocation inside visible screen (without scrolling)
5633
5634     RedrawPlayfield();
5635
5636     return;
5637   }
5638
5639   if (!level.shifted_relocation || center_screen)
5640   {
5641     // relocation _with_ centering of screen
5642
5643     new_scroll_x = SCROLL_POSITION_X(x);
5644     new_scroll_y = SCROLL_POSITION_Y(y);
5645   }
5646   else
5647   {
5648     // relocation _without_ centering of screen
5649
5650     int center_scroll_x = SCROLL_POSITION_X(old_x);
5651     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5652     int offset_x = x + (scroll_x - center_scroll_x);
5653     int offset_y = y + (scroll_y - center_scroll_y);
5654
5655     // for new screen position, apply previous offset to center position
5656     new_scroll_x = SCROLL_POSITION_X(offset_x);
5657     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5658   }
5659
5660   if (quick_relocation)
5661   {
5662     // case 2: quick relocation (redraw without visible scrolling)
5663
5664     scroll_x = new_scroll_x;
5665     scroll_y = new_scroll_y;
5666
5667     RedrawPlayfield();
5668
5669     return;
5670   }
5671
5672   // case 3: visible relocation (with scrolling to new position)
5673
5674   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5675
5676   SetVideoFrameDelay(wait_delay_value);
5677
5678   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5679   {
5680     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5681     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5682
5683     if (dx == 0 && dy == 0)             // no scrolling needed at all
5684       break;
5685
5686     scroll_x -= dx;
5687     scroll_y -= dy;
5688
5689     // set values for horizontal/vertical screen scrolling (half tile size)
5690     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5691     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5692     int pos_x = dx * TILEX / 2;
5693     int pos_y = dy * TILEY / 2;
5694     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5695     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5696
5697     ScrollLevel(dx, dy);
5698     DrawAllPlayers();
5699
5700     // scroll in two steps of half tile size to make things smoother
5701     BlitScreenToBitmapExt_RND(window, fx, fy);
5702
5703     // scroll second step to align at full tile size
5704     BlitScreenToBitmap(window);
5705   }
5706
5707   DrawAllPlayers();
5708   BackToFront();
5709
5710   SetVideoFrameDelay(frame_delay_value_old);
5711 }
5712
5713 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5714 {
5715   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5716   int player_nr = GET_PLAYER_NR(el_player);
5717   struct PlayerInfo *player = &stored_player[player_nr];
5718   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5719   boolean no_delay = (tape.warp_forward);
5720   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5721   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5722   int old_jx = player->jx;
5723   int old_jy = player->jy;
5724   int old_element = Tile[old_jx][old_jy];
5725   int element = Tile[jx][jy];
5726   boolean player_relocated = (old_jx != jx || old_jy != jy);
5727
5728   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5729   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5730   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5731   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5732   int leave_side_horiz = move_dir_horiz;
5733   int leave_side_vert  = move_dir_vert;
5734   int enter_side = enter_side_horiz | enter_side_vert;
5735   int leave_side = leave_side_horiz | leave_side_vert;
5736
5737   if (player->buried)           // do not reanimate dead player
5738     return;
5739
5740   if (!player_relocated)        // no need to relocate the player
5741     return;
5742
5743   if (IS_PLAYER(jx, jy))        // player already placed at new position
5744   {
5745     RemoveField(jx, jy);        // temporarily remove newly placed player
5746     DrawLevelField(jx, jy);
5747   }
5748
5749   if (player->present)
5750   {
5751     while (player->MovPos)
5752     {
5753       ScrollPlayer(player, SCROLL_GO_ON);
5754       ScrollScreen(NULL, SCROLL_GO_ON);
5755
5756       AdvanceFrameAndPlayerCounters(player->index_nr);
5757
5758       DrawPlayer(player);
5759
5760       BackToFront_WithFrameDelay(wait_delay_value);
5761     }
5762
5763     DrawPlayer(player);         // needed here only to cleanup last field
5764     DrawLevelField(player->jx, player->jy);     // remove player graphic
5765
5766     player->is_moving = FALSE;
5767   }
5768
5769   if (IS_CUSTOM_ELEMENT(old_element))
5770     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5771                                CE_LEFT_BY_PLAYER,
5772                                player->index_bit, leave_side);
5773
5774   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5775                                       CE_PLAYER_LEAVES_X,
5776                                       player->index_bit, leave_side);
5777
5778   Tile[jx][jy] = el_player;
5779   InitPlayerField(jx, jy, el_player, TRUE);
5780
5781   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5782      possible that the relocation target field did not contain a player element,
5783      but a walkable element, to which the new player was relocated -- in this
5784      case, restore that (already initialized!) element on the player field */
5785   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5786   {
5787     Tile[jx][jy] = element;     // restore previously existing element
5788   }
5789
5790   // only visually relocate centered player
5791   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5792                      FALSE, level.instant_relocation);
5793
5794   TestIfPlayerTouchesBadThing(jx, jy);
5795   TestIfPlayerTouchesCustomElement(jx, jy);
5796
5797   if (IS_CUSTOM_ELEMENT(element))
5798     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5799                                player->index_bit, enter_side);
5800
5801   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5802                                       player->index_bit, enter_side);
5803
5804   if (player->is_switching)
5805   {
5806     /* ensure that relocation while still switching an element does not cause
5807        a new element to be treated as also switched directly after relocation
5808        (this is important for teleporter switches that teleport the player to
5809        a place where another teleporter switch is in the same direction, which
5810        would then incorrectly be treated as immediately switched before the
5811        direction key that caused the switch was released) */
5812
5813     player->switch_x += jx - old_jx;
5814     player->switch_y += jy - old_jy;
5815   }
5816 }
5817
5818 static void Explode(int ex, int ey, int phase, int mode)
5819 {
5820   int x, y;
5821   int last_phase;
5822   int border_element;
5823
5824   // !!! eliminate this variable !!!
5825   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5826
5827   if (game.explosions_delayed)
5828   {
5829     ExplodeField[ex][ey] = mode;
5830     return;
5831   }
5832
5833   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5834   {
5835     int center_element = Tile[ex][ey];
5836     int artwork_element, explosion_element;     // set these values later
5837
5838     // remove things displayed in background while burning dynamite
5839     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5840       Back[ex][ey] = 0;
5841
5842     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5843     {
5844       // put moving element to center field (and let it explode there)
5845       center_element = MovingOrBlocked2Element(ex, ey);
5846       RemoveMovingField(ex, ey);
5847       Tile[ex][ey] = center_element;
5848     }
5849
5850     // now "center_element" is finally determined -- set related values now
5851     artwork_element = center_element;           // for custom player artwork
5852     explosion_element = center_element;         // for custom player artwork
5853
5854     if (IS_PLAYER(ex, ey))
5855     {
5856       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5857
5858       artwork_element = stored_player[player_nr].artwork_element;
5859
5860       if (level.use_explosion_element[player_nr])
5861       {
5862         explosion_element = level.explosion_element[player_nr];
5863         artwork_element = explosion_element;
5864       }
5865     }
5866
5867     if (mode == EX_TYPE_NORMAL ||
5868         mode == EX_TYPE_CENTER ||
5869         mode == EX_TYPE_CROSS)
5870       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5871
5872     last_phase = element_info[explosion_element].explosion_delay + 1;
5873
5874     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5875     {
5876       int xx = x - ex + 1;
5877       int yy = y - ey + 1;
5878       int element;
5879
5880       if (!IN_LEV_FIELD(x, y) ||
5881           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5882           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5883         continue;
5884
5885       element = Tile[x][y];
5886
5887       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5888       {
5889         element = MovingOrBlocked2Element(x, y);
5890
5891         if (!IS_EXPLOSION_PROOF(element))
5892           RemoveMovingField(x, y);
5893       }
5894
5895       // indestructible elements can only explode in center (but not flames)
5896       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5897                                            mode == EX_TYPE_BORDER)) ||
5898           element == EL_FLAMES)
5899         continue;
5900
5901       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5902          behaviour, for example when touching a yamyam that explodes to rocks
5903          with active deadly shield, a rock is created under the player !!! */
5904       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5905 #if 0
5906       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5907           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5908            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5909 #else
5910       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5911 #endif
5912       {
5913         if (IS_ACTIVE_BOMB(element))
5914         {
5915           // re-activate things under the bomb like gate or penguin
5916           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5917           Back[x][y] = 0;
5918         }
5919
5920         continue;
5921       }
5922
5923       // save walkable background elements while explosion on same tile
5924       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5925           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5926         Back[x][y] = element;
5927
5928       // ignite explodable elements reached by other explosion
5929       if (element == EL_EXPLOSION)
5930         element = Store2[x][y];
5931
5932       if (AmoebaNr[x][y] &&
5933           (element == EL_AMOEBA_FULL ||
5934            element == EL_BD_AMOEBA ||
5935            element == EL_AMOEBA_GROWING))
5936       {
5937         AmoebaCnt[AmoebaNr[x][y]]--;
5938         AmoebaCnt2[AmoebaNr[x][y]]--;
5939       }
5940
5941       RemoveField(x, y);
5942
5943       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5944       {
5945         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5946
5947         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5948
5949         if (PLAYERINFO(ex, ey)->use_murphy)
5950           Store[x][y] = EL_EMPTY;
5951       }
5952
5953       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5954       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5955       else if (IS_PLAYER_ELEMENT(center_element))
5956         Store[x][y] = EL_EMPTY;
5957       else if (center_element == EL_YAMYAM)
5958         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5959       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5960         Store[x][y] = element_info[center_element].content.e[xx][yy];
5961 #if 1
5962       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5963       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5964       // otherwise) -- FIX THIS !!!
5965       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5966         Store[x][y] = element_info[element].content.e[1][1];
5967 #else
5968       else if (!CAN_EXPLODE(element))
5969         Store[x][y] = element_info[element].content.e[1][1];
5970 #endif
5971       else
5972         Store[x][y] = EL_EMPTY;
5973
5974       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5975           center_element == EL_AMOEBA_TO_DIAMOND)
5976         Store2[x][y] = element;
5977
5978       Tile[x][y] = EL_EXPLOSION;
5979       GfxElement[x][y] = artwork_element;
5980
5981       ExplodePhase[x][y] = 1;
5982       ExplodeDelay[x][y] = last_phase;
5983
5984       Stop[x][y] = TRUE;
5985     }
5986
5987     if (center_element == EL_YAMYAM)
5988       game.yamyam_content_nr =
5989         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5990
5991     return;
5992   }
5993
5994   if (Stop[ex][ey])
5995     return;
5996
5997   x = ex;
5998   y = ey;
5999
6000   if (phase == 1)
6001     GfxFrame[x][y] = 0;         // restart explosion animation
6002
6003   last_phase = ExplodeDelay[x][y];
6004
6005   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6006
6007   // this can happen if the player leaves an explosion just in time
6008   if (GfxElement[x][y] == EL_UNDEFINED)
6009     GfxElement[x][y] = EL_EMPTY;
6010
6011   border_element = Store2[x][y];
6012   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6013     border_element = StorePlayer[x][y];
6014
6015   if (phase == element_info[border_element].ignition_delay ||
6016       phase == last_phase)
6017   {
6018     boolean border_explosion = FALSE;
6019
6020     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6021         !PLAYER_EXPLOSION_PROTECTED(x, y))
6022     {
6023       KillPlayerUnlessExplosionProtected(x, y);
6024       border_explosion = TRUE;
6025     }
6026     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6027     {
6028       Tile[x][y] = Store2[x][y];
6029       Store2[x][y] = 0;
6030       Bang(x, y);
6031       border_explosion = TRUE;
6032     }
6033     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6034     {
6035       AmoebaToDiamond(x, y);
6036       Store2[x][y] = 0;
6037       border_explosion = TRUE;
6038     }
6039
6040     // if an element just explodes due to another explosion (chain-reaction),
6041     // do not immediately end the new explosion when it was the last frame of
6042     // the explosion (as it would be done in the following "if"-statement!)
6043     if (border_explosion && phase == last_phase)
6044       return;
6045   }
6046
6047   // this can happen if the player was just killed by an explosion
6048   if (GfxElement[x][y] == EL_UNDEFINED)
6049     GfxElement[x][y] = EL_EMPTY;
6050
6051   if (phase == last_phase)
6052   {
6053     int element;
6054
6055     element = Tile[x][y] = Store[x][y];
6056     Store[x][y] = Store2[x][y] = 0;
6057     GfxElement[x][y] = EL_UNDEFINED;
6058
6059     // player can escape from explosions and might therefore be still alive
6060     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6061         element <= EL_PLAYER_IS_EXPLODING_4)
6062     {
6063       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6064       int explosion_element = EL_PLAYER_1 + player_nr;
6065       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6066       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6067
6068       if (level.use_explosion_element[player_nr])
6069         explosion_element = level.explosion_element[player_nr];
6070
6071       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6072                     element_info[explosion_element].content.e[xx][yy]);
6073     }
6074
6075     // restore probably existing indestructible background element
6076     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6077       element = Tile[x][y] = Back[x][y];
6078     Back[x][y] = 0;
6079
6080     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6081     GfxDir[x][y] = MV_NONE;
6082     ChangeDelay[x][y] = 0;
6083     ChangePage[x][y] = -1;
6084
6085     CustomValue[x][y] = 0;
6086
6087     InitField_WithBug2(x, y, FALSE);
6088
6089     TEST_DrawLevelField(x, y);
6090
6091     TestIfElementTouchesCustomElement(x, y);
6092
6093     if (GFX_CRUMBLED(element))
6094       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6095
6096     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6097       StorePlayer[x][y] = 0;
6098
6099     if (IS_PLAYER_ELEMENT(element))
6100       RelocatePlayer(x, y, element);
6101   }
6102   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6103   {
6104     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6105     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6106
6107     if (phase == delay)
6108       TEST_DrawLevelFieldCrumbled(x, y);
6109
6110     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6111     {
6112       DrawLevelElement(x, y, Back[x][y]);
6113       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6114     }
6115     else if (IS_WALKABLE_UNDER(Back[x][y]))
6116     {
6117       DrawLevelGraphic(x, y, graphic, frame);
6118       DrawLevelElementThruMask(x, y, Back[x][y]);
6119     }
6120     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6121       DrawLevelGraphic(x, y, graphic, frame);
6122   }
6123 }
6124
6125 static void DynaExplode(int ex, int ey)
6126 {
6127   int i, j;
6128   int dynabomb_element = Tile[ex][ey];
6129   int dynabomb_size = 1;
6130   boolean dynabomb_xl = FALSE;
6131   struct PlayerInfo *player;
6132   struct XY *xy = xy_topdown;
6133
6134   if (IS_ACTIVE_BOMB(dynabomb_element))
6135   {
6136     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6137     dynabomb_size = player->dynabomb_size;
6138     dynabomb_xl = player->dynabomb_xl;
6139     player->dynabombs_left++;
6140   }
6141
6142   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6143
6144   for (i = 0; i < NUM_DIRECTIONS; i++)
6145   {
6146     for (j = 1; j <= dynabomb_size; j++)
6147     {
6148       int x = ex + j * xy[i].x;
6149       int y = ey + j * xy[i].y;
6150       int element;
6151
6152       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6153         break;
6154
6155       element = Tile[x][y];
6156
6157       // do not restart explosions of fields with active bombs
6158       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6159         continue;
6160
6161       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6162
6163       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6164           !IS_DIGGABLE(element) && !dynabomb_xl)
6165         break;
6166     }
6167   }
6168 }
6169
6170 void Bang(int x, int y)
6171 {
6172   int element = MovingOrBlocked2Element(x, y);
6173   int explosion_type = EX_TYPE_NORMAL;
6174
6175   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6176   {
6177     struct PlayerInfo *player = PLAYERINFO(x, y);
6178
6179     element = Tile[x][y] = player->initial_element;
6180
6181     if (level.use_explosion_element[player->index_nr])
6182     {
6183       int explosion_element = level.explosion_element[player->index_nr];
6184
6185       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6186         explosion_type = EX_TYPE_CROSS;
6187       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6188         explosion_type = EX_TYPE_CENTER;
6189     }
6190   }
6191
6192   switch (element)
6193   {
6194     case EL_BUG:
6195     case EL_SPACESHIP:
6196     case EL_BD_BUTTERFLY:
6197     case EL_BD_FIREFLY:
6198     case EL_YAMYAM:
6199     case EL_DARK_YAMYAM:
6200     case EL_ROBOT:
6201     case EL_PACMAN:
6202     case EL_MOLE:
6203       RaiseScoreElement(element);
6204       break;
6205
6206     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6207     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6208     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6209     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6210     case EL_DYNABOMB_INCREASE_NUMBER:
6211     case EL_DYNABOMB_INCREASE_SIZE:
6212     case EL_DYNABOMB_INCREASE_POWER:
6213       explosion_type = EX_TYPE_DYNA;
6214       break;
6215
6216     case EL_DC_LANDMINE:
6217       explosion_type = EX_TYPE_CENTER;
6218       break;
6219
6220     case EL_PENGUIN:
6221     case EL_LAMP:
6222     case EL_LAMP_ACTIVE:
6223     case EL_AMOEBA_TO_DIAMOND:
6224       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6225         explosion_type = EX_TYPE_CENTER;
6226       break;
6227
6228     default:
6229       if (element_info[element].explosion_type == EXPLODES_CROSS)
6230         explosion_type = EX_TYPE_CROSS;
6231       else if (element_info[element].explosion_type == EXPLODES_1X1)
6232         explosion_type = EX_TYPE_CENTER;
6233       break;
6234   }
6235
6236   if (explosion_type == EX_TYPE_DYNA)
6237     DynaExplode(x, y);
6238   else
6239     Explode(x, y, EX_PHASE_START, explosion_type);
6240
6241   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6242 }
6243
6244 static void SplashAcid(int x, int y)
6245 {
6246   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6247       (!IN_LEV_FIELD(x - 1, y - 2) ||
6248        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6249     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6250
6251   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6252       (!IN_LEV_FIELD(x + 1, y - 2) ||
6253        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6254     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6255
6256   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6257 }
6258
6259 static void InitBeltMovement(void)
6260 {
6261   static int belt_base_element[4] =
6262   {
6263     EL_CONVEYOR_BELT_1_LEFT,
6264     EL_CONVEYOR_BELT_2_LEFT,
6265     EL_CONVEYOR_BELT_3_LEFT,
6266     EL_CONVEYOR_BELT_4_LEFT
6267   };
6268   static int belt_base_active_element[4] =
6269   {
6270     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6271     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6272     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6273     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6274   };
6275
6276   int x, y, i, j;
6277
6278   // set frame order for belt animation graphic according to belt direction
6279   for (i = 0; i < NUM_BELTS; i++)
6280   {
6281     int belt_nr = i;
6282
6283     for (j = 0; j < NUM_BELT_PARTS; j++)
6284     {
6285       int element = belt_base_active_element[belt_nr] + j;
6286       int graphic_1 = el2img(element);
6287       int graphic_2 = el2panelimg(element);
6288
6289       if (game.belt_dir[i] == MV_LEFT)
6290       {
6291         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6292         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6293       }
6294       else
6295       {
6296         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6297         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6298       }
6299     }
6300   }
6301
6302   SCAN_PLAYFIELD(x, y)
6303   {
6304     int element = Tile[x][y];
6305
6306     for (i = 0; i < NUM_BELTS; i++)
6307     {
6308       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6309       {
6310         int e_belt_nr = getBeltNrFromBeltElement(element);
6311         int belt_nr = i;
6312
6313         if (e_belt_nr == belt_nr)
6314         {
6315           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6316
6317           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6318         }
6319       }
6320     }
6321   }
6322 }
6323
6324 static void ToggleBeltSwitch(int x, int y)
6325 {
6326   static int belt_base_element[4] =
6327   {
6328     EL_CONVEYOR_BELT_1_LEFT,
6329     EL_CONVEYOR_BELT_2_LEFT,
6330     EL_CONVEYOR_BELT_3_LEFT,
6331     EL_CONVEYOR_BELT_4_LEFT
6332   };
6333   static int belt_base_active_element[4] =
6334   {
6335     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6336     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6337     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6338     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6339   };
6340   static int belt_base_switch_element[4] =
6341   {
6342     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6343     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6344     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6345     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6346   };
6347   static int belt_move_dir[4] =
6348   {
6349     MV_LEFT,
6350     MV_NONE,
6351     MV_RIGHT,
6352     MV_NONE,
6353   };
6354
6355   int element = Tile[x][y];
6356   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6357   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6358   int belt_dir = belt_move_dir[belt_dir_nr];
6359   int xx, yy, i;
6360
6361   if (!IS_BELT_SWITCH(element))
6362     return;
6363
6364   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6365   game.belt_dir[belt_nr] = belt_dir;
6366
6367   if (belt_dir_nr == 3)
6368     belt_dir_nr = 1;
6369
6370   // set frame order for belt animation graphic according to belt direction
6371   for (i = 0; i < NUM_BELT_PARTS; i++)
6372   {
6373     int element = belt_base_active_element[belt_nr] + i;
6374     int graphic_1 = el2img(element);
6375     int graphic_2 = el2panelimg(element);
6376
6377     if (belt_dir == MV_LEFT)
6378     {
6379       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6380       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6381     }
6382     else
6383     {
6384       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6385       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6386     }
6387   }
6388
6389   SCAN_PLAYFIELD(xx, yy)
6390   {
6391     int element = Tile[xx][yy];
6392
6393     if (IS_BELT_SWITCH(element))
6394     {
6395       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6396
6397       if (e_belt_nr == belt_nr)
6398       {
6399         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6400         TEST_DrawLevelField(xx, yy);
6401       }
6402     }
6403     else if (IS_BELT(element) && belt_dir != MV_NONE)
6404     {
6405       int e_belt_nr = getBeltNrFromBeltElement(element);
6406
6407       if (e_belt_nr == belt_nr)
6408       {
6409         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6410
6411         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6412         TEST_DrawLevelField(xx, yy);
6413       }
6414     }
6415     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6416     {
6417       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6418
6419       if (e_belt_nr == belt_nr)
6420       {
6421         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6422
6423         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6424         TEST_DrawLevelField(xx, yy);
6425       }
6426     }
6427   }
6428 }
6429
6430 static void ToggleSwitchgateSwitch(void)
6431 {
6432   int xx, yy;
6433
6434   game.switchgate_pos = !game.switchgate_pos;
6435
6436   SCAN_PLAYFIELD(xx, yy)
6437   {
6438     int element = Tile[xx][yy];
6439
6440     if (element == EL_SWITCHGATE_SWITCH_UP)
6441     {
6442       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6443       TEST_DrawLevelField(xx, yy);
6444     }
6445     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6446     {
6447       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6448       TEST_DrawLevelField(xx, yy);
6449     }
6450     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6451     {
6452       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6453       TEST_DrawLevelField(xx, yy);
6454     }
6455     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6456     {
6457       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6458       TEST_DrawLevelField(xx, yy);
6459     }
6460     else if (element == EL_SWITCHGATE_OPEN ||
6461              element == EL_SWITCHGATE_OPENING)
6462     {
6463       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6464
6465       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6466     }
6467     else if (element == EL_SWITCHGATE_CLOSED ||
6468              element == EL_SWITCHGATE_CLOSING)
6469     {
6470       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6471
6472       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6473     }
6474   }
6475 }
6476
6477 static int getInvisibleActiveFromInvisibleElement(int element)
6478 {
6479   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6480           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6481           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6482           element);
6483 }
6484
6485 static int getInvisibleFromInvisibleActiveElement(int element)
6486 {
6487   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6488           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6489           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6490           element);
6491 }
6492
6493 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6494 {
6495   int x, y;
6496
6497   SCAN_PLAYFIELD(x, y)
6498   {
6499     int element = Tile[x][y];
6500
6501     if (element == EL_LIGHT_SWITCH &&
6502         game.light_time_left > 0)
6503     {
6504       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6505       TEST_DrawLevelField(x, y);
6506     }
6507     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6508              game.light_time_left == 0)
6509     {
6510       Tile[x][y] = EL_LIGHT_SWITCH;
6511       TEST_DrawLevelField(x, y);
6512     }
6513     else if (element == EL_EMC_DRIPPER &&
6514              game.light_time_left > 0)
6515     {
6516       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6517       TEST_DrawLevelField(x, y);
6518     }
6519     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6520              game.light_time_left == 0)
6521     {
6522       Tile[x][y] = EL_EMC_DRIPPER;
6523       TEST_DrawLevelField(x, y);
6524     }
6525     else if (element == EL_INVISIBLE_STEELWALL ||
6526              element == EL_INVISIBLE_WALL ||
6527              element == EL_INVISIBLE_SAND)
6528     {
6529       if (game.light_time_left > 0)
6530         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6531
6532       TEST_DrawLevelField(x, y);
6533
6534       // uncrumble neighbour fields, if needed
6535       if (element == EL_INVISIBLE_SAND)
6536         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6537     }
6538     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6539              element == EL_INVISIBLE_WALL_ACTIVE ||
6540              element == EL_INVISIBLE_SAND_ACTIVE)
6541     {
6542       if (game.light_time_left == 0)
6543         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6544
6545       TEST_DrawLevelField(x, y);
6546
6547       // re-crumble neighbour fields, if needed
6548       if (element == EL_INVISIBLE_SAND)
6549         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6550     }
6551   }
6552 }
6553
6554 static void RedrawAllInvisibleElementsForLenses(void)
6555 {
6556   int x, y;
6557
6558   SCAN_PLAYFIELD(x, y)
6559   {
6560     int element = Tile[x][y];
6561
6562     if (element == EL_EMC_DRIPPER &&
6563         game.lenses_time_left > 0)
6564     {
6565       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6566       TEST_DrawLevelField(x, y);
6567     }
6568     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6569              game.lenses_time_left == 0)
6570     {
6571       Tile[x][y] = EL_EMC_DRIPPER;
6572       TEST_DrawLevelField(x, y);
6573     }
6574     else if (element == EL_INVISIBLE_STEELWALL ||
6575              element == EL_INVISIBLE_WALL ||
6576              element == EL_INVISIBLE_SAND)
6577     {
6578       if (game.lenses_time_left > 0)
6579         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6580
6581       TEST_DrawLevelField(x, y);
6582
6583       // uncrumble neighbour fields, if needed
6584       if (element == EL_INVISIBLE_SAND)
6585         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6586     }
6587     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6588              element == EL_INVISIBLE_WALL_ACTIVE ||
6589              element == EL_INVISIBLE_SAND_ACTIVE)
6590     {
6591       if (game.lenses_time_left == 0)
6592         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6593
6594       TEST_DrawLevelField(x, y);
6595
6596       // re-crumble neighbour fields, if needed
6597       if (element == EL_INVISIBLE_SAND)
6598         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6599     }
6600   }
6601 }
6602
6603 static void RedrawAllInvisibleElementsForMagnifier(void)
6604 {
6605   int x, y;
6606
6607   SCAN_PLAYFIELD(x, y)
6608   {
6609     int element = Tile[x][y];
6610
6611     if (element == EL_EMC_FAKE_GRASS &&
6612         game.magnify_time_left > 0)
6613     {
6614       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6615       TEST_DrawLevelField(x, y);
6616     }
6617     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6618              game.magnify_time_left == 0)
6619     {
6620       Tile[x][y] = EL_EMC_FAKE_GRASS;
6621       TEST_DrawLevelField(x, y);
6622     }
6623     else if (IS_GATE_GRAY(element) &&
6624              game.magnify_time_left > 0)
6625     {
6626       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6627                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6628                     IS_EM_GATE_GRAY(element) ?
6629                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6630                     IS_EMC_GATE_GRAY(element) ?
6631                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6632                     IS_DC_GATE_GRAY(element) ?
6633                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6634                     element);
6635       TEST_DrawLevelField(x, y);
6636     }
6637     else if (IS_GATE_GRAY_ACTIVE(element) &&
6638              game.magnify_time_left == 0)
6639     {
6640       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6641                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6642                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6643                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6644                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6645                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6646                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6647                     EL_DC_GATE_WHITE_GRAY :
6648                     element);
6649       TEST_DrawLevelField(x, y);
6650     }
6651   }
6652 }
6653
6654 static void ToggleLightSwitch(int x, int y)
6655 {
6656   int element = Tile[x][y];
6657
6658   game.light_time_left =
6659     (element == EL_LIGHT_SWITCH ?
6660      level.time_light * FRAMES_PER_SECOND : 0);
6661
6662   RedrawAllLightSwitchesAndInvisibleElements();
6663 }
6664
6665 static void ActivateTimegateSwitch(int x, int y)
6666 {
6667   int xx, yy;
6668
6669   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6670
6671   SCAN_PLAYFIELD(xx, yy)
6672   {
6673     int element = Tile[xx][yy];
6674
6675     if (element == EL_TIMEGATE_CLOSED ||
6676         element == EL_TIMEGATE_CLOSING)
6677     {
6678       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6679       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6680     }
6681
6682     /*
6683     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6684     {
6685       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6686       TEST_DrawLevelField(xx, yy);
6687     }
6688     */
6689
6690   }
6691
6692   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6693                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6694 }
6695
6696 static void Impact(int x, int y)
6697 {
6698   boolean last_line = (y == lev_fieldy - 1);
6699   boolean object_hit = FALSE;
6700   boolean impact = (last_line || object_hit);
6701   int element = Tile[x][y];
6702   int smashed = EL_STEELWALL;
6703
6704   if (!last_line)       // check if element below was hit
6705   {
6706     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6707       return;
6708
6709     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6710                                          MovDir[x][y + 1] != MV_DOWN ||
6711                                          MovPos[x][y + 1] <= TILEY / 2));
6712
6713     // do not smash moving elements that left the smashed field in time
6714     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6715         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6716       object_hit = FALSE;
6717
6718 #if USE_QUICKSAND_IMPACT_BUGFIX
6719     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6720     {
6721       RemoveMovingField(x, y + 1);
6722       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6723       Tile[x][y + 2] = EL_ROCK;
6724       TEST_DrawLevelField(x, y + 2);
6725
6726       object_hit = TRUE;
6727     }
6728
6729     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6730     {
6731       RemoveMovingField(x, y + 1);
6732       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6733       Tile[x][y + 2] = EL_ROCK;
6734       TEST_DrawLevelField(x, y + 2);
6735
6736       object_hit = TRUE;
6737     }
6738 #endif
6739
6740     if (object_hit)
6741       smashed = MovingOrBlocked2Element(x, y + 1);
6742
6743     impact = (last_line || object_hit);
6744   }
6745
6746   if (!last_line && smashed == EL_ACID) // element falls into acid
6747   {
6748     SplashAcid(x, y + 1);
6749     return;
6750   }
6751
6752   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6753   // only reset graphic animation if graphic really changes after impact
6754   if (impact &&
6755       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6756   {
6757     ResetGfxAnimation(x, y);
6758     TEST_DrawLevelField(x, y);
6759   }
6760
6761   if (impact && CAN_EXPLODE_IMPACT(element))
6762   {
6763     Bang(x, y);
6764     return;
6765   }
6766   else if (impact && element == EL_PEARL &&
6767            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6768   {
6769     ResetGfxAnimation(x, y);
6770
6771     Tile[x][y] = EL_PEARL_BREAKING;
6772     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6773     return;
6774   }
6775   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6776   {
6777     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6778
6779     return;
6780   }
6781
6782   if (impact && element == EL_AMOEBA_DROP)
6783   {
6784     if (object_hit && IS_PLAYER(x, y + 1))
6785       KillPlayerUnlessEnemyProtected(x, y + 1);
6786     else if (object_hit && smashed == EL_PENGUIN)
6787       Bang(x, y + 1);
6788     else
6789     {
6790       Tile[x][y] = EL_AMOEBA_GROWING;
6791       Store[x][y] = EL_AMOEBA_WET;
6792
6793       ResetRandomAnimationValue(x, y);
6794     }
6795     return;
6796   }
6797
6798   if (object_hit)               // check which object was hit
6799   {
6800     if ((CAN_PASS_MAGIC_WALL(element) && 
6801          (smashed == EL_MAGIC_WALL ||
6802           smashed == EL_BD_MAGIC_WALL)) ||
6803         (CAN_PASS_DC_MAGIC_WALL(element) &&
6804          smashed == EL_DC_MAGIC_WALL))
6805     {
6806       int xx, yy;
6807       int activated_magic_wall =
6808         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6809          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6810          EL_DC_MAGIC_WALL_ACTIVE);
6811
6812       // activate magic wall / mill
6813       SCAN_PLAYFIELD(xx, yy)
6814       {
6815         if (Tile[xx][yy] == smashed)
6816           Tile[xx][yy] = activated_magic_wall;
6817       }
6818
6819       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6820       game.magic_wall_active = TRUE;
6821
6822       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6823                             SND_MAGIC_WALL_ACTIVATING :
6824                             smashed == EL_BD_MAGIC_WALL ?
6825                             SND_BD_MAGIC_WALL_ACTIVATING :
6826                             SND_DC_MAGIC_WALL_ACTIVATING));
6827     }
6828
6829     if (IS_PLAYER(x, y + 1))
6830     {
6831       if (CAN_SMASH_PLAYER(element))
6832       {
6833         KillPlayerUnlessEnemyProtected(x, y + 1);
6834         return;
6835       }
6836     }
6837     else if (smashed == EL_PENGUIN)
6838     {
6839       if (CAN_SMASH_PLAYER(element))
6840       {
6841         Bang(x, y + 1);
6842         return;
6843       }
6844     }
6845     else if (element == EL_BD_DIAMOND)
6846     {
6847       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6848       {
6849         Bang(x, y + 1);
6850         return;
6851       }
6852     }
6853     else if (((element == EL_SP_INFOTRON ||
6854                element == EL_SP_ZONK) &&
6855               (smashed == EL_SP_SNIKSNAK ||
6856                smashed == EL_SP_ELECTRON ||
6857                smashed == EL_SP_DISK_ORANGE)) ||
6858              (element == EL_SP_INFOTRON &&
6859               smashed == EL_SP_DISK_YELLOW))
6860     {
6861       Bang(x, y + 1);
6862       return;
6863     }
6864     else if (CAN_SMASH_EVERYTHING(element))
6865     {
6866       if (IS_CLASSIC_ENEMY(smashed) ||
6867           CAN_EXPLODE_SMASHED(smashed))
6868       {
6869         Bang(x, y + 1);
6870         return;
6871       }
6872       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6873       {
6874         if (smashed == EL_LAMP ||
6875             smashed == EL_LAMP_ACTIVE)
6876         {
6877           Bang(x, y + 1);
6878           return;
6879         }
6880         else if (smashed == EL_NUT)
6881         {
6882           Tile[x][y + 1] = EL_NUT_BREAKING;
6883           PlayLevelSound(x, y, SND_NUT_BREAKING);
6884           RaiseScoreElement(EL_NUT);
6885           return;
6886         }
6887         else if (smashed == EL_PEARL)
6888         {
6889           ResetGfxAnimation(x, y);
6890
6891           Tile[x][y + 1] = EL_PEARL_BREAKING;
6892           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6893           return;
6894         }
6895         else if (smashed == EL_DIAMOND)
6896         {
6897           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6898           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6899           return;
6900         }
6901         else if (IS_BELT_SWITCH(smashed))
6902         {
6903           ToggleBeltSwitch(x, y + 1);
6904         }
6905         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6906                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6907                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6908                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6909         {
6910           ToggleSwitchgateSwitch();
6911         }
6912         else if (smashed == EL_LIGHT_SWITCH ||
6913                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6914         {
6915           ToggleLightSwitch(x, y + 1);
6916         }
6917         else
6918         {
6919           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6920
6921           CheckElementChangeBySide(x, y + 1, smashed, element,
6922                                    CE_SWITCHED, CH_SIDE_TOP);
6923           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6924                                             CH_SIDE_TOP);
6925         }
6926       }
6927       else
6928       {
6929         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6930       }
6931     }
6932   }
6933
6934   // play sound of magic wall / mill
6935   if (!last_line &&
6936       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6937        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6938        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6939   {
6940     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6941       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6942     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6943       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6944     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6945       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6946
6947     return;
6948   }
6949
6950   // play sound of object that hits the ground
6951   if (last_line || object_hit)
6952     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6953 }
6954
6955 static void TurnRoundExt(int x, int y)
6956 {
6957   static struct
6958   {
6959     int dx, dy;
6960   } move_xy[] =
6961   {
6962     {  0,  0 },
6963     { -1,  0 },
6964     { +1,  0 },
6965     {  0,  0 },
6966     {  0, -1 },
6967     {  0,  0 }, { 0, 0 }, { 0, 0 },
6968     {  0, +1 }
6969   };
6970   static struct
6971   {
6972     int left, right, back;
6973   } turn[] =
6974   {
6975     { 0,        0,              0        },
6976     { MV_DOWN,  MV_UP,          MV_RIGHT },
6977     { MV_UP,    MV_DOWN,        MV_LEFT  },
6978     { 0,        0,              0        },
6979     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6980     { 0,        0,              0        },
6981     { 0,        0,              0        },
6982     { 0,        0,              0        },
6983     { MV_RIGHT, MV_LEFT,        MV_UP    }
6984   };
6985
6986   int element = Tile[x][y];
6987   int move_pattern = element_info[element].move_pattern;
6988
6989   int old_move_dir = MovDir[x][y];
6990   int left_dir  = turn[old_move_dir].left;
6991   int right_dir = turn[old_move_dir].right;
6992   int back_dir  = turn[old_move_dir].back;
6993
6994   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6995   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6996   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6997   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6998
6999   int left_x  = x + left_dx,  left_y  = y + left_dy;
7000   int right_x = x + right_dx, right_y = y + right_dy;
7001   int move_x  = x + move_dx,  move_y  = y + move_dy;
7002
7003   int xx, yy;
7004
7005   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7006   {
7007     TestIfBadThingTouchesOtherBadThing(x, y);
7008
7009     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7010       MovDir[x][y] = right_dir;
7011     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7012       MovDir[x][y] = left_dir;
7013
7014     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7015       MovDelay[x][y] = 9;
7016     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7017       MovDelay[x][y] = 1;
7018   }
7019   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7020   {
7021     TestIfBadThingTouchesOtherBadThing(x, y);
7022
7023     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7024       MovDir[x][y] = left_dir;
7025     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7026       MovDir[x][y] = right_dir;
7027
7028     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7029       MovDelay[x][y] = 9;
7030     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7031       MovDelay[x][y] = 1;
7032   }
7033   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7034   {
7035     TestIfBadThingTouchesOtherBadThing(x, y);
7036
7037     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7038       MovDir[x][y] = left_dir;
7039     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7040       MovDir[x][y] = right_dir;
7041
7042     if (MovDir[x][y] != old_move_dir)
7043       MovDelay[x][y] = 9;
7044   }
7045   else if (element == EL_YAMYAM)
7046   {
7047     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7048     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7049
7050     if (can_turn_left && can_turn_right)
7051       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7052     else if (can_turn_left)
7053       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7054     else if (can_turn_right)
7055       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7056     else
7057       MovDir[x][y] = back_dir;
7058
7059     MovDelay[x][y] = 16 + 16 * RND(3);
7060   }
7061   else if (element == EL_DARK_YAMYAM)
7062   {
7063     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7064                                                          left_x, left_y);
7065     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7066                                                          right_x, right_y);
7067
7068     if (can_turn_left && can_turn_right)
7069       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7070     else if (can_turn_left)
7071       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7072     else if (can_turn_right)
7073       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7074     else
7075       MovDir[x][y] = back_dir;
7076
7077     MovDelay[x][y] = 16 + 16 * RND(3);
7078   }
7079   else if (element == EL_PACMAN)
7080   {
7081     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7082     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7083
7084     if (can_turn_left && can_turn_right)
7085       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7086     else if (can_turn_left)
7087       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7088     else if (can_turn_right)
7089       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7090     else
7091       MovDir[x][y] = back_dir;
7092
7093     MovDelay[x][y] = 6 + RND(40);
7094   }
7095   else if (element == EL_PIG)
7096   {
7097     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7098     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7099     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7100     boolean should_turn_left, should_turn_right, should_move_on;
7101     int rnd_value = 24;
7102     int rnd = RND(rnd_value);
7103
7104     should_turn_left = (can_turn_left &&
7105                         (!can_move_on ||
7106                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7107                                                    y + back_dy + left_dy)));
7108     should_turn_right = (can_turn_right &&
7109                          (!can_move_on ||
7110                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7111                                                     y + back_dy + right_dy)));
7112     should_move_on = (can_move_on &&
7113                       (!can_turn_left ||
7114                        !can_turn_right ||
7115                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7116                                                  y + move_dy + left_dy) ||
7117                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7118                                                  y + move_dy + right_dy)));
7119
7120     if (should_turn_left || should_turn_right || should_move_on)
7121     {
7122       if (should_turn_left && should_turn_right && should_move_on)
7123         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7124                         rnd < 2 * rnd_value / 3 ? right_dir :
7125                         old_move_dir);
7126       else if (should_turn_left && should_turn_right)
7127         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7128       else if (should_turn_left && should_move_on)
7129         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7130       else if (should_turn_right && should_move_on)
7131         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7132       else if (should_turn_left)
7133         MovDir[x][y] = left_dir;
7134       else if (should_turn_right)
7135         MovDir[x][y] = right_dir;
7136       else if (should_move_on)
7137         MovDir[x][y] = old_move_dir;
7138     }
7139     else if (can_move_on && rnd > rnd_value / 8)
7140       MovDir[x][y] = old_move_dir;
7141     else if (can_turn_left && can_turn_right)
7142       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7143     else if (can_turn_left && rnd > rnd_value / 8)
7144       MovDir[x][y] = left_dir;
7145     else if (can_turn_right && rnd > rnd_value/8)
7146       MovDir[x][y] = right_dir;
7147     else
7148       MovDir[x][y] = back_dir;
7149
7150     xx = x + move_xy[MovDir[x][y]].dx;
7151     yy = y + move_xy[MovDir[x][y]].dy;
7152
7153     if (!IN_LEV_FIELD(xx, yy) ||
7154         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7155       MovDir[x][y] = old_move_dir;
7156
7157     MovDelay[x][y] = 0;
7158   }
7159   else if (element == EL_DRAGON)
7160   {
7161     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7162     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7163     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7164     int rnd_value = 24;
7165     int rnd = RND(rnd_value);
7166
7167     if (can_move_on && rnd > rnd_value / 8)
7168       MovDir[x][y] = old_move_dir;
7169     else if (can_turn_left && can_turn_right)
7170       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7171     else if (can_turn_left && rnd > rnd_value / 8)
7172       MovDir[x][y] = left_dir;
7173     else if (can_turn_right && rnd > rnd_value / 8)
7174       MovDir[x][y] = right_dir;
7175     else
7176       MovDir[x][y] = back_dir;
7177
7178     xx = x + move_xy[MovDir[x][y]].dx;
7179     yy = y + move_xy[MovDir[x][y]].dy;
7180
7181     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7182       MovDir[x][y] = old_move_dir;
7183
7184     MovDelay[x][y] = 0;
7185   }
7186   else if (element == EL_MOLE)
7187   {
7188     boolean can_move_on =
7189       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7190                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7191                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7192     if (!can_move_on)
7193     {
7194       boolean can_turn_left =
7195         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7196                               IS_AMOEBOID(Tile[left_x][left_y])));
7197
7198       boolean can_turn_right =
7199         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7200                               IS_AMOEBOID(Tile[right_x][right_y])));
7201
7202       if (can_turn_left && can_turn_right)
7203         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7204       else if (can_turn_left)
7205         MovDir[x][y] = left_dir;
7206       else
7207         MovDir[x][y] = right_dir;
7208     }
7209
7210     if (MovDir[x][y] != old_move_dir)
7211       MovDelay[x][y] = 9;
7212   }
7213   else if (element == EL_BALLOON)
7214   {
7215     MovDir[x][y] = game.wind_direction;
7216     MovDelay[x][y] = 0;
7217   }
7218   else if (element == EL_SPRING)
7219   {
7220     if (MovDir[x][y] & MV_HORIZONTAL)
7221     {
7222       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7223           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7224       {
7225         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7226         ResetGfxAnimation(move_x, move_y);
7227         TEST_DrawLevelField(move_x, move_y);
7228
7229         MovDir[x][y] = back_dir;
7230       }
7231       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7232                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7233         MovDir[x][y] = MV_NONE;
7234     }
7235
7236     MovDelay[x][y] = 0;
7237   }
7238   else if (element == EL_ROBOT ||
7239            element == EL_SATELLITE ||
7240            element == EL_PENGUIN ||
7241            element == EL_EMC_ANDROID)
7242   {
7243     int attr_x = -1, attr_y = -1;
7244
7245     if (game.all_players_gone)
7246     {
7247       attr_x = game.exit_x;
7248       attr_y = game.exit_y;
7249     }
7250     else
7251     {
7252       int i;
7253
7254       for (i = 0; i < MAX_PLAYERS; i++)
7255       {
7256         struct PlayerInfo *player = &stored_player[i];
7257         int jx = player->jx, jy = player->jy;
7258
7259         if (!player->active)
7260           continue;
7261
7262         if (attr_x == -1 ||
7263             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7264         {
7265           attr_x = jx;
7266           attr_y = jy;
7267         }
7268       }
7269     }
7270
7271     if (element == EL_ROBOT &&
7272         game.robot_wheel_x >= 0 &&
7273         game.robot_wheel_y >= 0 &&
7274         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7275          game.engine_version < VERSION_IDENT(3,1,0,0)))
7276     {
7277       attr_x = game.robot_wheel_x;
7278       attr_y = game.robot_wheel_y;
7279     }
7280
7281     if (element == EL_PENGUIN)
7282     {
7283       int i;
7284       struct XY *xy = xy_topdown;
7285
7286       for (i = 0; i < NUM_DIRECTIONS; i++)
7287       {
7288         int ex = x + xy[i].x;
7289         int ey = y + xy[i].y;
7290
7291         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7292                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7293                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7294                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7295         {
7296           attr_x = ex;
7297           attr_y = ey;
7298           break;
7299         }
7300       }
7301     }
7302
7303     MovDir[x][y] = MV_NONE;
7304     if (attr_x < x)
7305       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7306     else if (attr_x > x)
7307       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7308     if (attr_y < y)
7309       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7310     else if (attr_y > y)
7311       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7312
7313     if (element == EL_ROBOT)
7314     {
7315       int newx, newy;
7316
7317       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7318         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7319       Moving2Blocked(x, y, &newx, &newy);
7320
7321       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7322         MovDelay[x][y] = 8 + 8 * !RND(3);
7323       else
7324         MovDelay[x][y] = 16;
7325     }
7326     else if (element == EL_PENGUIN)
7327     {
7328       int newx, newy;
7329
7330       MovDelay[x][y] = 1;
7331
7332       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7333       {
7334         boolean first_horiz = RND(2);
7335         int new_move_dir = MovDir[x][y];
7336
7337         MovDir[x][y] =
7338           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7339         Moving2Blocked(x, y, &newx, &newy);
7340
7341         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7342           return;
7343
7344         MovDir[x][y] =
7345           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7346         Moving2Blocked(x, y, &newx, &newy);
7347
7348         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7349           return;
7350
7351         MovDir[x][y] = old_move_dir;
7352         return;
7353       }
7354     }
7355     else if (element == EL_SATELLITE)
7356     {
7357       int newx, newy;
7358
7359       MovDelay[x][y] = 1;
7360
7361       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7362       {
7363         boolean first_horiz = RND(2);
7364         int new_move_dir = MovDir[x][y];
7365
7366         MovDir[x][y] =
7367           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7368         Moving2Blocked(x, y, &newx, &newy);
7369
7370         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7371           return;
7372
7373         MovDir[x][y] =
7374           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7375         Moving2Blocked(x, y, &newx, &newy);
7376
7377         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7378           return;
7379
7380         MovDir[x][y] = old_move_dir;
7381         return;
7382       }
7383     }
7384     else if (element == EL_EMC_ANDROID)
7385     {
7386       static int check_pos[16] =
7387       {
7388         -1,             //  0 => (invalid)
7389         7,              //  1 => MV_LEFT
7390         3,              //  2 => MV_RIGHT
7391         -1,             //  3 => (invalid)
7392         1,              //  4 =>            MV_UP
7393         0,              //  5 => MV_LEFT  | MV_UP
7394         2,              //  6 => MV_RIGHT | MV_UP
7395         -1,             //  7 => (invalid)
7396         5,              //  8 =>            MV_DOWN
7397         6,              //  9 => MV_LEFT  | MV_DOWN
7398         4,              // 10 => MV_RIGHT | MV_DOWN
7399         -1,             // 11 => (invalid)
7400         -1,             // 12 => (invalid)
7401         -1,             // 13 => (invalid)
7402         -1,             // 14 => (invalid)
7403         -1,             // 15 => (invalid)
7404       };
7405       static struct
7406       {
7407         int dx, dy;
7408         int dir;
7409       } check_xy[8] =
7410       {
7411         { -1, -1,       MV_LEFT  | MV_UP   },
7412         {  0, -1,                  MV_UP   },
7413         { +1, -1,       MV_RIGHT | MV_UP   },
7414         { +1,  0,       MV_RIGHT           },
7415         { +1, +1,       MV_RIGHT | MV_DOWN },
7416         {  0, +1,                  MV_DOWN },
7417         { -1, +1,       MV_LEFT  | MV_DOWN },
7418         { -1,  0,       MV_LEFT            },
7419       };
7420       int start_pos, check_order;
7421       boolean can_clone = FALSE;
7422       int i;
7423
7424       // check if there is any free field around current position
7425       for (i = 0; i < 8; i++)
7426       {
7427         int newx = x + check_xy[i].dx;
7428         int newy = y + check_xy[i].dy;
7429
7430         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7431         {
7432           can_clone = TRUE;
7433
7434           break;
7435         }
7436       }
7437
7438       if (can_clone)            // randomly find an element to clone
7439       {
7440         can_clone = FALSE;
7441
7442         start_pos = check_pos[RND(8)];
7443         check_order = (RND(2) ? -1 : +1);
7444
7445         for (i = 0; i < 8; i++)
7446         {
7447           int pos_raw = start_pos + i * check_order;
7448           int pos = (pos_raw + 8) % 8;
7449           int newx = x + check_xy[pos].dx;
7450           int newy = y + check_xy[pos].dy;
7451
7452           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7453           {
7454             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7455             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7456
7457             Store[x][y] = Tile[newx][newy];
7458
7459             can_clone = TRUE;
7460
7461             break;
7462           }
7463         }
7464       }
7465
7466       if (can_clone)            // randomly find a direction to move
7467       {
7468         can_clone = FALSE;
7469
7470         start_pos = check_pos[RND(8)];
7471         check_order = (RND(2) ? -1 : +1);
7472
7473         for (i = 0; i < 8; i++)
7474         {
7475           int pos_raw = start_pos + i * check_order;
7476           int pos = (pos_raw + 8) % 8;
7477           int newx = x + check_xy[pos].dx;
7478           int newy = y + check_xy[pos].dy;
7479           int new_move_dir = check_xy[pos].dir;
7480
7481           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7482           {
7483             MovDir[x][y] = new_move_dir;
7484             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7485
7486             can_clone = TRUE;
7487
7488             break;
7489           }
7490         }
7491       }
7492
7493       if (can_clone)            // cloning and moving successful
7494         return;
7495
7496       // cannot clone -- try to move towards player
7497
7498       start_pos = check_pos[MovDir[x][y] & 0x0f];
7499       check_order = (RND(2) ? -1 : +1);
7500
7501       for (i = 0; i < 3; i++)
7502       {
7503         // first check start_pos, then previous/next or (next/previous) pos
7504         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7505         int pos = (pos_raw + 8) % 8;
7506         int newx = x + check_xy[pos].dx;
7507         int newy = y + check_xy[pos].dy;
7508         int new_move_dir = check_xy[pos].dir;
7509
7510         if (IS_PLAYER(newx, newy))
7511           break;
7512
7513         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7514         {
7515           MovDir[x][y] = new_move_dir;
7516           MovDelay[x][y] = level.android_move_time * 8 + 1;
7517
7518           break;
7519         }
7520       }
7521     }
7522   }
7523   else if (move_pattern == MV_TURNING_LEFT ||
7524            move_pattern == MV_TURNING_RIGHT ||
7525            move_pattern == MV_TURNING_LEFT_RIGHT ||
7526            move_pattern == MV_TURNING_RIGHT_LEFT ||
7527            move_pattern == MV_TURNING_RANDOM ||
7528            move_pattern == MV_ALL_DIRECTIONS)
7529   {
7530     boolean can_turn_left =
7531       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7532     boolean can_turn_right =
7533       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7534
7535     if (element_info[element].move_stepsize == 0)       // "not moving"
7536       return;
7537
7538     if (move_pattern == MV_TURNING_LEFT)
7539       MovDir[x][y] = left_dir;
7540     else if (move_pattern == MV_TURNING_RIGHT)
7541       MovDir[x][y] = right_dir;
7542     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7543       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7544     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7545       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7546     else if (move_pattern == MV_TURNING_RANDOM)
7547       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7548                       can_turn_right && !can_turn_left ? right_dir :
7549                       RND(2) ? left_dir : right_dir);
7550     else if (can_turn_left && can_turn_right)
7551       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7552     else if (can_turn_left)
7553       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7554     else if (can_turn_right)
7555       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7556     else
7557       MovDir[x][y] = back_dir;
7558
7559     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7560   }
7561   else if (move_pattern == MV_HORIZONTAL ||
7562            move_pattern == MV_VERTICAL)
7563   {
7564     if (move_pattern & old_move_dir)
7565       MovDir[x][y] = back_dir;
7566     else if (move_pattern == MV_HORIZONTAL)
7567       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7568     else if (move_pattern == MV_VERTICAL)
7569       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7570
7571     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7572   }
7573   else if (move_pattern & MV_ANY_DIRECTION)
7574   {
7575     MovDir[x][y] = move_pattern;
7576     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7577   }
7578   else if (move_pattern & MV_WIND_DIRECTION)
7579   {
7580     MovDir[x][y] = game.wind_direction;
7581     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7582   }
7583   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7584   {
7585     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7586       MovDir[x][y] = left_dir;
7587     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7588       MovDir[x][y] = right_dir;
7589
7590     if (MovDir[x][y] != old_move_dir)
7591       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7592   }
7593   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7594   {
7595     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7596       MovDir[x][y] = right_dir;
7597     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7598       MovDir[x][y] = left_dir;
7599
7600     if (MovDir[x][y] != old_move_dir)
7601       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7602   }
7603   else if (move_pattern == MV_TOWARDS_PLAYER ||
7604            move_pattern == MV_AWAY_FROM_PLAYER)
7605   {
7606     int attr_x = -1, attr_y = -1;
7607     int newx, newy;
7608     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7609
7610     if (game.all_players_gone)
7611     {
7612       attr_x = game.exit_x;
7613       attr_y = game.exit_y;
7614     }
7615     else
7616     {
7617       int i;
7618
7619       for (i = 0; i < MAX_PLAYERS; i++)
7620       {
7621         struct PlayerInfo *player = &stored_player[i];
7622         int jx = player->jx, jy = player->jy;
7623
7624         if (!player->active)
7625           continue;
7626
7627         if (attr_x == -1 ||
7628             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7629         {
7630           attr_x = jx;
7631           attr_y = jy;
7632         }
7633       }
7634     }
7635
7636     MovDir[x][y] = MV_NONE;
7637     if (attr_x < x)
7638       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7639     else if (attr_x > x)
7640       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7641     if (attr_y < y)
7642       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7643     else if (attr_y > y)
7644       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7645
7646     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7647
7648     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7649     {
7650       boolean first_horiz = RND(2);
7651       int new_move_dir = MovDir[x][y];
7652
7653       if (element_info[element].move_stepsize == 0)     // "not moving"
7654       {
7655         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7656         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7657
7658         return;
7659       }
7660
7661       MovDir[x][y] =
7662         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7663       Moving2Blocked(x, y, &newx, &newy);
7664
7665       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7666         return;
7667
7668       MovDir[x][y] =
7669         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7670       Moving2Blocked(x, y, &newx, &newy);
7671
7672       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7673         return;
7674
7675       MovDir[x][y] = old_move_dir;
7676     }
7677   }
7678   else if (move_pattern == MV_WHEN_PUSHED ||
7679            move_pattern == MV_WHEN_DROPPED)
7680   {
7681     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7682       MovDir[x][y] = MV_NONE;
7683
7684     MovDelay[x][y] = 0;
7685   }
7686   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7687   {
7688     struct XY *test_xy = xy_topdown;
7689     static int test_dir[4] =
7690     {
7691       MV_UP,
7692       MV_LEFT,
7693       MV_RIGHT,
7694       MV_DOWN
7695     };
7696     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7697     int move_preference = -1000000;     // start with very low preference
7698     int new_move_dir = MV_NONE;
7699     int start_test = RND(4);
7700     int i;
7701
7702     for (i = 0; i < NUM_DIRECTIONS; i++)
7703     {
7704       int j = (start_test + i) % 4;
7705       int move_dir = test_dir[j];
7706       int move_dir_preference;
7707
7708       xx = x + test_xy[j].x;
7709       yy = y + test_xy[j].y;
7710
7711       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7712           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7713       {
7714         new_move_dir = move_dir;
7715
7716         break;
7717       }
7718
7719       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7720         continue;
7721
7722       move_dir_preference = -1 * RunnerVisit[xx][yy];
7723       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7724         move_dir_preference = PlayerVisit[xx][yy];
7725
7726       if (move_dir_preference > move_preference)
7727       {
7728         // prefer field that has not been visited for the longest time
7729         move_preference = move_dir_preference;
7730         new_move_dir = move_dir;
7731       }
7732       else if (move_dir_preference == move_preference &&
7733                move_dir == old_move_dir)
7734       {
7735         // prefer last direction when all directions are preferred equally
7736         move_preference = move_dir_preference;
7737         new_move_dir = move_dir;
7738       }
7739     }
7740
7741     MovDir[x][y] = new_move_dir;
7742     if (old_move_dir != new_move_dir)
7743       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7744   }
7745 }
7746
7747 static void TurnRound(int x, int y)
7748 {
7749   int direction = MovDir[x][y];
7750
7751   TurnRoundExt(x, y);
7752
7753   GfxDir[x][y] = MovDir[x][y];
7754
7755   if (direction != MovDir[x][y])
7756     GfxFrame[x][y] = 0;
7757
7758   if (MovDelay[x][y])
7759     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7760
7761   ResetGfxFrame(x, y);
7762 }
7763
7764 static boolean JustBeingPushed(int x, int y)
7765 {
7766   int i;
7767
7768   for (i = 0; i < MAX_PLAYERS; i++)
7769   {
7770     struct PlayerInfo *player = &stored_player[i];
7771
7772     if (player->active && player->is_pushing && player->MovPos)
7773     {
7774       int next_jx = player->jx + (player->jx - player->last_jx);
7775       int next_jy = player->jy + (player->jy - player->last_jy);
7776
7777       if (x == next_jx && y == next_jy)
7778         return TRUE;
7779     }
7780   }
7781
7782   return FALSE;
7783 }
7784
7785 static void StartMoving(int x, int y)
7786 {
7787   boolean started_moving = FALSE;       // some elements can fall _and_ move
7788   int element = Tile[x][y];
7789
7790   if (Stop[x][y])
7791     return;
7792
7793   if (MovDelay[x][y] == 0)
7794     GfxAction[x][y] = ACTION_DEFAULT;
7795
7796   if (CAN_FALL(element) && y < lev_fieldy - 1)
7797   {
7798     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7799         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7800       if (JustBeingPushed(x, y))
7801         return;
7802
7803     if (element == EL_QUICKSAND_FULL)
7804     {
7805       if (IS_FREE(x, y + 1))
7806       {
7807         InitMovingField(x, y, MV_DOWN);
7808         started_moving = TRUE;
7809
7810         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7811 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7812         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7813           Store[x][y] = EL_ROCK;
7814 #else
7815         Store[x][y] = EL_ROCK;
7816 #endif
7817
7818         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7819       }
7820       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7821       {
7822         if (!MovDelay[x][y])
7823         {
7824           MovDelay[x][y] = TILEY + 1;
7825
7826           ResetGfxAnimation(x, y);
7827           ResetGfxAnimation(x, y + 1);
7828         }
7829
7830         if (MovDelay[x][y])
7831         {
7832           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7833           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7834
7835           MovDelay[x][y]--;
7836           if (MovDelay[x][y])
7837             return;
7838         }
7839
7840         Tile[x][y] = EL_QUICKSAND_EMPTY;
7841         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7842         Store[x][y + 1] = Store[x][y];
7843         Store[x][y] = 0;
7844
7845         PlayLevelSoundAction(x, y, ACTION_FILLING);
7846       }
7847       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7848       {
7849         if (!MovDelay[x][y])
7850         {
7851           MovDelay[x][y] = TILEY + 1;
7852
7853           ResetGfxAnimation(x, y);
7854           ResetGfxAnimation(x, y + 1);
7855         }
7856
7857         if (MovDelay[x][y])
7858         {
7859           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7860           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7861
7862           MovDelay[x][y]--;
7863           if (MovDelay[x][y])
7864             return;
7865         }
7866
7867         Tile[x][y] = EL_QUICKSAND_EMPTY;
7868         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7869         Store[x][y + 1] = Store[x][y];
7870         Store[x][y] = 0;
7871
7872         PlayLevelSoundAction(x, y, ACTION_FILLING);
7873       }
7874     }
7875     else if (element == EL_QUICKSAND_FAST_FULL)
7876     {
7877       if (IS_FREE(x, y + 1))
7878       {
7879         InitMovingField(x, y, MV_DOWN);
7880         started_moving = TRUE;
7881
7882         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7883 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7884         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7885           Store[x][y] = EL_ROCK;
7886 #else
7887         Store[x][y] = EL_ROCK;
7888 #endif
7889
7890         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7891       }
7892       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7893       {
7894         if (!MovDelay[x][y])
7895         {
7896           MovDelay[x][y] = TILEY + 1;
7897
7898           ResetGfxAnimation(x, y);
7899           ResetGfxAnimation(x, y + 1);
7900         }
7901
7902         if (MovDelay[x][y])
7903         {
7904           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7905           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7906
7907           MovDelay[x][y]--;
7908           if (MovDelay[x][y])
7909             return;
7910         }
7911
7912         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7913         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7914         Store[x][y + 1] = Store[x][y];
7915         Store[x][y] = 0;
7916
7917         PlayLevelSoundAction(x, y, ACTION_FILLING);
7918       }
7919       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7920       {
7921         if (!MovDelay[x][y])
7922         {
7923           MovDelay[x][y] = TILEY + 1;
7924
7925           ResetGfxAnimation(x, y);
7926           ResetGfxAnimation(x, y + 1);
7927         }
7928
7929         if (MovDelay[x][y])
7930         {
7931           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7932           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7933
7934           MovDelay[x][y]--;
7935           if (MovDelay[x][y])
7936             return;
7937         }
7938
7939         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7940         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7941         Store[x][y + 1] = Store[x][y];
7942         Store[x][y] = 0;
7943
7944         PlayLevelSoundAction(x, y, ACTION_FILLING);
7945       }
7946     }
7947     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7948              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7949     {
7950       InitMovingField(x, y, MV_DOWN);
7951       started_moving = TRUE;
7952
7953       Tile[x][y] = EL_QUICKSAND_FILLING;
7954       Store[x][y] = element;
7955
7956       PlayLevelSoundAction(x, y, ACTION_FILLING);
7957     }
7958     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7959              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7960     {
7961       InitMovingField(x, y, MV_DOWN);
7962       started_moving = TRUE;
7963
7964       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7965       Store[x][y] = element;
7966
7967       PlayLevelSoundAction(x, y, ACTION_FILLING);
7968     }
7969     else if (element == EL_MAGIC_WALL_FULL)
7970     {
7971       if (IS_FREE(x, y + 1))
7972       {
7973         InitMovingField(x, y, MV_DOWN);
7974         started_moving = TRUE;
7975
7976         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7977         Store[x][y] = EL_CHANGED(Store[x][y]);
7978       }
7979       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7980       {
7981         if (!MovDelay[x][y])
7982           MovDelay[x][y] = TILEY / 4 + 1;
7983
7984         if (MovDelay[x][y])
7985         {
7986           MovDelay[x][y]--;
7987           if (MovDelay[x][y])
7988             return;
7989         }
7990
7991         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7992         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7993         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7994         Store[x][y] = 0;
7995       }
7996     }
7997     else if (element == EL_BD_MAGIC_WALL_FULL)
7998     {
7999       if (IS_FREE(x, y + 1))
8000       {
8001         InitMovingField(x, y, MV_DOWN);
8002         started_moving = TRUE;
8003
8004         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8005         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8006       }
8007       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8008       {
8009         if (!MovDelay[x][y])
8010           MovDelay[x][y] = TILEY / 4 + 1;
8011
8012         if (MovDelay[x][y])
8013         {
8014           MovDelay[x][y]--;
8015           if (MovDelay[x][y])
8016             return;
8017         }
8018
8019         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8020         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8021         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8022         Store[x][y] = 0;
8023       }
8024     }
8025     else if (element == EL_DC_MAGIC_WALL_FULL)
8026     {
8027       if (IS_FREE(x, y + 1))
8028       {
8029         InitMovingField(x, y, MV_DOWN);
8030         started_moving = TRUE;
8031
8032         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8033         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8034       }
8035       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8036       {
8037         if (!MovDelay[x][y])
8038           MovDelay[x][y] = TILEY / 4 + 1;
8039
8040         if (MovDelay[x][y])
8041         {
8042           MovDelay[x][y]--;
8043           if (MovDelay[x][y])
8044             return;
8045         }
8046
8047         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8048         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8049         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8050         Store[x][y] = 0;
8051       }
8052     }
8053     else if ((CAN_PASS_MAGIC_WALL(element) &&
8054               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8055                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8056              (CAN_PASS_DC_MAGIC_WALL(element) &&
8057               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8058
8059     {
8060       InitMovingField(x, y, MV_DOWN);
8061       started_moving = TRUE;
8062
8063       Tile[x][y] =
8064         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8065          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8066          EL_DC_MAGIC_WALL_FILLING);
8067       Store[x][y] = element;
8068     }
8069     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8070     {
8071       SplashAcid(x, y + 1);
8072
8073       InitMovingField(x, y, MV_DOWN);
8074       started_moving = TRUE;
8075
8076       Store[x][y] = EL_ACID;
8077     }
8078     else if (
8079              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8080               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8081              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8082               CAN_FALL(element) && WasJustFalling[x][y] &&
8083               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8084
8085              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8086               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8087               (Tile[x][y + 1] == EL_BLOCKED)))
8088     {
8089       /* this is needed for a special case not covered by calling "Impact()"
8090          from "ContinueMoving()": if an element moves to a tile directly below
8091          another element which was just falling on that tile (which was empty
8092          in the previous frame), the falling element above would just stop
8093          instead of smashing the element below (in previous version, the above
8094          element was just checked for "moving" instead of "falling", resulting
8095          in incorrect smashes caused by horizontal movement of the above
8096          element; also, the case of the player being the element to smash was
8097          simply not covered here... :-/ ) */
8098
8099       CheckCollision[x][y] = 0;
8100       CheckImpact[x][y] = 0;
8101
8102       Impact(x, y);
8103     }
8104     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8105     {
8106       if (MovDir[x][y] == MV_NONE)
8107       {
8108         InitMovingField(x, y, MV_DOWN);
8109         started_moving = TRUE;
8110       }
8111     }
8112     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8113     {
8114       if (WasJustFalling[x][y]) // prevent animation from being restarted
8115         MovDir[x][y] = MV_DOWN;
8116
8117       InitMovingField(x, y, MV_DOWN);
8118       started_moving = TRUE;
8119     }
8120     else if (element == EL_AMOEBA_DROP)
8121     {
8122       Tile[x][y] = EL_AMOEBA_GROWING;
8123       Store[x][y] = EL_AMOEBA_WET;
8124     }
8125     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8126               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8127              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8128              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8129     {
8130       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8131                                 (IS_FREE(x - 1, y + 1) ||
8132                                  Tile[x - 1][y + 1] == EL_ACID));
8133       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8134                                 (IS_FREE(x + 1, y + 1) ||
8135                                  Tile[x + 1][y + 1] == EL_ACID));
8136       boolean can_fall_any  = (can_fall_left || can_fall_right);
8137       boolean can_fall_both = (can_fall_left && can_fall_right);
8138       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8139
8140       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8141       {
8142         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8143           can_fall_right = FALSE;
8144         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8145           can_fall_left = FALSE;
8146         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8147           can_fall_right = FALSE;
8148         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8149           can_fall_left = FALSE;
8150
8151         can_fall_any  = (can_fall_left || can_fall_right);
8152         can_fall_both = FALSE;
8153       }
8154
8155       if (can_fall_both)
8156       {
8157         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8158           can_fall_right = FALSE;       // slip down on left side
8159         else
8160           can_fall_left = !(can_fall_right = RND(2));
8161
8162         can_fall_both = FALSE;
8163       }
8164
8165       if (can_fall_any)
8166       {
8167         // if not determined otherwise, prefer left side for slipping down
8168         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8169         started_moving = TRUE;
8170       }
8171     }
8172     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8173     {
8174       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8175       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8176       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8177       int belt_dir = game.belt_dir[belt_nr];
8178
8179       if ((belt_dir == MV_LEFT  && left_is_free) ||
8180           (belt_dir == MV_RIGHT && right_is_free))
8181       {
8182         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8183
8184         InitMovingField(x, y, belt_dir);
8185         started_moving = TRUE;
8186
8187         Pushed[x][y] = TRUE;
8188         Pushed[nextx][y] = TRUE;
8189
8190         GfxAction[x][y] = ACTION_DEFAULT;
8191       }
8192       else
8193       {
8194         MovDir[x][y] = 0;       // if element was moving, stop it
8195       }
8196     }
8197   }
8198
8199   // not "else if" because of elements that can fall and move (EL_SPRING)
8200   if (CAN_MOVE(element) && !started_moving)
8201   {
8202     int move_pattern = element_info[element].move_pattern;
8203     int newx, newy;
8204
8205     Moving2Blocked(x, y, &newx, &newy);
8206
8207     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8208       return;
8209
8210     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8211         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8212     {
8213       WasJustMoving[x][y] = 0;
8214       CheckCollision[x][y] = 0;
8215
8216       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8217
8218       if (Tile[x][y] != element)        // element has changed
8219         return;
8220     }
8221
8222     if (!MovDelay[x][y])        // start new movement phase
8223     {
8224       // all objects that can change their move direction after each step
8225       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8226
8227       if (element != EL_YAMYAM &&
8228           element != EL_DARK_YAMYAM &&
8229           element != EL_PACMAN &&
8230           !(move_pattern & MV_ANY_DIRECTION) &&
8231           move_pattern != MV_TURNING_LEFT &&
8232           move_pattern != MV_TURNING_RIGHT &&
8233           move_pattern != MV_TURNING_LEFT_RIGHT &&
8234           move_pattern != MV_TURNING_RIGHT_LEFT &&
8235           move_pattern != MV_TURNING_RANDOM)
8236       {
8237         TurnRound(x, y);
8238
8239         if (MovDelay[x][y] && (element == EL_BUG ||
8240                                element == EL_SPACESHIP ||
8241                                element == EL_SP_SNIKSNAK ||
8242                                element == EL_SP_ELECTRON ||
8243                                element == EL_MOLE))
8244           TEST_DrawLevelField(x, y);
8245       }
8246     }
8247
8248     if (MovDelay[x][y])         // wait some time before next movement
8249     {
8250       MovDelay[x][y]--;
8251
8252       if (element == EL_ROBOT ||
8253           element == EL_YAMYAM ||
8254           element == EL_DARK_YAMYAM)
8255       {
8256         DrawLevelElementAnimationIfNeeded(x, y, element);
8257         PlayLevelSoundAction(x, y, ACTION_WAITING);
8258       }
8259       else if (element == EL_SP_ELECTRON)
8260         DrawLevelElementAnimationIfNeeded(x, y, element);
8261       else if (element == EL_DRAGON)
8262       {
8263         int i;
8264         int dir = MovDir[x][y];
8265         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8266         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8267         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8268                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8269                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8270                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8271         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8272
8273         GfxAction[x][y] = ACTION_ATTACKING;
8274
8275         if (IS_PLAYER(x, y))
8276           DrawPlayerField(x, y);
8277         else
8278           TEST_DrawLevelField(x, y);
8279
8280         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8281
8282         for (i = 1; i <= 3; i++)
8283         {
8284           int xx = x + i * dx;
8285           int yy = y + i * dy;
8286           int sx = SCREENX(xx);
8287           int sy = SCREENY(yy);
8288           int flame_graphic = graphic + (i - 1);
8289
8290           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8291             break;
8292
8293           if (MovDelay[x][y])
8294           {
8295             int flamed = MovingOrBlocked2Element(xx, yy);
8296
8297             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8298               Bang(xx, yy);
8299             else
8300               RemoveMovingField(xx, yy);
8301
8302             ChangeDelay[xx][yy] = 0;
8303
8304             Tile[xx][yy] = EL_FLAMES;
8305
8306             if (IN_SCR_FIELD(sx, sy))
8307             {
8308               TEST_DrawLevelFieldCrumbled(xx, yy);
8309               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8310             }
8311           }
8312           else
8313           {
8314             if (Tile[xx][yy] == EL_FLAMES)
8315               Tile[xx][yy] = EL_EMPTY;
8316             TEST_DrawLevelField(xx, yy);
8317           }
8318         }
8319       }
8320
8321       if (MovDelay[x][y])       // element still has to wait some time
8322       {
8323         PlayLevelSoundAction(x, y, ACTION_WAITING);
8324
8325         return;
8326       }
8327     }
8328
8329     // now make next step
8330
8331     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8332
8333     if (DONT_COLLIDE_WITH(element) &&
8334         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8335         !PLAYER_ENEMY_PROTECTED(newx, newy))
8336     {
8337       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8338
8339       return;
8340     }
8341
8342     else if (CAN_MOVE_INTO_ACID(element) &&
8343              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8344              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8345              (MovDir[x][y] == MV_DOWN ||
8346               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8347     {
8348       SplashAcid(newx, newy);
8349       Store[x][y] = EL_ACID;
8350     }
8351     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8352     {
8353       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8354           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8355           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8356           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8357       {
8358         RemoveField(x, y);
8359         TEST_DrawLevelField(x, y);
8360
8361         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8362         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8363           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8364
8365         game.friends_still_needed--;
8366         if (!game.friends_still_needed &&
8367             !game.GameOver &&
8368             game.all_players_gone)
8369           LevelSolved();
8370
8371         return;
8372       }
8373       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8374       {
8375         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8376           TEST_DrawLevelField(newx, newy);
8377         else
8378           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8379       }
8380       else if (!IS_FREE(newx, newy))
8381       {
8382         GfxAction[x][y] = ACTION_WAITING;
8383
8384         if (IS_PLAYER(x, y))
8385           DrawPlayerField(x, y);
8386         else
8387           TEST_DrawLevelField(x, y);
8388
8389         return;
8390       }
8391     }
8392     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8393     {
8394       if (IS_FOOD_PIG(Tile[newx][newy]))
8395       {
8396         if (IS_MOVING(newx, newy))
8397           RemoveMovingField(newx, newy);
8398         else
8399         {
8400           Tile[newx][newy] = EL_EMPTY;
8401           TEST_DrawLevelField(newx, newy);
8402         }
8403
8404         PlayLevelSound(x, y, SND_PIG_DIGGING);
8405       }
8406       else if (!IS_FREE(newx, newy))
8407       {
8408         if (IS_PLAYER(x, y))
8409           DrawPlayerField(x, y);
8410         else
8411           TEST_DrawLevelField(x, y);
8412
8413         return;
8414       }
8415     }
8416     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8417     {
8418       if (Store[x][y] != EL_EMPTY)
8419       {
8420         boolean can_clone = FALSE;
8421         int xx, yy;
8422
8423         // check if element to clone is still there
8424         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8425         {
8426           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8427           {
8428             can_clone = TRUE;
8429
8430             break;
8431           }
8432         }
8433
8434         // cannot clone or target field not free anymore -- do not clone
8435         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8436           Store[x][y] = EL_EMPTY;
8437       }
8438
8439       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8440       {
8441         if (IS_MV_DIAGONAL(MovDir[x][y]))
8442         {
8443           int diagonal_move_dir = MovDir[x][y];
8444           int stored = Store[x][y];
8445           int change_delay = 8;
8446           int graphic;
8447
8448           // android is moving diagonally
8449
8450           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8451
8452           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8453           GfxElement[x][y] = EL_EMC_ANDROID;
8454           GfxAction[x][y] = ACTION_SHRINKING;
8455           GfxDir[x][y] = diagonal_move_dir;
8456           ChangeDelay[x][y] = change_delay;
8457
8458           if (Store[x][y] == EL_EMPTY)
8459             Store[x][y] = GfxElementEmpty[x][y];
8460
8461           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8462                                    GfxDir[x][y]);
8463
8464           DrawLevelGraphicAnimation(x, y, graphic);
8465           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8466
8467           if (Tile[newx][newy] == EL_ACID)
8468           {
8469             SplashAcid(newx, newy);
8470
8471             return;
8472           }
8473
8474           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8475
8476           Store[newx][newy] = EL_EMC_ANDROID;
8477           GfxElement[newx][newy] = EL_EMC_ANDROID;
8478           GfxAction[newx][newy] = ACTION_GROWING;
8479           GfxDir[newx][newy] = diagonal_move_dir;
8480           ChangeDelay[newx][newy] = change_delay;
8481
8482           graphic = el_act_dir2img(GfxElement[newx][newy],
8483                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8484
8485           DrawLevelGraphicAnimation(newx, newy, graphic);
8486           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8487
8488           return;
8489         }
8490         else
8491         {
8492           Tile[newx][newy] = EL_EMPTY;
8493           TEST_DrawLevelField(newx, newy);
8494
8495           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8496         }
8497       }
8498       else if (!IS_FREE(newx, newy))
8499       {
8500         return;
8501       }
8502     }
8503     else if (IS_CUSTOM_ELEMENT(element) &&
8504              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8505     {
8506       if (!DigFieldByCE(newx, newy, element))
8507         return;
8508
8509       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8510       {
8511         RunnerVisit[x][y] = FrameCounter;
8512         PlayerVisit[x][y] /= 8;         // expire player visit path
8513       }
8514     }
8515     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8516     {
8517       if (!IS_FREE(newx, newy))
8518       {
8519         if (IS_PLAYER(x, y))
8520           DrawPlayerField(x, y);
8521         else
8522           TEST_DrawLevelField(x, y);
8523
8524         return;
8525       }
8526       else
8527       {
8528         boolean wanna_flame = !RND(10);
8529         int dx = newx - x, dy = newy - y;
8530         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8531         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8532         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8533                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8534         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8535                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8536
8537         if ((wanna_flame ||
8538              IS_CLASSIC_ENEMY(element1) ||
8539              IS_CLASSIC_ENEMY(element2)) &&
8540             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8541             element1 != EL_FLAMES && element2 != EL_FLAMES)
8542         {
8543           ResetGfxAnimation(x, y);
8544           GfxAction[x][y] = ACTION_ATTACKING;
8545
8546           if (IS_PLAYER(x, y))
8547             DrawPlayerField(x, y);
8548           else
8549             TEST_DrawLevelField(x, y);
8550
8551           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8552
8553           MovDelay[x][y] = 50;
8554
8555           Tile[newx][newy] = EL_FLAMES;
8556           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8557             Tile[newx1][newy1] = EL_FLAMES;
8558           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8559             Tile[newx2][newy2] = EL_FLAMES;
8560
8561           return;
8562         }
8563       }
8564     }
8565     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8566              Tile[newx][newy] == EL_DIAMOND)
8567     {
8568       if (IS_MOVING(newx, newy))
8569         RemoveMovingField(newx, newy);
8570       else
8571       {
8572         Tile[newx][newy] = EL_EMPTY;
8573         TEST_DrawLevelField(newx, newy);
8574       }
8575
8576       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8577     }
8578     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8579              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8580     {
8581       if (AmoebaNr[newx][newy])
8582       {
8583         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8584         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8585             Tile[newx][newy] == EL_BD_AMOEBA)
8586           AmoebaCnt[AmoebaNr[newx][newy]]--;
8587       }
8588
8589       if (IS_MOVING(newx, newy))
8590       {
8591         RemoveMovingField(newx, newy);
8592       }
8593       else
8594       {
8595         Tile[newx][newy] = EL_EMPTY;
8596         TEST_DrawLevelField(newx, newy);
8597       }
8598
8599       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8600     }
8601     else if ((element == EL_PACMAN || element == EL_MOLE)
8602              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8603     {
8604       if (AmoebaNr[newx][newy])
8605       {
8606         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8607         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8608             Tile[newx][newy] == EL_BD_AMOEBA)
8609           AmoebaCnt[AmoebaNr[newx][newy]]--;
8610       }
8611
8612       if (element == EL_MOLE)
8613       {
8614         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8615         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8616
8617         ResetGfxAnimation(x, y);
8618         GfxAction[x][y] = ACTION_DIGGING;
8619         TEST_DrawLevelField(x, y);
8620
8621         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8622
8623         return;                         // wait for shrinking amoeba
8624       }
8625       else      // element == EL_PACMAN
8626       {
8627         Tile[newx][newy] = EL_EMPTY;
8628         TEST_DrawLevelField(newx, newy);
8629         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8630       }
8631     }
8632     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8633              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8634               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8635     {
8636       // wait for shrinking amoeba to completely disappear
8637       return;
8638     }
8639     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8640     {
8641       // object was running against a wall
8642
8643       TurnRound(x, y);
8644
8645       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8646         DrawLevelElementAnimation(x, y, element);
8647
8648       if (DONT_TOUCH(element))
8649         TestIfBadThingTouchesPlayer(x, y);
8650
8651       return;
8652     }
8653
8654     InitMovingField(x, y, MovDir[x][y]);
8655
8656     PlayLevelSoundAction(x, y, ACTION_MOVING);
8657   }
8658
8659   if (MovDir[x][y])
8660     ContinueMoving(x, y);
8661 }
8662
8663 void ContinueMoving(int x, int y)
8664 {
8665   int element = Tile[x][y];
8666   struct ElementInfo *ei = &element_info[element];
8667   int direction = MovDir[x][y];
8668   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8669   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8670   int newx = x + dx, newy = y + dy;
8671   int stored = Store[x][y];
8672   int stored_new = Store[newx][newy];
8673   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8674   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8675   boolean last_line = (newy == lev_fieldy - 1);
8676   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8677
8678   if (pushed_by_player)         // special case: moving object pushed by player
8679   {
8680     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8681   }
8682   else if (use_step_delay)      // special case: moving object has step delay
8683   {
8684     if (!MovDelay[x][y])
8685       MovPos[x][y] += getElementMoveStepsize(x, y);
8686
8687     if (MovDelay[x][y])
8688       MovDelay[x][y]--;
8689     else
8690       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8691
8692     if (MovDelay[x][y])
8693     {
8694       TEST_DrawLevelField(x, y);
8695
8696       return;   // element is still waiting
8697     }
8698   }
8699   else                          // normal case: generically moving object
8700   {
8701     MovPos[x][y] += getElementMoveStepsize(x, y);
8702   }
8703
8704   if (ABS(MovPos[x][y]) < TILEX)
8705   {
8706     TEST_DrawLevelField(x, y);
8707
8708     return;     // element is still moving
8709   }
8710
8711   // element reached destination field
8712
8713   Tile[x][y] = EL_EMPTY;
8714   Tile[newx][newy] = element;
8715   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8716
8717   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8718   {
8719     element = Tile[newx][newy] = EL_ACID;
8720   }
8721   else if (element == EL_MOLE)
8722   {
8723     Tile[x][y] = EL_SAND;
8724
8725     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8726   }
8727   else if (element == EL_QUICKSAND_FILLING)
8728   {
8729     element = Tile[newx][newy] = get_next_element(element);
8730     Store[newx][newy] = Store[x][y];
8731   }
8732   else if (element == EL_QUICKSAND_EMPTYING)
8733   {
8734     Tile[x][y] = get_next_element(element);
8735     element = Tile[newx][newy] = Store[x][y];
8736   }
8737   else if (element == EL_QUICKSAND_FAST_FILLING)
8738   {
8739     element = Tile[newx][newy] = get_next_element(element);
8740     Store[newx][newy] = Store[x][y];
8741   }
8742   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8743   {
8744     Tile[x][y] = get_next_element(element);
8745     element = Tile[newx][newy] = Store[x][y];
8746   }
8747   else if (element == EL_MAGIC_WALL_FILLING)
8748   {
8749     element = Tile[newx][newy] = get_next_element(element);
8750     if (!game.magic_wall_active)
8751       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8752     Store[newx][newy] = Store[x][y];
8753   }
8754   else if (element == EL_MAGIC_WALL_EMPTYING)
8755   {
8756     Tile[x][y] = get_next_element(element);
8757     if (!game.magic_wall_active)
8758       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8759     element = Tile[newx][newy] = Store[x][y];
8760
8761     InitField(newx, newy, FALSE);
8762   }
8763   else if (element == EL_BD_MAGIC_WALL_FILLING)
8764   {
8765     element = Tile[newx][newy] = get_next_element(element);
8766     if (!game.magic_wall_active)
8767       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8768     Store[newx][newy] = Store[x][y];
8769   }
8770   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8771   {
8772     Tile[x][y] = get_next_element(element);
8773     if (!game.magic_wall_active)
8774       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8775     element = Tile[newx][newy] = Store[x][y];
8776
8777     InitField(newx, newy, FALSE);
8778   }
8779   else if (element == EL_DC_MAGIC_WALL_FILLING)
8780   {
8781     element = Tile[newx][newy] = get_next_element(element);
8782     if (!game.magic_wall_active)
8783       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8784     Store[newx][newy] = Store[x][y];
8785   }
8786   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8787   {
8788     Tile[x][y] = get_next_element(element);
8789     if (!game.magic_wall_active)
8790       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8791     element = Tile[newx][newy] = Store[x][y];
8792
8793     InitField(newx, newy, FALSE);
8794   }
8795   else if (element == EL_AMOEBA_DROPPING)
8796   {
8797     Tile[x][y] = get_next_element(element);
8798     element = Tile[newx][newy] = Store[x][y];
8799   }
8800   else if (element == EL_SOKOBAN_OBJECT)
8801   {
8802     if (Back[x][y])
8803       Tile[x][y] = Back[x][y];
8804
8805     if (Back[newx][newy])
8806       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8807
8808     Back[x][y] = Back[newx][newy] = 0;
8809   }
8810
8811   Store[x][y] = EL_EMPTY;
8812   MovPos[x][y] = 0;
8813   MovDir[x][y] = 0;
8814   MovDelay[x][y] = 0;
8815
8816   MovDelay[newx][newy] = 0;
8817
8818   if (CAN_CHANGE_OR_HAS_ACTION(element))
8819   {
8820     // copy element change control values to new field
8821     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8822     ChangePage[newx][newy]  = ChangePage[x][y];
8823     ChangeCount[newx][newy] = ChangeCount[x][y];
8824     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8825   }
8826
8827   CustomValue[newx][newy] = CustomValue[x][y];
8828
8829   ChangeDelay[x][y] = 0;
8830   ChangePage[x][y] = -1;
8831   ChangeCount[x][y] = 0;
8832   ChangeEvent[x][y] = -1;
8833
8834   CustomValue[x][y] = 0;
8835
8836   // copy animation control values to new field
8837   GfxFrame[newx][newy]  = GfxFrame[x][y];
8838   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8839   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8840   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8841
8842   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8843
8844   // some elements can leave other elements behind after moving
8845   if (ei->move_leave_element != EL_EMPTY &&
8846       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8847       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8848   {
8849     int move_leave_element = ei->move_leave_element;
8850
8851     // this makes it possible to leave the removed element again
8852     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8853       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8854
8855     Tile[x][y] = move_leave_element;
8856
8857     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8858       MovDir[x][y] = direction;
8859
8860     InitField(x, y, FALSE);
8861
8862     if (GFX_CRUMBLED(Tile[x][y]))
8863       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8864
8865     if (IS_PLAYER_ELEMENT(move_leave_element))
8866       RelocatePlayer(x, y, move_leave_element);
8867   }
8868
8869   // do this after checking for left-behind element
8870   ResetGfxAnimation(x, y);      // reset animation values for old field
8871
8872   if (!CAN_MOVE(element) ||
8873       (CAN_FALL(element) && direction == MV_DOWN &&
8874        (element == EL_SPRING ||
8875         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8876         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8877     GfxDir[x][y] = MovDir[newx][newy] = 0;
8878
8879   TEST_DrawLevelField(x, y);
8880   TEST_DrawLevelField(newx, newy);
8881
8882   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8883
8884   // prevent pushed element from moving on in pushed direction
8885   if (pushed_by_player && CAN_MOVE(element) &&
8886       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8887       !(element_info[element].move_pattern & direction))
8888     TurnRound(newx, newy);
8889
8890   // prevent elements on conveyor belt from moving on in last direction
8891   if (pushed_by_conveyor && CAN_FALL(element) &&
8892       direction & MV_HORIZONTAL)
8893     MovDir[newx][newy] = 0;
8894
8895   if (!pushed_by_player)
8896   {
8897     int nextx = newx + dx, nexty = newy + dy;
8898     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8899
8900     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8901
8902     if (CAN_FALL(element) && direction == MV_DOWN)
8903       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8904
8905     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8906       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8907
8908     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8909       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8910   }
8911
8912   if (DONT_TOUCH(element))      // object may be nasty to player or others
8913   {
8914     TestIfBadThingTouchesPlayer(newx, newy);
8915     TestIfBadThingTouchesFriend(newx, newy);
8916
8917     if (!IS_CUSTOM_ELEMENT(element))
8918       TestIfBadThingTouchesOtherBadThing(newx, newy);
8919   }
8920   else if (element == EL_PENGUIN)
8921     TestIfFriendTouchesBadThing(newx, newy);
8922
8923   if (DONT_GET_HIT_BY(element))
8924   {
8925     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8926   }
8927
8928   // give the player one last chance (one more frame) to move away
8929   if (CAN_FALL(element) && direction == MV_DOWN &&
8930       (last_line || (!IS_FREE(x, newy + 1) &&
8931                      (!IS_PLAYER(x, newy + 1) ||
8932                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8933     Impact(x, newy);
8934
8935   if (pushed_by_player && !game.use_change_when_pushing_bug)
8936   {
8937     int push_side = MV_DIR_OPPOSITE(direction);
8938     struct PlayerInfo *player = PLAYERINFO(x, y);
8939
8940     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8941                                player->index_bit, push_side);
8942     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8943                                         player->index_bit, push_side);
8944   }
8945
8946   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8947     MovDelay[newx][newy] = 1;
8948
8949   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8950
8951   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8952   TestIfElementHitsCustomElement(newx, newy, direction);
8953   TestIfPlayerTouchesCustomElement(newx, newy);
8954   TestIfElementTouchesCustomElement(newx, newy);
8955
8956   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8957       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8958     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8959                              MV_DIR_OPPOSITE(direction));
8960 }
8961
8962 int AmoebaNeighbourNr(int ax, int ay)
8963 {
8964   int i;
8965   int element = Tile[ax][ay];
8966   int group_nr = 0;
8967   struct XY *xy = xy_topdown;
8968
8969   for (i = 0; i < NUM_DIRECTIONS; i++)
8970   {
8971     int x = ax + xy[i].x;
8972     int y = ay + xy[i].y;
8973
8974     if (!IN_LEV_FIELD(x, y))
8975       continue;
8976
8977     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8978       group_nr = AmoebaNr[x][y];
8979   }
8980
8981   return group_nr;
8982 }
8983
8984 static void AmoebaMerge(int ax, int ay)
8985 {
8986   int i, x, y, xx, yy;
8987   int new_group_nr = AmoebaNr[ax][ay];
8988   struct XY *xy = xy_topdown;
8989
8990   if (new_group_nr == 0)
8991     return;
8992
8993   for (i = 0; i < NUM_DIRECTIONS; i++)
8994   {
8995     x = ax + xy[i].x;
8996     y = ay + xy[i].y;
8997
8998     if (!IN_LEV_FIELD(x, y))
8999       continue;
9000
9001     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9002          Tile[x][y] == EL_BD_AMOEBA ||
9003          Tile[x][y] == EL_AMOEBA_DEAD) &&
9004         AmoebaNr[x][y] != new_group_nr)
9005     {
9006       int old_group_nr = AmoebaNr[x][y];
9007
9008       if (old_group_nr == 0)
9009         return;
9010
9011       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9012       AmoebaCnt[old_group_nr] = 0;
9013       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9014       AmoebaCnt2[old_group_nr] = 0;
9015
9016       SCAN_PLAYFIELD(xx, yy)
9017       {
9018         if (AmoebaNr[xx][yy] == old_group_nr)
9019           AmoebaNr[xx][yy] = new_group_nr;
9020       }
9021     }
9022   }
9023 }
9024
9025 void AmoebaToDiamond(int ax, int ay)
9026 {
9027   int i, x, y;
9028
9029   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9030   {
9031     int group_nr = AmoebaNr[ax][ay];
9032
9033 #ifdef DEBUG
9034     if (group_nr == 0)
9035     {
9036       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9037       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9038
9039       return;
9040     }
9041 #endif
9042
9043     SCAN_PLAYFIELD(x, y)
9044     {
9045       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9046       {
9047         AmoebaNr[x][y] = 0;
9048         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9049       }
9050     }
9051
9052     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9053                             SND_AMOEBA_TURNING_TO_GEM :
9054                             SND_AMOEBA_TURNING_TO_ROCK));
9055     Bang(ax, ay);
9056   }
9057   else
9058   {
9059     struct XY *xy = xy_topdown;
9060
9061     for (i = 0; i < NUM_DIRECTIONS; i++)
9062     {
9063       x = ax + xy[i].x;
9064       y = ay + xy[i].y;
9065
9066       if (!IN_LEV_FIELD(x, y))
9067         continue;
9068
9069       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9070       {
9071         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9072                               SND_AMOEBA_TURNING_TO_GEM :
9073                               SND_AMOEBA_TURNING_TO_ROCK));
9074         Bang(x, y);
9075       }
9076     }
9077   }
9078 }
9079
9080 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9081 {
9082   int x, y;
9083   int group_nr = AmoebaNr[ax][ay];
9084   boolean done = FALSE;
9085
9086 #ifdef DEBUG
9087   if (group_nr == 0)
9088   {
9089     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9090     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9091
9092     return;
9093   }
9094 #endif
9095
9096   SCAN_PLAYFIELD(x, y)
9097   {
9098     if (AmoebaNr[x][y] == group_nr &&
9099         (Tile[x][y] == EL_AMOEBA_DEAD ||
9100          Tile[x][y] == EL_BD_AMOEBA ||
9101          Tile[x][y] == EL_AMOEBA_GROWING))
9102     {
9103       AmoebaNr[x][y] = 0;
9104       Tile[x][y] = new_element;
9105       InitField(x, y, FALSE);
9106       TEST_DrawLevelField(x, y);
9107       done = TRUE;
9108     }
9109   }
9110
9111   if (done)
9112     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9113                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9114                             SND_BD_AMOEBA_TURNING_TO_GEM));
9115 }
9116
9117 static void AmoebaGrowing(int x, int y)
9118 {
9119   static DelayCounter sound_delay = { 0 };
9120
9121   if (!MovDelay[x][y])          // start new growing cycle
9122   {
9123     MovDelay[x][y] = 7;
9124
9125     if (DelayReached(&sound_delay))
9126     {
9127       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9128       sound_delay.value = 30;
9129     }
9130   }
9131
9132   if (MovDelay[x][y])           // wait some time before growing bigger
9133   {
9134     MovDelay[x][y]--;
9135     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9136     {
9137       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9138                                            6 - MovDelay[x][y]);
9139
9140       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9141     }
9142
9143     if (!MovDelay[x][y])
9144     {
9145       Tile[x][y] = Store[x][y];
9146       Store[x][y] = 0;
9147       TEST_DrawLevelField(x, y);
9148     }
9149   }
9150 }
9151
9152 static void AmoebaShrinking(int x, int y)
9153 {
9154   static DelayCounter sound_delay = { 0 };
9155
9156   if (!MovDelay[x][y])          // start new shrinking cycle
9157   {
9158     MovDelay[x][y] = 7;
9159
9160     if (DelayReached(&sound_delay))
9161       sound_delay.value = 30;
9162   }
9163
9164   if (MovDelay[x][y])           // wait some time before shrinking
9165   {
9166     MovDelay[x][y]--;
9167     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9168     {
9169       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9170                                            6 - MovDelay[x][y]);
9171
9172       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9173     }
9174
9175     if (!MovDelay[x][y])
9176     {
9177       Tile[x][y] = EL_EMPTY;
9178       TEST_DrawLevelField(x, y);
9179
9180       // don't let mole enter this field in this cycle;
9181       // (give priority to objects falling to this field from above)
9182       Stop[x][y] = TRUE;
9183     }
9184   }
9185 }
9186
9187 static void AmoebaReproduce(int ax, int ay)
9188 {
9189   int i;
9190   int element = Tile[ax][ay];
9191   int graphic = el2img(element);
9192   int newax = ax, neway = ay;
9193   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9194   struct XY *xy = xy_topdown;
9195
9196   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9197   {
9198     Tile[ax][ay] = EL_AMOEBA_DEAD;
9199     TEST_DrawLevelField(ax, ay);
9200     return;
9201   }
9202
9203   if (IS_ANIMATED(graphic))
9204     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9205
9206   if (!MovDelay[ax][ay])        // start making new amoeba field
9207     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9208
9209   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9210   {
9211     MovDelay[ax][ay]--;
9212     if (MovDelay[ax][ay])
9213       return;
9214   }
9215
9216   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9217   {
9218     int start = RND(4);
9219     int x = ax + xy[start].x;
9220     int y = ay + xy[start].y;
9221
9222     if (!IN_LEV_FIELD(x, y))
9223       return;
9224
9225     if (IS_FREE(x, y) ||
9226         CAN_GROW_INTO(Tile[x][y]) ||
9227         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9228         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9229     {
9230       newax = x;
9231       neway = y;
9232     }
9233
9234     if (newax == ax && neway == ay)
9235       return;
9236   }
9237   else                          // normal or "filled" (BD style) amoeba
9238   {
9239     int start = RND(4);
9240     boolean waiting_for_player = FALSE;
9241
9242     for (i = 0; i < NUM_DIRECTIONS; i++)
9243     {
9244       int j = (start + i) % 4;
9245       int x = ax + xy[j].x;
9246       int y = ay + xy[j].y;
9247
9248       if (!IN_LEV_FIELD(x, y))
9249         continue;
9250
9251       if (IS_FREE(x, y) ||
9252           CAN_GROW_INTO(Tile[x][y]) ||
9253           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9254           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9255       {
9256         newax = x;
9257         neway = y;
9258         break;
9259       }
9260       else if (IS_PLAYER(x, y))
9261         waiting_for_player = TRUE;
9262     }
9263
9264     if (newax == ax && neway == ay)             // amoeba cannot grow
9265     {
9266       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9267       {
9268         Tile[ax][ay] = EL_AMOEBA_DEAD;
9269         TEST_DrawLevelField(ax, ay);
9270         AmoebaCnt[AmoebaNr[ax][ay]]--;
9271
9272         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9273         {
9274           if (element == EL_AMOEBA_FULL)
9275             AmoebaToDiamond(ax, ay);
9276           else if (element == EL_BD_AMOEBA)
9277             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9278         }
9279       }
9280       return;
9281     }
9282     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9283     {
9284       // amoeba gets larger by growing in some direction
9285
9286       int new_group_nr = AmoebaNr[ax][ay];
9287
9288 #ifdef DEBUG
9289   if (new_group_nr == 0)
9290   {
9291     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9292           newax, neway);
9293     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9294
9295     return;
9296   }
9297 #endif
9298
9299       AmoebaNr[newax][neway] = new_group_nr;
9300       AmoebaCnt[new_group_nr]++;
9301       AmoebaCnt2[new_group_nr]++;
9302
9303       // if amoeba touches other amoeba(s) after growing, unify them
9304       AmoebaMerge(newax, neway);
9305
9306       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9307       {
9308         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9309         return;
9310       }
9311     }
9312   }
9313
9314   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9315       (neway == lev_fieldy - 1 && newax != ax))
9316   {
9317     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9318     Store[newax][neway] = element;
9319   }
9320   else if (neway == ay || element == EL_EMC_DRIPPER)
9321   {
9322     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9323
9324     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9325   }
9326   else
9327   {
9328     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9329     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9330     Store[ax][ay] = EL_AMOEBA_DROP;
9331     ContinueMoving(ax, ay);
9332     return;
9333   }
9334
9335   TEST_DrawLevelField(newax, neway);
9336 }
9337
9338 static void Life(int ax, int ay)
9339 {
9340   int x1, y1, x2, y2;
9341   int life_time = 40;
9342   int element = Tile[ax][ay];
9343   int graphic = el2img(element);
9344   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9345                          level.biomaze);
9346   boolean changed = FALSE;
9347
9348   if (IS_ANIMATED(graphic))
9349     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9350
9351   if (Stop[ax][ay])
9352     return;
9353
9354   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9355     MovDelay[ax][ay] = life_time;
9356
9357   if (MovDelay[ax][ay])         // wait some time before next cycle
9358   {
9359     MovDelay[ax][ay]--;
9360     if (MovDelay[ax][ay])
9361       return;
9362   }
9363
9364   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9365   {
9366     int xx = ax+x1, yy = ay+y1;
9367     int old_element = Tile[xx][yy];
9368     int num_neighbours = 0;
9369
9370     if (!IN_LEV_FIELD(xx, yy))
9371       continue;
9372
9373     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9374     {
9375       int x = xx+x2, y = yy+y2;
9376
9377       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9378         continue;
9379
9380       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9381       boolean is_neighbour = FALSE;
9382
9383       if (level.use_life_bugs)
9384         is_neighbour =
9385           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9386            (IS_FREE(x, y)                             &&  Stop[x][y]));
9387       else
9388         is_neighbour =
9389           (Last[x][y] == element || is_player_cell);
9390
9391       if (is_neighbour)
9392         num_neighbours++;
9393     }
9394
9395     boolean is_free = FALSE;
9396
9397     if (level.use_life_bugs)
9398       is_free = (IS_FREE(xx, yy));
9399     else
9400       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9401
9402     if (xx == ax && yy == ay)           // field in the middle
9403     {
9404       if (num_neighbours < life_parameter[0] ||
9405           num_neighbours > life_parameter[1])
9406       {
9407         Tile[xx][yy] = EL_EMPTY;
9408         if (Tile[xx][yy] != old_element)
9409           TEST_DrawLevelField(xx, yy);
9410         Stop[xx][yy] = TRUE;
9411         changed = TRUE;
9412       }
9413     }
9414     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9415     {                                   // free border field
9416       if (num_neighbours >= life_parameter[2] &&
9417           num_neighbours <= life_parameter[3])
9418       {
9419         Tile[xx][yy] = element;
9420         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9421         if (Tile[xx][yy] != old_element)
9422           TEST_DrawLevelField(xx, yy);
9423         Stop[xx][yy] = TRUE;
9424         changed = TRUE;
9425       }
9426     }
9427   }
9428
9429   if (changed)
9430     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9431                    SND_GAME_OF_LIFE_GROWING);
9432 }
9433
9434 static void InitRobotWheel(int x, int y)
9435 {
9436   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9437 }
9438
9439 static void RunRobotWheel(int x, int y)
9440 {
9441   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9442 }
9443
9444 static void StopRobotWheel(int x, int y)
9445 {
9446   if (game.robot_wheel_x == x &&
9447       game.robot_wheel_y == y)
9448   {
9449     game.robot_wheel_x = -1;
9450     game.robot_wheel_y = -1;
9451     game.robot_wheel_active = FALSE;
9452   }
9453 }
9454
9455 static void InitTimegateWheel(int x, int y)
9456 {
9457   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9458 }
9459
9460 static void RunTimegateWheel(int x, int y)
9461 {
9462   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9463 }
9464
9465 static void InitMagicBallDelay(int x, int y)
9466 {
9467   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9468 }
9469
9470 static void ActivateMagicBall(int bx, int by)
9471 {
9472   int x, y;
9473
9474   if (level.ball_random)
9475   {
9476     int pos_border = RND(8);    // select one of the eight border elements
9477     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9478     int xx = pos_content % 3;
9479     int yy = pos_content / 3;
9480
9481     x = bx - 1 + xx;
9482     y = by - 1 + yy;
9483
9484     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9485       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9486   }
9487   else
9488   {
9489     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9490     {
9491       int xx = x - bx + 1;
9492       int yy = y - by + 1;
9493
9494       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9495         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9496     }
9497   }
9498
9499   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9500 }
9501
9502 static void CheckExit(int x, int y)
9503 {
9504   if (game.gems_still_needed > 0 ||
9505       game.sokoban_fields_still_needed > 0 ||
9506       game.sokoban_objects_still_needed > 0 ||
9507       game.lights_still_needed > 0)
9508   {
9509     int element = Tile[x][y];
9510     int graphic = el2img(element);
9511
9512     if (IS_ANIMATED(graphic))
9513       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9514
9515     return;
9516   }
9517
9518   // do not re-open exit door closed after last player
9519   if (game.all_players_gone)
9520     return;
9521
9522   Tile[x][y] = EL_EXIT_OPENING;
9523
9524   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9525 }
9526
9527 static void CheckExitEM(int x, int y)
9528 {
9529   if (game.gems_still_needed > 0 ||
9530       game.sokoban_fields_still_needed > 0 ||
9531       game.sokoban_objects_still_needed > 0 ||
9532       game.lights_still_needed > 0)
9533   {
9534     int element = Tile[x][y];
9535     int graphic = el2img(element);
9536
9537     if (IS_ANIMATED(graphic))
9538       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9539
9540     return;
9541   }
9542
9543   // do not re-open exit door closed after last player
9544   if (game.all_players_gone)
9545     return;
9546
9547   Tile[x][y] = EL_EM_EXIT_OPENING;
9548
9549   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9550 }
9551
9552 static void CheckExitSteel(int x, int y)
9553 {
9554   if (game.gems_still_needed > 0 ||
9555       game.sokoban_fields_still_needed > 0 ||
9556       game.sokoban_objects_still_needed > 0 ||
9557       game.lights_still_needed > 0)
9558   {
9559     int element = Tile[x][y];
9560     int graphic = el2img(element);
9561
9562     if (IS_ANIMATED(graphic))
9563       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9564
9565     return;
9566   }
9567
9568   // do not re-open exit door closed after last player
9569   if (game.all_players_gone)
9570     return;
9571
9572   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9573
9574   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9575 }
9576
9577 static void CheckExitSteelEM(int x, int y)
9578 {
9579   if (game.gems_still_needed > 0 ||
9580       game.sokoban_fields_still_needed > 0 ||
9581       game.sokoban_objects_still_needed > 0 ||
9582       game.lights_still_needed > 0)
9583   {
9584     int element = Tile[x][y];
9585     int graphic = el2img(element);
9586
9587     if (IS_ANIMATED(graphic))
9588       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9589
9590     return;
9591   }
9592
9593   // do not re-open exit door closed after last player
9594   if (game.all_players_gone)
9595     return;
9596
9597   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9598
9599   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9600 }
9601
9602 static void CheckExitSP(int x, int y)
9603 {
9604   if (game.gems_still_needed > 0)
9605   {
9606     int element = Tile[x][y];
9607     int graphic = el2img(element);
9608
9609     if (IS_ANIMATED(graphic))
9610       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9611
9612     return;
9613   }
9614
9615   // do not re-open exit door closed after last player
9616   if (game.all_players_gone)
9617     return;
9618
9619   Tile[x][y] = EL_SP_EXIT_OPENING;
9620
9621   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9622 }
9623
9624 static void CloseAllOpenTimegates(void)
9625 {
9626   int x, y;
9627
9628   SCAN_PLAYFIELD(x, y)
9629   {
9630     int element = Tile[x][y];
9631
9632     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9633     {
9634       Tile[x][y] = EL_TIMEGATE_CLOSING;
9635
9636       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9637     }
9638   }
9639 }
9640
9641 static void DrawTwinkleOnField(int x, int y)
9642 {
9643   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9644     return;
9645
9646   if (Tile[x][y] == EL_BD_DIAMOND)
9647     return;
9648
9649   if (MovDelay[x][y] == 0)      // next animation frame
9650     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9651
9652   if (MovDelay[x][y] != 0)      // wait some time before next frame
9653   {
9654     MovDelay[x][y]--;
9655
9656     DrawLevelElementAnimation(x, y, Tile[x][y]);
9657
9658     if (MovDelay[x][y] != 0)
9659     {
9660       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9661                                            10 - MovDelay[x][y]);
9662
9663       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9664     }
9665   }
9666 }
9667
9668 static void WallGrowing(int x, int y)
9669 {
9670   int delay = 6;
9671
9672   if (!MovDelay[x][y])          // next animation frame
9673     MovDelay[x][y] = 3 * delay;
9674
9675   if (MovDelay[x][y])           // wait some time before next frame
9676   {
9677     MovDelay[x][y]--;
9678
9679     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9680     {
9681       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9682       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9683
9684       DrawLevelGraphic(x, y, graphic, frame);
9685     }
9686
9687     if (!MovDelay[x][y])
9688     {
9689       if (MovDir[x][y] == MV_LEFT)
9690       {
9691         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9692           TEST_DrawLevelField(x - 1, y);
9693       }
9694       else if (MovDir[x][y] == MV_RIGHT)
9695       {
9696         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9697           TEST_DrawLevelField(x + 1, y);
9698       }
9699       else if (MovDir[x][y] == MV_UP)
9700       {
9701         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9702           TEST_DrawLevelField(x, y - 1);
9703       }
9704       else
9705       {
9706         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9707           TEST_DrawLevelField(x, y + 1);
9708       }
9709
9710       Tile[x][y] = Store[x][y];
9711       Store[x][y] = 0;
9712       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9713       TEST_DrawLevelField(x, y);
9714     }
9715   }
9716 }
9717
9718 static void CheckWallGrowing(int ax, int ay)
9719 {
9720   int element = Tile[ax][ay];
9721   int graphic = el2img(element);
9722   boolean free_top    = FALSE;
9723   boolean free_bottom = FALSE;
9724   boolean free_left   = FALSE;
9725   boolean free_right  = FALSE;
9726   boolean stop_top    = FALSE;
9727   boolean stop_bottom = FALSE;
9728   boolean stop_left   = FALSE;
9729   boolean stop_right  = FALSE;
9730   boolean new_wall    = FALSE;
9731
9732   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9733                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9734                            element == EL_EXPANDABLE_STEELWALL_ANY);
9735
9736   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9737                              element == EL_EXPANDABLE_WALL_ANY ||
9738                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9739                              element == EL_EXPANDABLE_STEELWALL_ANY);
9740
9741   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9742                              element == EL_EXPANDABLE_WALL_ANY ||
9743                              element == EL_EXPANDABLE_WALL ||
9744                              element == EL_BD_EXPANDABLE_WALL ||
9745                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9746                              element == EL_EXPANDABLE_STEELWALL_ANY);
9747
9748   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9749                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9750
9751   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9752                              element == EL_EXPANDABLE_WALL ||
9753                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9754
9755   int wall_growing = (is_steelwall ?
9756                       EL_EXPANDABLE_STEELWALL_GROWING :
9757                       EL_EXPANDABLE_WALL_GROWING);
9758
9759   int gfx_wall_growing_up    = (is_steelwall ?
9760                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9761                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9762   int gfx_wall_growing_down  = (is_steelwall ?
9763                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9764                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9765   int gfx_wall_growing_left  = (is_steelwall ?
9766                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9767                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9768   int gfx_wall_growing_right = (is_steelwall ?
9769                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9770                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9771
9772   if (IS_ANIMATED(graphic))
9773     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9774
9775   if (!MovDelay[ax][ay])        // start building new wall
9776     MovDelay[ax][ay] = 6;
9777
9778   if (MovDelay[ax][ay])         // wait some time before building new wall
9779   {
9780     MovDelay[ax][ay]--;
9781     if (MovDelay[ax][ay])
9782       return;
9783   }
9784
9785   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9786     free_top = TRUE;
9787   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9788     free_bottom = TRUE;
9789   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9790     free_left = TRUE;
9791   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9792     free_right = TRUE;
9793
9794   if (grow_vertical)
9795   {
9796     if (free_top)
9797     {
9798       Tile[ax][ay - 1] = wall_growing;
9799       Store[ax][ay - 1] = element;
9800       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9801
9802       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9803         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9804
9805       new_wall = TRUE;
9806     }
9807
9808     if (free_bottom)
9809     {
9810       Tile[ax][ay + 1] = wall_growing;
9811       Store[ax][ay + 1] = element;
9812       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9813
9814       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9815         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9816
9817       new_wall = TRUE;
9818     }
9819   }
9820
9821   if (grow_horizontal)
9822   {
9823     if (free_left)
9824     {
9825       Tile[ax - 1][ay] = wall_growing;
9826       Store[ax - 1][ay] = element;
9827       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9828
9829       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9830         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9831
9832       new_wall = TRUE;
9833     }
9834
9835     if (free_right)
9836     {
9837       Tile[ax + 1][ay] = wall_growing;
9838       Store[ax + 1][ay] = element;
9839       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9840
9841       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9842         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9843
9844       new_wall = TRUE;
9845     }
9846   }
9847
9848   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9849     TEST_DrawLevelField(ax, ay);
9850
9851   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9852     stop_top = TRUE;
9853   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9854     stop_bottom = TRUE;
9855   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9856     stop_left = TRUE;
9857   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9858     stop_right = TRUE;
9859
9860   if (((stop_top && stop_bottom) || stop_horizontal) &&
9861       ((stop_left && stop_right) || stop_vertical))
9862     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9863
9864   if (new_wall)
9865     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9866 }
9867
9868 static void CheckForDragon(int x, int y)
9869 {
9870   int i, j;
9871   boolean dragon_found = FALSE;
9872   struct XY *xy = xy_topdown;
9873
9874   for (i = 0; i < NUM_DIRECTIONS; i++)
9875   {
9876     for (j = 0; j < 4; j++)
9877     {
9878       int xx = x + j * xy[i].x;
9879       int yy = y + j * xy[i].y;
9880
9881       if (IN_LEV_FIELD(xx, yy) &&
9882           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9883       {
9884         if (Tile[xx][yy] == EL_DRAGON)
9885           dragon_found = TRUE;
9886       }
9887       else
9888         break;
9889     }
9890   }
9891
9892   if (!dragon_found)
9893   {
9894     for (i = 0; i < NUM_DIRECTIONS; i++)
9895     {
9896       for (j = 0; j < 3; j++)
9897       {
9898         int xx = x + j * xy[i].x;
9899         int yy = y + j * xy[i].y;
9900
9901         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9902         {
9903           Tile[xx][yy] = EL_EMPTY;
9904           TEST_DrawLevelField(xx, yy);
9905         }
9906         else
9907           break;
9908       }
9909     }
9910   }
9911 }
9912
9913 static void InitBuggyBase(int x, int y)
9914 {
9915   int element = Tile[x][y];
9916   int activating_delay = FRAMES_PER_SECOND / 4;
9917
9918   ChangeDelay[x][y] =
9919     (element == EL_SP_BUGGY_BASE ?
9920      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9921      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9922      activating_delay :
9923      element == EL_SP_BUGGY_BASE_ACTIVE ?
9924      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9925 }
9926
9927 static void WarnBuggyBase(int x, int y)
9928 {
9929   int i;
9930   struct XY *xy = xy_topdown;
9931
9932   for (i = 0; i < NUM_DIRECTIONS; i++)
9933   {
9934     int xx = x + xy[i].x;
9935     int yy = y + xy[i].y;
9936
9937     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9938     {
9939       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9940
9941       break;
9942     }
9943   }
9944 }
9945
9946 static void InitTrap(int x, int y)
9947 {
9948   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9949 }
9950
9951 static void ActivateTrap(int x, int y)
9952 {
9953   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9954 }
9955
9956 static void ChangeActiveTrap(int x, int y)
9957 {
9958   int graphic = IMG_TRAP_ACTIVE;
9959
9960   // if new animation frame was drawn, correct crumbled sand border
9961   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9962     TEST_DrawLevelFieldCrumbled(x, y);
9963 }
9964
9965 static int getSpecialActionElement(int element, int number, int base_element)
9966 {
9967   return (element != EL_EMPTY ? element :
9968           number != -1 ? base_element + number - 1 :
9969           EL_EMPTY);
9970 }
9971
9972 static int getModifiedActionNumber(int value_old, int operator, int operand,
9973                                    int value_min, int value_max)
9974 {
9975   int value_new = (operator == CA_MODE_SET      ? operand :
9976                    operator == CA_MODE_ADD      ? value_old + operand :
9977                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9978                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9979                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9980                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9981                    value_old);
9982
9983   return (value_new < value_min ? value_min :
9984           value_new > value_max ? value_max :
9985           value_new);
9986 }
9987
9988 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9989 {
9990   struct ElementInfo *ei = &element_info[element];
9991   struct ElementChangeInfo *change = &ei->change_page[page];
9992   int target_element = change->target_element;
9993   int action_type = change->action_type;
9994   int action_mode = change->action_mode;
9995   int action_arg = change->action_arg;
9996   int action_element = change->action_element;
9997   int i;
9998
9999   if (!change->has_action)
10000     return;
10001
10002   // ---------- determine action paramater values -----------------------------
10003
10004   int level_time_value =
10005     (level.time > 0 ? TimeLeft :
10006      TimePlayed);
10007
10008   int action_arg_element_raw =
10009     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10010      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10011      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10012      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10013      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10014      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10015      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10016      EL_EMPTY);
10017   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10018
10019   int action_arg_direction =
10020     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10021      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10022      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10023      change->actual_trigger_side :
10024      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10025      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10026      MV_NONE);
10027
10028   int action_arg_number_min =
10029     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10030      CA_ARG_MIN);
10031
10032   int action_arg_number_max =
10033     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10034      action_type == CA_SET_LEVEL_GEMS ? 999 :
10035      action_type == CA_SET_LEVEL_TIME ? 9999 :
10036      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10037      action_type == CA_SET_CE_VALUE ? 9999 :
10038      action_type == CA_SET_CE_SCORE ? 9999 :
10039      CA_ARG_MAX);
10040
10041   int action_arg_number_reset =
10042     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10043      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10044      action_type == CA_SET_LEVEL_TIME ? level.time :
10045      action_type == CA_SET_LEVEL_SCORE ? 0 :
10046      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10047      action_type == CA_SET_CE_SCORE ? 0 :
10048      0);
10049
10050   int action_arg_number =
10051     (action_arg <= CA_ARG_MAX ? action_arg :
10052      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10053      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10054      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10055      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10056      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10057      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10058      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10059      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10060      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10061      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10062      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10063      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10064      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10065      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10066      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10067      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10068      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10069      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10070      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10071      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10072      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10073      -1);
10074
10075   int action_arg_number_old =
10076     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10077      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10078      action_type == CA_SET_LEVEL_SCORE ? game.score :
10079      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10080      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10081      0);
10082
10083   int action_arg_number_new =
10084     getModifiedActionNumber(action_arg_number_old,
10085                             action_mode, action_arg_number,
10086                             action_arg_number_min, action_arg_number_max);
10087
10088   int trigger_player_bits =
10089     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10090      change->actual_trigger_player_bits : change->trigger_player);
10091
10092   int action_arg_player_bits =
10093     (action_arg >= CA_ARG_PLAYER_1 &&
10094      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10095      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10096      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10097      PLAYER_BITS_ANY);
10098
10099   // ---------- execute action  -----------------------------------------------
10100
10101   switch (action_type)
10102   {
10103     case CA_NO_ACTION:
10104     {
10105       return;
10106     }
10107
10108     // ---------- level actions  ----------------------------------------------
10109
10110     case CA_RESTART_LEVEL:
10111     {
10112       game.restart_level = TRUE;
10113
10114       break;
10115     }
10116
10117     case CA_SHOW_ENVELOPE:
10118     {
10119       int element = getSpecialActionElement(action_arg_element,
10120                                             action_arg_number, EL_ENVELOPE_1);
10121
10122       if (IS_ENVELOPE(element))
10123         local_player->show_envelope = element;
10124
10125       break;
10126     }
10127
10128     case CA_SET_LEVEL_TIME:
10129     {
10130       if (level.time > 0)       // only modify limited time value
10131       {
10132         TimeLeft = action_arg_number_new;
10133
10134         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10135
10136         DisplayGameControlValues();
10137
10138         if (!TimeLeft && game.time_limit)
10139           for (i = 0; i < MAX_PLAYERS; i++)
10140             KillPlayer(&stored_player[i]);
10141       }
10142
10143       break;
10144     }
10145
10146     case CA_SET_LEVEL_SCORE:
10147     {
10148       game.score = action_arg_number_new;
10149
10150       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10151
10152       DisplayGameControlValues();
10153
10154       break;
10155     }
10156
10157     case CA_SET_LEVEL_GEMS:
10158     {
10159       game.gems_still_needed = action_arg_number_new;
10160
10161       game.snapshot.collected_item = TRUE;
10162
10163       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10164
10165       DisplayGameControlValues();
10166
10167       break;
10168     }
10169
10170     case CA_SET_LEVEL_WIND:
10171     {
10172       game.wind_direction = action_arg_direction;
10173
10174       break;
10175     }
10176
10177     case CA_SET_LEVEL_RANDOM_SEED:
10178     {
10179       // ensure that setting a new random seed while playing is predictable
10180       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10181
10182       break;
10183     }
10184
10185     // ---------- player actions  ---------------------------------------------
10186
10187     case CA_MOVE_PLAYER:
10188     case CA_MOVE_PLAYER_NEW:
10189     {
10190       // automatically move to the next field in specified direction
10191       for (i = 0; i < MAX_PLAYERS; i++)
10192         if (trigger_player_bits & (1 << i))
10193           if (action_type == CA_MOVE_PLAYER ||
10194               stored_player[i].MovPos == 0)
10195             stored_player[i].programmed_action = action_arg_direction;
10196
10197       break;
10198     }
10199
10200     case CA_EXIT_PLAYER:
10201     {
10202       for (i = 0; i < MAX_PLAYERS; i++)
10203         if (action_arg_player_bits & (1 << i))
10204           ExitPlayer(&stored_player[i]);
10205
10206       if (game.players_still_needed == 0)
10207         LevelSolved();
10208
10209       break;
10210     }
10211
10212     case CA_KILL_PLAYER:
10213     {
10214       for (i = 0; i < MAX_PLAYERS; i++)
10215         if (action_arg_player_bits & (1 << i))
10216           KillPlayer(&stored_player[i]);
10217
10218       break;
10219     }
10220
10221     case CA_SET_PLAYER_KEYS:
10222     {
10223       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10224       int element = getSpecialActionElement(action_arg_element,
10225                                             action_arg_number, EL_KEY_1);
10226
10227       if (IS_KEY(element))
10228       {
10229         for (i = 0; i < MAX_PLAYERS; i++)
10230         {
10231           if (trigger_player_bits & (1 << i))
10232           {
10233             stored_player[i].key[KEY_NR(element)] = key_state;
10234
10235             DrawGameDoorValues();
10236           }
10237         }
10238       }
10239
10240       break;
10241     }
10242
10243     case CA_SET_PLAYER_SPEED:
10244     {
10245       for (i = 0; i < MAX_PLAYERS; i++)
10246       {
10247         if (trigger_player_bits & (1 << i))
10248         {
10249           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10250
10251           if (action_arg == CA_ARG_SPEED_FASTER &&
10252               stored_player[i].cannot_move)
10253           {
10254             action_arg_number = STEPSIZE_VERY_SLOW;
10255           }
10256           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10257                    action_arg == CA_ARG_SPEED_FASTER)
10258           {
10259             action_arg_number = 2;
10260             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10261                            CA_MODE_MULTIPLY);
10262           }
10263           else if (action_arg == CA_ARG_NUMBER_RESET)
10264           {
10265             action_arg_number = level.initial_player_stepsize[i];
10266           }
10267
10268           move_stepsize =
10269             getModifiedActionNumber(move_stepsize,
10270                                     action_mode,
10271                                     action_arg_number,
10272                                     action_arg_number_min,
10273                                     action_arg_number_max);
10274
10275           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10276         }
10277       }
10278
10279       break;
10280     }
10281
10282     case CA_SET_PLAYER_SHIELD:
10283     {
10284       for (i = 0; i < MAX_PLAYERS; i++)
10285       {
10286         if (trigger_player_bits & (1 << i))
10287         {
10288           if (action_arg == CA_ARG_SHIELD_OFF)
10289           {
10290             stored_player[i].shield_normal_time_left = 0;
10291             stored_player[i].shield_deadly_time_left = 0;
10292           }
10293           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10294           {
10295             stored_player[i].shield_normal_time_left = 999999;
10296           }
10297           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10298           {
10299             stored_player[i].shield_normal_time_left = 999999;
10300             stored_player[i].shield_deadly_time_left = 999999;
10301           }
10302         }
10303       }
10304
10305       break;
10306     }
10307
10308     case CA_SET_PLAYER_GRAVITY:
10309     {
10310       for (i = 0; i < MAX_PLAYERS; i++)
10311       {
10312         if (trigger_player_bits & (1 << i))
10313         {
10314           stored_player[i].gravity =
10315             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10316              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10317              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10318              stored_player[i].gravity);
10319         }
10320       }
10321
10322       break;
10323     }
10324
10325     case CA_SET_PLAYER_ARTWORK:
10326     {
10327       for (i = 0; i < MAX_PLAYERS; i++)
10328       {
10329         if (trigger_player_bits & (1 << i))
10330         {
10331           int artwork_element = action_arg_element;
10332
10333           if (action_arg == CA_ARG_ELEMENT_RESET)
10334             artwork_element =
10335               (level.use_artwork_element[i] ? level.artwork_element[i] :
10336                stored_player[i].element_nr);
10337
10338           if (stored_player[i].artwork_element != artwork_element)
10339             stored_player[i].Frame = 0;
10340
10341           stored_player[i].artwork_element = artwork_element;
10342
10343           SetPlayerWaiting(&stored_player[i], FALSE);
10344
10345           // set number of special actions for bored and sleeping animation
10346           stored_player[i].num_special_action_bored =
10347             get_num_special_action(artwork_element,
10348                                    ACTION_BORING_1, ACTION_BORING_LAST);
10349           stored_player[i].num_special_action_sleeping =
10350             get_num_special_action(artwork_element,
10351                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10352         }
10353       }
10354
10355       break;
10356     }
10357
10358     case CA_SET_PLAYER_INVENTORY:
10359     {
10360       for (i = 0; i < MAX_PLAYERS; i++)
10361       {
10362         struct PlayerInfo *player = &stored_player[i];
10363         int j, k;
10364
10365         if (trigger_player_bits & (1 << i))
10366         {
10367           int inventory_element = action_arg_element;
10368
10369           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10370               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10371               action_arg == CA_ARG_ELEMENT_ACTION)
10372           {
10373             int element = inventory_element;
10374             int collect_count = element_info[element].collect_count_initial;
10375
10376             if (!IS_CUSTOM_ELEMENT(element))
10377               collect_count = 1;
10378
10379             if (collect_count == 0)
10380               player->inventory_infinite_element = element;
10381             else
10382               for (k = 0; k < collect_count; k++)
10383                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10384                   player->inventory_element[player->inventory_size++] =
10385                     element;
10386           }
10387           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10388                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10389                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10390           {
10391             if (player->inventory_infinite_element != EL_UNDEFINED &&
10392                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10393                                      action_arg_element_raw))
10394               player->inventory_infinite_element = EL_UNDEFINED;
10395
10396             for (k = 0, j = 0; j < player->inventory_size; j++)
10397             {
10398               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10399                                         action_arg_element_raw))
10400                 player->inventory_element[k++] = player->inventory_element[j];
10401             }
10402
10403             player->inventory_size = k;
10404           }
10405           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10406           {
10407             if (player->inventory_size > 0)
10408             {
10409               for (j = 0; j < player->inventory_size - 1; j++)
10410                 player->inventory_element[j] = player->inventory_element[j + 1];
10411
10412               player->inventory_size--;
10413             }
10414           }
10415           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10416           {
10417             if (player->inventory_size > 0)
10418               player->inventory_size--;
10419           }
10420           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10421           {
10422             player->inventory_infinite_element = EL_UNDEFINED;
10423             player->inventory_size = 0;
10424           }
10425           else if (action_arg == CA_ARG_INVENTORY_RESET)
10426           {
10427             player->inventory_infinite_element = EL_UNDEFINED;
10428             player->inventory_size = 0;
10429
10430             if (level.use_initial_inventory[i])
10431             {
10432               for (j = 0; j < level.initial_inventory_size[i]; j++)
10433               {
10434                 int element = level.initial_inventory_content[i][j];
10435                 int collect_count = element_info[element].collect_count_initial;
10436
10437                 if (!IS_CUSTOM_ELEMENT(element))
10438                   collect_count = 1;
10439
10440                 if (collect_count == 0)
10441                   player->inventory_infinite_element = element;
10442                 else
10443                   for (k = 0; k < collect_count; k++)
10444                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10445                       player->inventory_element[player->inventory_size++] =
10446                         element;
10447               }
10448             }
10449           }
10450         }
10451       }
10452
10453       break;
10454     }
10455
10456     // ---------- CE actions  -------------------------------------------------
10457
10458     case CA_SET_CE_VALUE:
10459     {
10460       int last_ce_value = CustomValue[x][y];
10461
10462       CustomValue[x][y] = action_arg_number_new;
10463
10464       if (CustomValue[x][y] != last_ce_value)
10465       {
10466         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10467         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10468
10469         if (CustomValue[x][y] == 0)
10470         {
10471           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10472           ChangeCount[x][y] = 0;        // allow at least one more change
10473
10474           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10475           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10476         }
10477       }
10478
10479       break;
10480     }
10481
10482     case CA_SET_CE_SCORE:
10483     {
10484       int last_ce_score = ei->collect_score;
10485
10486       ei->collect_score = action_arg_number_new;
10487
10488       if (ei->collect_score != last_ce_score)
10489       {
10490         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10491         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10492
10493         if (ei->collect_score == 0)
10494         {
10495           int xx, yy;
10496
10497           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10498           ChangeCount[x][y] = 0;        // allow at least one more change
10499
10500           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10501           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10502
10503           /*
10504             This is a very special case that seems to be a mixture between
10505             CheckElementChange() and CheckTriggeredElementChange(): while
10506             the first one only affects single elements that are triggered
10507             directly, the second one affects multiple elements in the playfield
10508             that are triggered indirectly by another element. This is a third
10509             case: Changing the CE score always affects multiple identical CEs,
10510             so every affected CE must be checked, not only the single CE for
10511             which the CE score was changed in the first place (as every instance
10512             of that CE shares the same CE score, and therefore also can change)!
10513           */
10514           SCAN_PLAYFIELD(xx, yy)
10515           {
10516             if (Tile[xx][yy] == element)
10517               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10518                                  CE_SCORE_GETS_ZERO);
10519           }
10520         }
10521       }
10522
10523       break;
10524     }
10525
10526     case CA_SET_CE_ARTWORK:
10527     {
10528       int artwork_element = action_arg_element;
10529       boolean reset_frame = FALSE;
10530       int xx, yy;
10531
10532       if (action_arg == CA_ARG_ELEMENT_RESET)
10533         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10534                            element);
10535
10536       if (ei->gfx_element != artwork_element)
10537         reset_frame = TRUE;
10538
10539       ei->gfx_element = artwork_element;
10540
10541       SCAN_PLAYFIELD(xx, yy)
10542       {
10543         if (Tile[xx][yy] == element)
10544         {
10545           if (reset_frame)
10546           {
10547             ResetGfxAnimation(xx, yy);
10548             ResetRandomAnimationValue(xx, yy);
10549           }
10550
10551           TEST_DrawLevelField(xx, yy);
10552         }
10553       }
10554
10555       break;
10556     }
10557
10558     // ---------- engine actions  ---------------------------------------------
10559
10560     case CA_SET_ENGINE_SCAN_MODE:
10561     {
10562       InitPlayfieldScanMode(action_arg);
10563
10564       break;
10565     }
10566
10567     default:
10568       break;
10569   }
10570 }
10571
10572 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10573 {
10574   int old_element = Tile[x][y];
10575   int new_element = GetElementFromGroupElement(element);
10576   int previous_move_direction = MovDir[x][y];
10577   int last_ce_value = CustomValue[x][y];
10578   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10579   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10580   boolean add_player_onto_element = (new_element_is_player &&
10581                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10582                                      IS_WALKABLE(old_element));
10583
10584   if (!add_player_onto_element)
10585   {
10586     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10587       RemoveMovingField(x, y);
10588     else
10589       RemoveField(x, y);
10590
10591     Tile[x][y] = new_element;
10592
10593     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10594       MovDir[x][y] = previous_move_direction;
10595
10596     if (element_info[new_element].use_last_ce_value)
10597       CustomValue[x][y] = last_ce_value;
10598
10599     InitField_WithBug1(x, y, FALSE);
10600
10601     new_element = Tile[x][y];   // element may have changed
10602
10603     ResetGfxAnimation(x, y);
10604     ResetRandomAnimationValue(x, y);
10605
10606     TEST_DrawLevelField(x, y);
10607
10608     if (GFX_CRUMBLED(new_element))
10609       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10610   }
10611
10612   // check if element under the player changes from accessible to unaccessible
10613   // (needed for special case of dropping element which then changes)
10614   // (must be checked after creating new element for walkable group elements)
10615   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10616       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10617   {
10618     Bang(x, y);
10619
10620     return;
10621   }
10622
10623   // "ChangeCount" not set yet to allow "entered by player" change one time
10624   if (new_element_is_player)
10625     RelocatePlayer(x, y, new_element);
10626
10627   if (is_change)
10628     ChangeCount[x][y]++;        // count number of changes in the same frame
10629
10630   TestIfBadThingTouchesPlayer(x, y);
10631   TestIfPlayerTouchesCustomElement(x, y);
10632   TestIfElementTouchesCustomElement(x, y);
10633 }
10634
10635 static void CreateField(int x, int y, int element)
10636 {
10637   CreateFieldExt(x, y, element, FALSE);
10638 }
10639
10640 static void CreateElementFromChange(int x, int y, int element)
10641 {
10642   element = GET_VALID_RUNTIME_ELEMENT(element);
10643
10644   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10645   {
10646     int old_element = Tile[x][y];
10647
10648     // prevent changed element from moving in same engine frame
10649     // unless both old and new element can either fall or move
10650     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10651         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10652       Stop[x][y] = TRUE;
10653   }
10654
10655   CreateFieldExt(x, y, element, TRUE);
10656 }
10657
10658 static boolean ChangeElement(int x, int y, int element, int page)
10659 {
10660   struct ElementInfo *ei = &element_info[element];
10661   struct ElementChangeInfo *change = &ei->change_page[page];
10662   int ce_value = CustomValue[x][y];
10663   int ce_score = ei->collect_score;
10664   int target_element;
10665   int old_element = Tile[x][y];
10666
10667   // always use default change event to prevent running into a loop
10668   if (ChangeEvent[x][y] == -1)
10669     ChangeEvent[x][y] = CE_DELAY;
10670
10671   if (ChangeEvent[x][y] == CE_DELAY)
10672   {
10673     // reset actual trigger element, trigger player and action element
10674     change->actual_trigger_element = EL_EMPTY;
10675     change->actual_trigger_player = EL_EMPTY;
10676     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10677     change->actual_trigger_side = CH_SIDE_NONE;
10678     change->actual_trigger_ce_value = 0;
10679     change->actual_trigger_ce_score = 0;
10680   }
10681
10682   // do not change elements more than a specified maximum number of changes
10683   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10684     return FALSE;
10685
10686   ChangeCount[x][y]++;          // count number of changes in the same frame
10687
10688   if (change->explode)
10689   {
10690     Bang(x, y);
10691
10692     return TRUE;
10693   }
10694
10695   if (change->use_target_content)
10696   {
10697     boolean complete_replace = TRUE;
10698     boolean can_replace[3][3];
10699     int xx, yy;
10700
10701     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10702     {
10703       boolean is_empty;
10704       boolean is_walkable;
10705       boolean is_diggable;
10706       boolean is_collectible;
10707       boolean is_removable;
10708       boolean is_destructible;
10709       int ex = x + xx - 1;
10710       int ey = y + yy - 1;
10711       int content_element = change->target_content.e[xx][yy];
10712       int e;
10713
10714       can_replace[xx][yy] = TRUE;
10715
10716       if (ex == x && ey == y)   // do not check changing element itself
10717         continue;
10718
10719       if (content_element == EL_EMPTY_SPACE)
10720       {
10721         can_replace[xx][yy] = FALSE;    // do not replace border with space
10722
10723         continue;
10724       }
10725
10726       if (!IN_LEV_FIELD(ex, ey))
10727       {
10728         can_replace[xx][yy] = FALSE;
10729         complete_replace = FALSE;
10730
10731         continue;
10732       }
10733
10734       e = Tile[ex][ey];
10735
10736       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10737         e = MovingOrBlocked2Element(ex, ey);
10738
10739       is_empty = (IS_FREE(ex, ey) ||
10740                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10741
10742       is_walkable     = (is_empty || IS_WALKABLE(e));
10743       is_diggable     = (is_empty || IS_DIGGABLE(e));
10744       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10745       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10746       is_removable    = (is_diggable || is_collectible);
10747
10748       can_replace[xx][yy] =
10749         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10750           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10751           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10752           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10753           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10754           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10755          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10756
10757       if (!can_replace[xx][yy])
10758         complete_replace = FALSE;
10759     }
10760
10761     if (!change->only_if_complete || complete_replace)
10762     {
10763       boolean something_has_changed = FALSE;
10764
10765       if (change->only_if_complete && change->use_random_replace &&
10766           RND(100) < change->random_percentage)
10767         return FALSE;
10768
10769       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10770       {
10771         int ex = x + xx - 1;
10772         int ey = y + yy - 1;
10773         int content_element;
10774
10775         if (can_replace[xx][yy] && (!change->use_random_replace ||
10776                                     RND(100) < change->random_percentage))
10777         {
10778           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10779             RemoveMovingField(ex, ey);
10780
10781           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10782
10783           content_element = change->target_content.e[xx][yy];
10784           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10785                                               ce_value, ce_score);
10786
10787           CreateElementFromChange(ex, ey, target_element);
10788
10789           something_has_changed = TRUE;
10790
10791           // for symmetry reasons, freeze newly created border elements
10792           if (ex != x || ey != y)
10793             Stop[ex][ey] = TRUE;        // no more moving in this frame
10794         }
10795       }
10796
10797       if (something_has_changed)
10798       {
10799         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10800         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10801       }
10802     }
10803   }
10804   else
10805   {
10806     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10807                                         ce_value, ce_score);
10808
10809     if (element == EL_DIAGONAL_GROWING ||
10810         element == EL_DIAGONAL_SHRINKING)
10811     {
10812       target_element = Store[x][y];
10813
10814       Store[x][y] = EL_EMPTY;
10815     }
10816
10817     // special case: element changes to player (and may be kept if walkable)
10818     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10819       CreateElementFromChange(x, y, EL_EMPTY);
10820
10821     CreateElementFromChange(x, y, target_element);
10822
10823     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10824     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10825   }
10826
10827   // this uses direct change before indirect change
10828   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10829
10830   return TRUE;
10831 }
10832
10833 static void HandleElementChange(int x, int y, int page)
10834 {
10835   int element = MovingOrBlocked2Element(x, y);
10836   struct ElementInfo *ei = &element_info[element];
10837   struct ElementChangeInfo *change = &ei->change_page[page];
10838   boolean handle_action_before_change = FALSE;
10839
10840 #ifdef DEBUG
10841   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10842       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10843   {
10844     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10845           x, y, element, element_info[element].token_name);
10846     Debug("game:playing:HandleElementChange", "This should never happen!");
10847   }
10848 #endif
10849
10850   // this can happen with classic bombs on walkable, changing elements
10851   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10852   {
10853     return;
10854   }
10855
10856   if (ChangeDelay[x][y] == 0)           // initialize element change
10857   {
10858     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10859
10860     if (change->can_change)
10861     {
10862       // !!! not clear why graphic animation should be reset at all here !!!
10863       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10864       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10865
10866       /*
10867         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10868
10869         When using an animation frame delay of 1 (this only happens with
10870         "sp_zonk.moving.left/right" in the classic graphics), the default
10871         (non-moving) animation shows wrong animation frames (while the
10872         moving animation, like "sp_zonk.moving.left/right", is correct,
10873         so this graphical bug never shows up with the classic graphics).
10874         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10875         be drawn instead of the correct frames 0,1,2,3. This is caused by
10876         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10877         an element change: First when the change delay ("ChangeDelay[][]")
10878         counter has reached zero after decrementing, then a second time in
10879         the next frame (after "GfxFrame[][]" was already incremented) when
10880         "ChangeDelay[][]" is reset to the initial delay value again.
10881
10882         This causes frame 0 to be drawn twice, while the last frame won't
10883         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10884
10885         As some animations may already be cleverly designed around this bug
10886         (at least the "Snake Bite" snake tail animation does this), it cannot
10887         simply be fixed here without breaking such existing animations.
10888         Unfortunately, it cannot easily be detected if a graphics set was
10889         designed "before" or "after" the bug was fixed. As a workaround,
10890         a new graphics set option "game.graphics_engine_version" was added
10891         to be able to specify the game's major release version for which the
10892         graphics set was designed, which can then be used to decide if the
10893         bugfix should be used (version 4 and above) or not (version 3 or
10894         below, or if no version was specified at all, as with old sets).
10895
10896         (The wrong/fixed animation frames can be tested with the test level set
10897         "test_gfxframe" and level "000", which contains a specially prepared
10898         custom element at level position (x/y) == (11/9) which uses the zonk
10899         animation mentioned above. Using "game.graphics_engine_version: 4"
10900         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10901         This can also be seen from the debug output for this test element.)
10902       */
10903
10904       // when a custom element is about to change (for example by change delay),
10905       // do not reset graphic animation when the custom element is moving
10906       if (game.graphics_engine_version < 4 &&
10907           !IS_MOVING(x, y))
10908       {
10909         ResetGfxAnimation(x, y);
10910         ResetRandomAnimationValue(x, y);
10911       }
10912
10913       if (change->pre_change_function)
10914         change->pre_change_function(x, y);
10915     }
10916   }
10917
10918   ChangeDelay[x][y]--;
10919
10920   if (ChangeDelay[x][y] != 0)           // continue element change
10921   {
10922     if (change->can_change)
10923     {
10924       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10925
10926       if (IS_ANIMATED(graphic))
10927         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10928
10929       if (change->change_function)
10930         change->change_function(x, y);
10931     }
10932   }
10933   else                                  // finish element change
10934   {
10935     if (ChangePage[x][y] != -1)         // remember page from delayed change
10936     {
10937       page = ChangePage[x][y];
10938       ChangePage[x][y] = -1;
10939
10940       change = &ei->change_page[page];
10941     }
10942
10943     if (IS_MOVING(x, y))                // never change a running system ;-)
10944     {
10945       ChangeDelay[x][y] = 1;            // try change after next move step
10946       ChangePage[x][y] = page;          // remember page to use for change
10947
10948       return;
10949     }
10950
10951     // special case: set new level random seed before changing element
10952     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10953       handle_action_before_change = TRUE;
10954
10955     if (change->has_action && handle_action_before_change)
10956       ExecuteCustomElementAction(x, y, element, page);
10957
10958     if (change->can_change)
10959     {
10960       if (ChangeElement(x, y, element, page))
10961       {
10962         if (change->post_change_function)
10963           change->post_change_function(x, y);
10964       }
10965     }
10966
10967     if (change->has_action && !handle_action_before_change)
10968       ExecuteCustomElementAction(x, y, element, page);
10969   }
10970 }
10971
10972 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10973                                               int trigger_element,
10974                                               int trigger_event,
10975                                               int trigger_player,
10976                                               int trigger_side,
10977                                               int trigger_page)
10978 {
10979   boolean change_done_any = FALSE;
10980   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10981   int i;
10982
10983   if (!(trigger_events[trigger_element][trigger_event]))
10984     return FALSE;
10985
10986   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10987
10988   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10989   {
10990     int element = EL_CUSTOM_START + i;
10991     boolean change_done = FALSE;
10992     int p;
10993
10994     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10995         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10996       continue;
10997
10998     for (p = 0; p < element_info[element].num_change_pages; p++)
10999     {
11000       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11001
11002       if (change->can_change_or_has_action &&
11003           change->has_event[trigger_event] &&
11004           change->trigger_side & trigger_side &&
11005           change->trigger_player & trigger_player &&
11006           change->trigger_page & trigger_page_bits &&
11007           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11008       {
11009         change->actual_trigger_element = trigger_element;
11010         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11011         change->actual_trigger_player_bits = trigger_player;
11012         change->actual_trigger_side = trigger_side;
11013         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11014         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11015
11016         if ((change->can_change && !change_done) || change->has_action)
11017         {
11018           int x, y;
11019
11020           SCAN_PLAYFIELD(x, y)
11021           {
11022             if (Tile[x][y] == element)
11023             {
11024               if (change->can_change && !change_done)
11025               {
11026                 // if element already changed in this frame, not only prevent
11027                 // another element change (checked in ChangeElement()), but
11028                 // also prevent additional element actions for this element
11029
11030                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11031                     !level.use_action_after_change_bug)
11032                   continue;
11033
11034                 ChangeDelay[x][y] = 1;
11035                 ChangeEvent[x][y] = trigger_event;
11036
11037                 HandleElementChange(x, y, p);
11038               }
11039               else if (change->has_action)
11040               {
11041                 // if element already changed in this frame, not only prevent
11042                 // another element change (checked in ChangeElement()), but
11043                 // also prevent additional element actions for this element
11044
11045                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11046                     !level.use_action_after_change_bug)
11047                   continue;
11048
11049                 ExecuteCustomElementAction(x, y, element, p);
11050                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11051               }
11052             }
11053           }
11054
11055           if (change->can_change)
11056           {
11057             change_done = TRUE;
11058             change_done_any = TRUE;
11059           }
11060         }
11061       }
11062     }
11063   }
11064
11065   RECURSION_LOOP_DETECTION_END();
11066
11067   return change_done_any;
11068 }
11069
11070 static boolean CheckElementChangeExt(int x, int y,
11071                                      int element,
11072                                      int trigger_element,
11073                                      int trigger_event,
11074                                      int trigger_player,
11075                                      int trigger_side)
11076 {
11077   boolean change_done = FALSE;
11078   int p;
11079
11080   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11081       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11082     return FALSE;
11083
11084   if (Tile[x][y] == EL_BLOCKED)
11085   {
11086     Blocked2Moving(x, y, &x, &y);
11087     element = Tile[x][y];
11088   }
11089
11090   // check if element has already changed or is about to change after moving
11091   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11092        Tile[x][y] != element) ||
11093
11094       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11095        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11096         ChangePage[x][y] != -1)))
11097     return FALSE;
11098
11099   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11100
11101   for (p = 0; p < element_info[element].num_change_pages; p++)
11102   {
11103     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11104
11105     /* check trigger element for all events where the element that is checked
11106        for changing interacts with a directly adjacent element -- this is
11107        different to element changes that affect other elements to change on the
11108        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11109     boolean check_trigger_element =
11110       (trigger_event == CE_NEXT_TO_X ||
11111        trigger_event == CE_TOUCHING_X ||
11112        trigger_event == CE_HITTING_X ||
11113        trigger_event == CE_HIT_BY_X ||
11114        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11115
11116     if (change->can_change_or_has_action &&
11117         change->has_event[trigger_event] &&
11118         change->trigger_side & trigger_side &&
11119         change->trigger_player & trigger_player &&
11120         (!check_trigger_element ||
11121          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11122     {
11123       change->actual_trigger_element = trigger_element;
11124       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11125       change->actual_trigger_player_bits = trigger_player;
11126       change->actual_trigger_side = trigger_side;
11127       change->actual_trigger_ce_value = CustomValue[x][y];
11128       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11129
11130       // special case: trigger element not at (x,y) position for some events
11131       if (check_trigger_element)
11132       {
11133         static struct
11134         {
11135           int dx, dy;
11136         } move_xy[] =
11137           {
11138             {  0,  0 },
11139             { -1,  0 },
11140             { +1,  0 },
11141             {  0,  0 },
11142             {  0, -1 },
11143             {  0,  0 }, { 0, 0 }, { 0, 0 },
11144             {  0, +1 }
11145           };
11146
11147         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11148         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11149
11150         change->actual_trigger_ce_value = CustomValue[xx][yy];
11151         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11152       }
11153
11154       if (change->can_change && !change_done)
11155       {
11156         ChangeDelay[x][y] = 1;
11157         ChangeEvent[x][y] = trigger_event;
11158
11159         HandleElementChange(x, y, p);
11160
11161         change_done = TRUE;
11162       }
11163       else if (change->has_action)
11164       {
11165         ExecuteCustomElementAction(x, y, element, p);
11166         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11167       }
11168     }
11169   }
11170
11171   RECURSION_LOOP_DETECTION_END();
11172
11173   return change_done;
11174 }
11175
11176 static void PlayPlayerSound(struct PlayerInfo *player)
11177 {
11178   int jx = player->jx, jy = player->jy;
11179   int sound_element = player->artwork_element;
11180   int last_action = player->last_action_waiting;
11181   int action = player->action_waiting;
11182
11183   if (player->is_waiting)
11184   {
11185     if (action != last_action)
11186       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11187     else
11188       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11189   }
11190   else
11191   {
11192     if (action != last_action)
11193       StopSound(element_info[sound_element].sound[last_action]);
11194
11195     if (last_action == ACTION_SLEEPING)
11196       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11197   }
11198 }
11199
11200 static void PlayAllPlayersSound(void)
11201 {
11202   int i;
11203
11204   for (i = 0; i < MAX_PLAYERS; i++)
11205     if (stored_player[i].active)
11206       PlayPlayerSound(&stored_player[i]);
11207 }
11208
11209 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11210 {
11211   boolean last_waiting = player->is_waiting;
11212   int move_dir = player->MovDir;
11213
11214   player->dir_waiting = move_dir;
11215   player->last_action_waiting = player->action_waiting;
11216
11217   if (is_waiting)
11218   {
11219     if (!last_waiting)          // not waiting -> waiting
11220     {
11221       player->is_waiting = TRUE;
11222
11223       player->frame_counter_bored =
11224         FrameCounter +
11225         game.player_boring_delay_fixed +
11226         GetSimpleRandom(game.player_boring_delay_random);
11227       player->frame_counter_sleeping =
11228         FrameCounter +
11229         game.player_sleeping_delay_fixed +
11230         GetSimpleRandom(game.player_sleeping_delay_random);
11231
11232       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11233     }
11234
11235     if (game.player_sleeping_delay_fixed +
11236         game.player_sleeping_delay_random > 0 &&
11237         player->anim_delay_counter == 0 &&
11238         player->post_delay_counter == 0 &&
11239         FrameCounter >= player->frame_counter_sleeping)
11240       player->is_sleeping = TRUE;
11241     else if (game.player_boring_delay_fixed +
11242              game.player_boring_delay_random > 0 &&
11243              FrameCounter >= player->frame_counter_bored)
11244       player->is_bored = TRUE;
11245
11246     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11247                               player->is_bored ? ACTION_BORING :
11248                               ACTION_WAITING);
11249
11250     if (player->is_sleeping && player->use_murphy)
11251     {
11252       // special case for sleeping Murphy when leaning against non-free tile
11253
11254       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11255           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11256            !IS_MOVING(player->jx - 1, player->jy)))
11257         move_dir = MV_LEFT;
11258       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11259                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11260                 !IS_MOVING(player->jx + 1, player->jy)))
11261         move_dir = MV_RIGHT;
11262       else
11263         player->is_sleeping = FALSE;
11264
11265       player->dir_waiting = move_dir;
11266     }
11267
11268     if (player->is_sleeping)
11269     {
11270       if (player->num_special_action_sleeping > 0)
11271       {
11272         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11273         {
11274           int last_special_action = player->special_action_sleeping;
11275           int num_special_action = player->num_special_action_sleeping;
11276           int special_action =
11277             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11278              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11279              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11280              last_special_action + 1 : ACTION_SLEEPING);
11281           int special_graphic =
11282             el_act_dir2img(player->artwork_element, special_action, move_dir);
11283
11284           player->anim_delay_counter =
11285             graphic_info[special_graphic].anim_delay_fixed +
11286             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11287           player->post_delay_counter =
11288             graphic_info[special_graphic].post_delay_fixed +
11289             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11290
11291           player->special_action_sleeping = special_action;
11292         }
11293
11294         if (player->anim_delay_counter > 0)
11295         {
11296           player->action_waiting = player->special_action_sleeping;
11297           player->anim_delay_counter--;
11298         }
11299         else if (player->post_delay_counter > 0)
11300         {
11301           player->post_delay_counter--;
11302         }
11303       }
11304     }
11305     else if (player->is_bored)
11306     {
11307       if (player->num_special_action_bored > 0)
11308       {
11309         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11310         {
11311           int special_action =
11312             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11313           int special_graphic =
11314             el_act_dir2img(player->artwork_element, special_action, move_dir);
11315
11316           player->anim_delay_counter =
11317             graphic_info[special_graphic].anim_delay_fixed +
11318             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11319           player->post_delay_counter =
11320             graphic_info[special_graphic].post_delay_fixed +
11321             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11322
11323           player->special_action_bored = special_action;
11324         }
11325
11326         if (player->anim_delay_counter > 0)
11327         {
11328           player->action_waiting = player->special_action_bored;
11329           player->anim_delay_counter--;
11330         }
11331         else if (player->post_delay_counter > 0)
11332         {
11333           player->post_delay_counter--;
11334         }
11335       }
11336     }
11337   }
11338   else if (last_waiting)        // waiting -> not waiting
11339   {
11340     player->is_waiting = FALSE;
11341     player->is_bored = FALSE;
11342     player->is_sleeping = FALSE;
11343
11344     player->frame_counter_bored = -1;
11345     player->frame_counter_sleeping = -1;
11346
11347     player->anim_delay_counter = 0;
11348     player->post_delay_counter = 0;
11349
11350     player->dir_waiting = player->MovDir;
11351     player->action_waiting = ACTION_DEFAULT;
11352
11353     player->special_action_bored = ACTION_DEFAULT;
11354     player->special_action_sleeping = ACTION_DEFAULT;
11355   }
11356 }
11357
11358 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11359 {
11360   if ((!player->is_moving  && player->was_moving) ||
11361       (player->MovPos == 0 && player->was_moving) ||
11362       (player->is_snapping && !player->was_snapping) ||
11363       (player->is_dropping && !player->was_dropping))
11364   {
11365     if (!CheckSaveEngineSnapshotToList())
11366       return;
11367
11368     player->was_moving = FALSE;
11369     player->was_snapping = TRUE;
11370     player->was_dropping = TRUE;
11371   }
11372   else
11373   {
11374     if (player->is_moving)
11375       player->was_moving = TRUE;
11376
11377     if (!player->is_snapping)
11378       player->was_snapping = FALSE;
11379
11380     if (!player->is_dropping)
11381       player->was_dropping = FALSE;
11382   }
11383
11384   static struct MouseActionInfo mouse_action_last = { 0 };
11385   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11386   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11387
11388   if (new_released)
11389     CheckSaveEngineSnapshotToList();
11390
11391   mouse_action_last = mouse_action;
11392 }
11393
11394 static void CheckSingleStepMode(struct PlayerInfo *player)
11395 {
11396   if (tape.single_step && tape.recording && !tape.pausing)
11397   {
11398     // as it is called "single step mode", just return to pause mode when the
11399     // player stopped moving after one tile (or never starts moving at all)
11400     // (reverse logic needed here in case single step mode used in team mode)
11401     if (player->is_moving ||
11402         player->is_pushing ||
11403         player->is_dropping_pressed ||
11404         player->effective_mouse_action.button)
11405       game.enter_single_step_mode = FALSE;
11406   }
11407
11408   CheckSaveEngineSnapshot(player);
11409 }
11410
11411 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11412 {
11413   int left      = player_action & JOY_LEFT;
11414   int right     = player_action & JOY_RIGHT;
11415   int up        = player_action & JOY_UP;
11416   int down      = player_action & JOY_DOWN;
11417   int button1   = player_action & JOY_BUTTON_1;
11418   int button2   = player_action & JOY_BUTTON_2;
11419   int dx        = (left ? -1 : right ? 1 : 0);
11420   int dy        = (up   ? -1 : down  ? 1 : 0);
11421
11422   if (!player->active || tape.pausing)
11423     return 0;
11424
11425   if (player_action)
11426   {
11427     if (button1)
11428       SnapField(player, dx, dy);
11429     else
11430     {
11431       if (button2)
11432         DropElement(player);
11433
11434       MovePlayer(player, dx, dy);
11435     }
11436
11437     CheckSingleStepMode(player);
11438
11439     SetPlayerWaiting(player, FALSE);
11440
11441     return player_action;
11442   }
11443   else
11444   {
11445     // no actions for this player (no input at player's configured device)
11446
11447     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11448     SnapField(player, 0, 0);
11449     CheckGravityMovementWhenNotMoving(player);
11450
11451     if (player->MovPos == 0)
11452       SetPlayerWaiting(player, TRUE);
11453
11454     if (player->MovPos == 0)    // needed for tape.playing
11455       player->is_moving = FALSE;
11456
11457     player->is_dropping = FALSE;
11458     player->is_dropping_pressed = FALSE;
11459     player->drop_pressed_delay = 0;
11460
11461     CheckSingleStepMode(player);
11462
11463     return 0;
11464   }
11465 }
11466
11467 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11468                                          byte *tape_action)
11469 {
11470   if (!tape.use_mouse_actions)
11471     return;
11472
11473   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11474   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11475   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11476 }
11477
11478 static void SetTapeActionFromMouseAction(byte *tape_action,
11479                                          struct MouseActionInfo *mouse_action)
11480 {
11481   if (!tape.use_mouse_actions)
11482     return;
11483
11484   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11485   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11486   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11487 }
11488
11489 static void CheckLevelSolved(void)
11490 {
11491   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11492   {
11493     if (game_em.level_solved &&
11494         !game_em.game_over)                             // game won
11495     {
11496       LevelSolved();
11497
11498       game_em.game_over = TRUE;
11499
11500       game.all_players_gone = TRUE;
11501     }
11502
11503     if (game_em.game_over)                              // game lost
11504       game.all_players_gone = TRUE;
11505   }
11506   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11507   {
11508     if (game_sp.level_solved &&
11509         !game_sp.game_over)                             // game won
11510     {
11511       LevelSolved();
11512
11513       game_sp.game_over = TRUE;
11514
11515       game.all_players_gone = TRUE;
11516     }
11517
11518     if (game_sp.game_over)                              // game lost
11519       game.all_players_gone = TRUE;
11520   }
11521   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11522   {
11523     if (game_mm.level_solved &&
11524         !game_mm.game_over)                             // game won
11525     {
11526       LevelSolved();
11527
11528       game_mm.game_over = TRUE;
11529
11530       game.all_players_gone = TRUE;
11531     }
11532
11533     if (game_mm.game_over)                              // game lost
11534       game.all_players_gone = TRUE;
11535   }
11536 }
11537
11538 static void CheckLevelTime_StepCounter(void)
11539 {
11540   int i;
11541
11542   TimePlayed++;
11543
11544   if (TimeLeft > 0)
11545   {
11546     TimeLeft--;
11547
11548     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11549       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11550
11551     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11552
11553     DisplayGameControlValues();
11554
11555     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11556       for (i = 0; i < MAX_PLAYERS; i++)
11557         KillPlayer(&stored_player[i]);
11558   }
11559   else if (game.no_level_time_limit && !game.all_players_gone)
11560   {
11561     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11562
11563     DisplayGameControlValues();
11564   }
11565 }
11566
11567 static void CheckLevelTime(void)
11568 {
11569   int i;
11570
11571   if (TimeFrames >= FRAMES_PER_SECOND)
11572   {
11573     TimeFrames = 0;
11574     TapeTime++;
11575
11576     for (i = 0; i < MAX_PLAYERS; i++)
11577     {
11578       struct PlayerInfo *player = &stored_player[i];
11579
11580       if (SHIELD_ON(player))
11581       {
11582         player->shield_normal_time_left--;
11583
11584         if (player->shield_deadly_time_left > 0)
11585           player->shield_deadly_time_left--;
11586       }
11587     }
11588
11589     if (!game.LevelSolved && !level.use_step_counter)
11590     {
11591       TimePlayed++;
11592
11593       if (TimeLeft > 0)
11594       {
11595         TimeLeft--;
11596
11597         if (TimeLeft <= 10 && game.time_limit)
11598           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11599
11600         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11601            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11602
11603         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11604
11605         if (!TimeLeft && game.time_limit)
11606         {
11607           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11608             game_em.lev->killed_out_of_time = TRUE;
11609           else
11610             for (i = 0; i < MAX_PLAYERS; i++)
11611               KillPlayer(&stored_player[i]);
11612         }
11613       }
11614       else if (game.no_level_time_limit && !game.all_players_gone)
11615       {
11616         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11617       }
11618
11619       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11620     }
11621
11622     if (tape.recording || tape.playing)
11623       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11624   }
11625
11626   if (tape.recording || tape.playing)
11627     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11628
11629   UpdateAndDisplayGameControlValues();
11630 }
11631
11632 void AdvanceFrameAndPlayerCounters(int player_nr)
11633 {
11634   int i;
11635
11636   // advance frame counters (global frame counter and time frame counter)
11637   FrameCounter++;
11638   TimeFrames++;
11639
11640   // advance player counters (counters for move delay, move animation etc.)
11641   for (i = 0; i < MAX_PLAYERS; i++)
11642   {
11643     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11644     int move_delay_value = stored_player[i].move_delay_value;
11645     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11646
11647     if (!advance_player_counters)       // not all players may be affected
11648       continue;
11649
11650     if (move_frames == 0)       // less than one move per game frame
11651     {
11652       int stepsize = TILEX / move_delay_value;
11653       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11654       int count = (stored_player[i].is_moving ?
11655                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11656
11657       if (count % delay == 0)
11658         move_frames = 1;
11659     }
11660
11661     stored_player[i].Frame += move_frames;
11662
11663     if (stored_player[i].MovPos != 0)
11664       stored_player[i].StepFrame += move_frames;
11665
11666     if (stored_player[i].move_delay > 0)
11667       stored_player[i].move_delay--;
11668
11669     // due to bugs in previous versions, counter must count up, not down
11670     if (stored_player[i].push_delay != -1)
11671       stored_player[i].push_delay++;
11672
11673     if (stored_player[i].drop_delay > 0)
11674       stored_player[i].drop_delay--;
11675
11676     if (stored_player[i].is_dropping_pressed)
11677       stored_player[i].drop_pressed_delay++;
11678   }
11679 }
11680
11681 void AdvanceFrameCounter(void)
11682 {
11683   FrameCounter++;
11684 }
11685
11686 void AdvanceGfxFrame(void)
11687 {
11688   int x, y;
11689
11690   SCAN_PLAYFIELD(x, y)
11691   {
11692     GfxFrame[x][y]++;
11693   }
11694 }
11695
11696 void StartGameActions(boolean init_network_game, boolean record_tape,
11697                       int random_seed)
11698 {
11699   unsigned int new_random_seed = InitRND(random_seed);
11700
11701   if (record_tape)
11702     TapeStartRecording(new_random_seed);
11703
11704   if (setup.auto_pause_on_start && !tape.pausing)
11705     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11706
11707   if (init_network_game)
11708   {
11709     SendToServer_LevelFile();
11710     SendToServer_StartPlaying();
11711
11712     return;
11713   }
11714
11715   InitGame();
11716 }
11717
11718 static void GameActionsExt(void)
11719 {
11720 #if 0
11721   static unsigned int game_frame_delay = 0;
11722 #endif
11723   unsigned int game_frame_delay_value;
11724   byte *recorded_player_action;
11725   byte summarized_player_action = 0;
11726   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11727   int i;
11728
11729   // detect endless loops, caused by custom element programming
11730   if (recursion_loop_detected && recursion_loop_depth == 0)
11731   {
11732     char *message = getStringCat3("Internal Error! Element ",
11733                                   EL_NAME(recursion_loop_element),
11734                                   " caused endless loop! Quit the game?");
11735
11736     Warn("element '%s' caused endless loop in game engine",
11737          EL_NAME(recursion_loop_element));
11738
11739     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11740
11741     recursion_loop_detected = FALSE;    // if game should be continued
11742
11743     free(message);
11744
11745     return;
11746   }
11747
11748   if (game.restart_level)
11749     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11750
11751   CheckLevelSolved();
11752
11753   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11754     GameWon();
11755
11756   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11757     TapeStop();
11758
11759   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11760     return;
11761
11762   game_frame_delay_value =
11763     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11764
11765   if (tape.playing && tape.warp_forward && !tape.pausing)
11766     game_frame_delay_value = 0;
11767
11768   SetVideoFrameDelay(game_frame_delay_value);
11769
11770   // (de)activate virtual buttons depending on current game status
11771   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11772   {
11773     if (game.all_players_gone)  // if no players there to be controlled anymore
11774       SetOverlayActive(FALSE);
11775     else if (!tape.playing)     // if game continues after tape stopped playing
11776       SetOverlayActive(TRUE);
11777   }
11778
11779 #if 0
11780 #if 0
11781   // ---------- main game synchronization point ----------
11782
11783   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11784
11785   Debug("game:playing:skip", "skip == %d", skip);
11786
11787 #else
11788   // ---------- main game synchronization point ----------
11789
11790   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11791 #endif
11792 #endif
11793
11794   if (network_playing && !network_player_action_received)
11795   {
11796     // try to get network player actions in time
11797
11798     // last chance to get network player actions without main loop delay
11799     HandleNetworking();
11800
11801     // game was quit by network peer
11802     if (game_status != GAME_MODE_PLAYING)
11803       return;
11804
11805     // check if network player actions still missing and game still running
11806     if (!network_player_action_received && !checkGameEnded())
11807       return;           // failed to get network player actions in time
11808
11809     // do not yet reset "network_player_action_received" (for tape.pausing)
11810   }
11811
11812   if (tape.pausing)
11813     return;
11814
11815   // at this point we know that we really continue executing the game
11816
11817   network_player_action_received = FALSE;
11818
11819   // when playing tape, read previously recorded player input from tape data
11820   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11821
11822   local_player->effective_mouse_action = local_player->mouse_action;
11823
11824   if (recorded_player_action != NULL)
11825     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11826                                  recorded_player_action);
11827
11828   // TapePlayAction() may return NULL when toggling to "pause before death"
11829   if (tape.pausing)
11830     return;
11831
11832   if (tape.set_centered_player)
11833   {
11834     game.centered_player_nr_next = tape.centered_player_nr_next;
11835     game.set_centered_player = TRUE;
11836   }
11837
11838   for (i = 0; i < MAX_PLAYERS; i++)
11839   {
11840     summarized_player_action |= stored_player[i].action;
11841
11842     if (!network_playing && (game.team_mode || tape.playing))
11843       stored_player[i].effective_action = stored_player[i].action;
11844   }
11845
11846   if (network_playing && !checkGameEnded())
11847     SendToServer_MovePlayer(summarized_player_action);
11848
11849   // summarize all actions at local players mapped input device position
11850   // (this allows using different input devices in single player mode)
11851   if (!network.enabled && !game.team_mode)
11852     stored_player[map_player_action[local_player->index_nr]].effective_action =
11853       summarized_player_action;
11854
11855   // summarize all actions at centered player in local team mode
11856   if (tape.recording &&
11857       setup.team_mode && !network.enabled &&
11858       setup.input_on_focus &&
11859       game.centered_player_nr != -1)
11860   {
11861     for (i = 0; i < MAX_PLAYERS; i++)
11862       stored_player[map_player_action[i]].effective_action =
11863         (i == game.centered_player_nr ? summarized_player_action : 0);
11864   }
11865
11866   if (recorded_player_action != NULL)
11867     for (i = 0; i < MAX_PLAYERS; i++)
11868       stored_player[i].effective_action = recorded_player_action[i];
11869
11870   for (i = 0; i < MAX_PLAYERS; i++)
11871   {
11872     tape_action[i] = stored_player[i].effective_action;
11873
11874     /* (this may happen in the RND game engine if a player was not present on
11875        the playfield on level start, but appeared later from a custom element */
11876     if (setup.team_mode &&
11877         tape.recording &&
11878         tape_action[i] &&
11879         !tape.player_participates[i])
11880       tape.player_participates[i] = TRUE;
11881   }
11882
11883   SetTapeActionFromMouseAction(tape_action,
11884                                &local_player->effective_mouse_action);
11885
11886   // only record actions from input devices, but not programmed actions
11887   if (tape.recording)
11888     TapeRecordAction(tape_action);
11889
11890   // remember if game was played (especially after tape stopped playing)
11891   if (!tape.playing && summarized_player_action)
11892     game.GamePlayed = TRUE;
11893
11894 #if USE_NEW_PLAYER_ASSIGNMENTS
11895   // !!! also map player actions in single player mode !!!
11896   // if (game.team_mode)
11897   if (1)
11898   {
11899     byte mapped_action[MAX_PLAYERS];
11900
11901 #if DEBUG_PLAYER_ACTIONS
11902     for (i = 0; i < MAX_PLAYERS; i++)
11903       DebugContinued("", "%d, ", stored_player[i].effective_action);
11904 #endif
11905
11906     for (i = 0; i < MAX_PLAYERS; i++)
11907       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11908
11909     for (i = 0; i < MAX_PLAYERS; i++)
11910       stored_player[i].effective_action = mapped_action[i];
11911
11912 #if DEBUG_PLAYER_ACTIONS
11913     DebugContinued("", "=> ");
11914     for (i = 0; i < MAX_PLAYERS; i++)
11915       DebugContinued("", "%d, ", stored_player[i].effective_action);
11916     DebugContinued("game:playing:player", "\n");
11917 #endif
11918   }
11919 #if DEBUG_PLAYER_ACTIONS
11920   else
11921   {
11922     for (i = 0; i < MAX_PLAYERS; i++)
11923       DebugContinued("", "%d, ", stored_player[i].effective_action);
11924     DebugContinued("game:playing:player", "\n");
11925   }
11926 #endif
11927 #endif
11928
11929   for (i = 0; i < MAX_PLAYERS; i++)
11930   {
11931     // allow engine snapshot in case of changed movement attempt
11932     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11933         (stored_player[i].effective_action & KEY_MOTION))
11934       game.snapshot.changed_action = TRUE;
11935
11936     // allow engine snapshot in case of snapping/dropping attempt
11937     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11938         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11939       game.snapshot.changed_action = TRUE;
11940
11941     game.snapshot.last_action[i] = stored_player[i].effective_action;
11942   }
11943
11944   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11945   {
11946     GameActions_EM_Main();
11947   }
11948   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11949   {
11950     GameActions_SP_Main();
11951   }
11952   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11953   {
11954     GameActions_MM_Main();
11955   }
11956   else
11957   {
11958     GameActions_RND_Main();
11959   }
11960
11961   BlitScreenToBitmap(backbuffer);
11962
11963   CheckLevelSolved();
11964   CheckLevelTime();
11965
11966   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11967
11968   if (global.show_frames_per_second)
11969   {
11970     static unsigned int fps_counter = 0;
11971     static int fps_frames = 0;
11972     unsigned int fps_delay_ms = Counter() - fps_counter;
11973
11974     fps_frames++;
11975
11976     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11977     {
11978       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11979
11980       fps_frames = 0;
11981       fps_counter = Counter();
11982
11983       // always draw FPS to screen after FPS value was updated
11984       redraw_mask |= REDRAW_FPS;
11985     }
11986
11987     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11988     if (GetDrawDeactivationMask() == REDRAW_NONE)
11989       redraw_mask |= REDRAW_FPS;
11990   }
11991 }
11992
11993 static void GameActions_CheckSaveEngineSnapshot(void)
11994 {
11995   if (!game.snapshot.save_snapshot)
11996     return;
11997
11998   // clear flag for saving snapshot _before_ saving snapshot
11999   game.snapshot.save_snapshot = FALSE;
12000
12001   SaveEngineSnapshotToList();
12002 }
12003
12004 void GameActions(void)
12005 {
12006   GameActionsExt();
12007
12008   GameActions_CheckSaveEngineSnapshot();
12009 }
12010
12011 void GameActions_EM_Main(void)
12012 {
12013   byte effective_action[MAX_PLAYERS];
12014   int i;
12015
12016   for (i = 0; i < MAX_PLAYERS; i++)
12017     effective_action[i] = stored_player[i].effective_action;
12018
12019   GameActions_EM(effective_action);
12020 }
12021
12022 void GameActions_SP_Main(void)
12023 {
12024   byte effective_action[MAX_PLAYERS];
12025   int i;
12026
12027   for (i = 0; i < MAX_PLAYERS; i++)
12028     effective_action[i] = stored_player[i].effective_action;
12029
12030   GameActions_SP(effective_action);
12031
12032   for (i = 0; i < MAX_PLAYERS; i++)
12033   {
12034     if (stored_player[i].force_dropping)
12035       stored_player[i].action |= KEY_BUTTON_DROP;
12036
12037     stored_player[i].force_dropping = FALSE;
12038   }
12039 }
12040
12041 void GameActions_MM_Main(void)
12042 {
12043   AdvanceGfxFrame();
12044
12045   GameActions_MM(local_player->effective_mouse_action);
12046 }
12047
12048 void GameActions_RND_Main(void)
12049 {
12050   GameActions_RND();
12051 }
12052
12053 void GameActions_RND(void)
12054 {
12055   static struct MouseActionInfo mouse_action_last = { 0 };
12056   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12057   int magic_wall_x = 0, magic_wall_y = 0;
12058   int i, x, y, element, graphic, last_gfx_frame;
12059
12060   InitPlayfieldScanModeVars();
12061
12062   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12063   {
12064     SCAN_PLAYFIELD(x, y)
12065     {
12066       ChangeCount[x][y] = 0;
12067       ChangeEvent[x][y] = -1;
12068     }
12069   }
12070
12071   if (game.set_centered_player)
12072   {
12073     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12074
12075     // switching to "all players" only possible if all players fit to screen
12076     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12077     {
12078       game.centered_player_nr_next = game.centered_player_nr;
12079       game.set_centered_player = FALSE;
12080     }
12081
12082     // do not switch focus to non-existing (or non-active) player
12083     if (game.centered_player_nr_next >= 0 &&
12084         !stored_player[game.centered_player_nr_next].active)
12085     {
12086       game.centered_player_nr_next = game.centered_player_nr;
12087       game.set_centered_player = FALSE;
12088     }
12089   }
12090
12091   if (game.set_centered_player &&
12092       ScreenMovPos == 0)        // screen currently aligned at tile position
12093   {
12094     int sx, sy;
12095
12096     if (game.centered_player_nr_next == -1)
12097     {
12098       setScreenCenteredToAllPlayers(&sx, &sy);
12099     }
12100     else
12101     {
12102       sx = stored_player[game.centered_player_nr_next].jx;
12103       sy = stored_player[game.centered_player_nr_next].jy;
12104     }
12105
12106     game.centered_player_nr = game.centered_player_nr_next;
12107     game.set_centered_player = FALSE;
12108
12109     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12110     DrawGameDoorValues();
12111   }
12112
12113   // check single step mode (set flag and clear again if any player is active)
12114   game.enter_single_step_mode =
12115     (tape.single_step && tape.recording && !tape.pausing);
12116
12117   for (i = 0; i < MAX_PLAYERS; i++)
12118   {
12119     int actual_player_action = stored_player[i].effective_action;
12120
12121 #if 1
12122     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12123        - rnd_equinox_tetrachloride 048
12124        - rnd_equinox_tetrachloride_ii 096
12125        - rnd_emanuel_schmieg 002
12126        - doctor_sloan_ww 001, 020
12127     */
12128     if (stored_player[i].MovPos == 0)
12129       CheckGravityMovement(&stored_player[i]);
12130 #endif
12131
12132     // overwrite programmed action with tape action
12133     if (stored_player[i].programmed_action)
12134       actual_player_action = stored_player[i].programmed_action;
12135
12136     PlayerActions(&stored_player[i], actual_player_action);
12137
12138     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12139   }
12140
12141   // single step pause mode may already have been toggled by "ScrollPlayer()"
12142   if (game.enter_single_step_mode && !tape.pausing)
12143     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12144
12145   ScrollScreen(NULL, SCROLL_GO_ON);
12146
12147   /* for backwards compatibility, the following code emulates a fixed bug that
12148      occured when pushing elements (causing elements that just made their last
12149      pushing step to already (if possible) make their first falling step in the
12150      same game frame, which is bad); this code is also needed to use the famous
12151      "spring push bug" which is used in older levels and might be wanted to be
12152      used also in newer levels, but in this case the buggy pushing code is only
12153      affecting the "spring" element and no other elements */
12154
12155   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12156   {
12157     for (i = 0; i < MAX_PLAYERS; i++)
12158     {
12159       struct PlayerInfo *player = &stored_player[i];
12160       int x = player->jx;
12161       int y = player->jy;
12162
12163       if (player->active && player->is_pushing && player->is_moving &&
12164           IS_MOVING(x, y) &&
12165           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12166            Tile[x][y] == EL_SPRING))
12167       {
12168         ContinueMoving(x, y);
12169
12170         // continue moving after pushing (this is actually a bug)
12171         if (!IS_MOVING(x, y))
12172           Stop[x][y] = FALSE;
12173       }
12174     }
12175   }
12176
12177   SCAN_PLAYFIELD(x, y)
12178   {
12179     Last[x][y] = Tile[x][y];
12180
12181     ChangeCount[x][y] = 0;
12182     ChangeEvent[x][y] = -1;
12183
12184     // this must be handled before main playfield loop
12185     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12186     {
12187       MovDelay[x][y]--;
12188       if (MovDelay[x][y] <= 0)
12189         RemoveField(x, y);
12190     }
12191
12192     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12193     {
12194       MovDelay[x][y]--;
12195       if (MovDelay[x][y] <= 0)
12196       {
12197         int element = Store[x][y];
12198         int move_direction = MovDir[x][y];
12199         int player_index_bit = Store2[x][y];
12200
12201         Store[x][y] = 0;
12202         Store2[x][y] = 0;
12203
12204         RemoveField(x, y);
12205         TEST_DrawLevelField(x, y);
12206
12207         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12208
12209         if (IS_ENVELOPE(element))
12210           local_player->show_envelope = element;
12211       }
12212     }
12213
12214 #if DEBUG
12215     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12216     {
12217       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12218             x, y);
12219       Debug("game:playing:GameActions_RND", "This should never happen!");
12220
12221       ChangePage[x][y] = -1;
12222     }
12223 #endif
12224
12225     Stop[x][y] = FALSE;
12226     if (WasJustMoving[x][y] > 0)
12227       WasJustMoving[x][y]--;
12228     if (WasJustFalling[x][y] > 0)
12229       WasJustFalling[x][y]--;
12230     if (CheckCollision[x][y] > 0)
12231       CheckCollision[x][y]--;
12232     if (CheckImpact[x][y] > 0)
12233       CheckImpact[x][y]--;
12234
12235     GfxFrame[x][y]++;
12236
12237     /* reset finished pushing action (not done in ContinueMoving() to allow
12238        continuous pushing animation for elements with zero push delay) */
12239     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12240     {
12241       ResetGfxAnimation(x, y);
12242       TEST_DrawLevelField(x, y);
12243     }
12244
12245 #if DEBUG
12246     if (IS_BLOCKED(x, y))
12247     {
12248       int oldx, oldy;
12249
12250       Blocked2Moving(x, y, &oldx, &oldy);
12251       if (!IS_MOVING(oldx, oldy))
12252       {
12253         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12254         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12255         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12256         Debug("game:playing:GameActions_RND", "This should never happen!");
12257       }
12258     }
12259 #endif
12260   }
12261
12262   if (mouse_action.button)
12263   {
12264     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12265     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12266
12267     x = mouse_action.lx;
12268     y = mouse_action.ly;
12269     element = Tile[x][y];
12270
12271     if (new_button)
12272     {
12273       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12274       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12275                                          ch_button);
12276     }
12277
12278     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12279     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12280                                        ch_button);
12281
12282     if (level.use_step_counter)
12283     {
12284       boolean counted_click = FALSE;
12285
12286       // element clicked that can change when clicked/pressed
12287       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12288           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12289            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12290         counted_click = TRUE;
12291
12292       // element clicked that can trigger change when clicked/pressed
12293       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12294           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12295         counted_click = TRUE;
12296
12297       if (new_button && counted_click)
12298         CheckLevelTime_StepCounter();
12299     }
12300   }
12301
12302   SCAN_PLAYFIELD(x, y)
12303   {
12304     element = Tile[x][y];
12305     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12306     last_gfx_frame = GfxFrame[x][y];
12307
12308     if (element == EL_EMPTY)
12309       graphic = el2img(GfxElementEmpty[x][y]);
12310
12311     ResetGfxFrame(x, y);
12312
12313     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12314       DrawLevelGraphicAnimation(x, y, graphic);
12315
12316     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12317         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12318       ResetRandomAnimationValue(x, y);
12319
12320     SetRandomAnimationValue(x, y);
12321
12322     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12323
12324     if (IS_INACTIVE(element))
12325     {
12326       if (IS_ANIMATED(graphic))
12327         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12328
12329       continue;
12330     }
12331
12332     // this may take place after moving, so 'element' may have changed
12333     if (IS_CHANGING(x, y) &&
12334         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12335     {
12336       int page = element_info[element].event_page_nr[CE_DELAY];
12337
12338       HandleElementChange(x, y, page);
12339
12340       element = Tile[x][y];
12341       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12342     }
12343
12344     CheckNextToConditions(x, y);
12345
12346     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12347     {
12348       StartMoving(x, y);
12349
12350       element = Tile[x][y];
12351       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12352
12353       if (IS_ANIMATED(graphic) &&
12354           !IS_MOVING(x, y) &&
12355           !Stop[x][y])
12356         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12357
12358       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12359         TEST_DrawTwinkleOnField(x, y);
12360     }
12361     else if (element == EL_ACID)
12362     {
12363       if (!Stop[x][y])
12364         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12365     }
12366     else if ((element == EL_EXIT_OPEN ||
12367               element == EL_EM_EXIT_OPEN ||
12368               element == EL_SP_EXIT_OPEN ||
12369               element == EL_STEEL_EXIT_OPEN ||
12370               element == EL_EM_STEEL_EXIT_OPEN ||
12371               element == EL_SP_TERMINAL ||
12372               element == EL_SP_TERMINAL_ACTIVE ||
12373               element == EL_EXTRA_TIME ||
12374               element == EL_SHIELD_NORMAL ||
12375               element == EL_SHIELD_DEADLY) &&
12376              IS_ANIMATED(graphic))
12377       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12378     else if (IS_MOVING(x, y))
12379       ContinueMoving(x, y);
12380     else if (IS_ACTIVE_BOMB(element))
12381       CheckDynamite(x, y);
12382     else if (element == EL_AMOEBA_GROWING)
12383       AmoebaGrowing(x, y);
12384     else if (element == EL_AMOEBA_SHRINKING)
12385       AmoebaShrinking(x, y);
12386
12387 #if !USE_NEW_AMOEBA_CODE
12388     else if (IS_AMOEBALIVE(element))
12389       AmoebaReproduce(x, y);
12390 #endif
12391
12392     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12393       Life(x, y);
12394     else if (element == EL_EXIT_CLOSED)
12395       CheckExit(x, y);
12396     else if (element == EL_EM_EXIT_CLOSED)
12397       CheckExitEM(x, y);
12398     else if (element == EL_STEEL_EXIT_CLOSED)
12399       CheckExitSteel(x, y);
12400     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12401       CheckExitSteelEM(x, y);
12402     else if (element == EL_SP_EXIT_CLOSED)
12403       CheckExitSP(x, y);
12404     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12405              element == EL_EXPANDABLE_STEELWALL_GROWING)
12406       WallGrowing(x, y);
12407     else if (element == EL_EXPANDABLE_WALL ||
12408              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12409              element == EL_EXPANDABLE_WALL_VERTICAL ||
12410              element == EL_EXPANDABLE_WALL_ANY ||
12411              element == EL_BD_EXPANDABLE_WALL ||
12412              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12413              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12414              element == EL_EXPANDABLE_STEELWALL_ANY)
12415       CheckWallGrowing(x, y);
12416     else if (element == EL_FLAMES)
12417       CheckForDragon(x, y);
12418     else if (element == EL_EXPLOSION)
12419       ; // drawing of correct explosion animation is handled separately
12420     else if (element == EL_ELEMENT_SNAPPING ||
12421              element == EL_DIAGONAL_SHRINKING ||
12422              element == EL_DIAGONAL_GROWING)
12423     {
12424       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12425
12426       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12427     }
12428     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12429       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12430
12431     if (IS_BELT_ACTIVE(element))
12432       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12433
12434     if (game.magic_wall_active)
12435     {
12436       int jx = local_player->jx, jy = local_player->jy;
12437
12438       // play the element sound at the position nearest to the player
12439       if ((element == EL_MAGIC_WALL_FULL ||
12440            element == EL_MAGIC_WALL_ACTIVE ||
12441            element == EL_MAGIC_WALL_EMPTYING ||
12442            element == EL_BD_MAGIC_WALL_FULL ||
12443            element == EL_BD_MAGIC_WALL_ACTIVE ||
12444            element == EL_BD_MAGIC_WALL_EMPTYING ||
12445            element == EL_DC_MAGIC_WALL_FULL ||
12446            element == EL_DC_MAGIC_WALL_ACTIVE ||
12447            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12448           ABS(x - jx) + ABS(y - jy) <
12449           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12450       {
12451         magic_wall_x = x;
12452         magic_wall_y = y;
12453       }
12454     }
12455   }
12456
12457 #if USE_NEW_AMOEBA_CODE
12458   // new experimental amoeba growth stuff
12459   if (!(FrameCounter % 8))
12460   {
12461     static unsigned int random = 1684108901;
12462
12463     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12464     {
12465       x = RND(lev_fieldx);
12466       y = RND(lev_fieldy);
12467       element = Tile[x][y];
12468
12469       if (!IS_PLAYER(x,y) &&
12470           (element == EL_EMPTY ||
12471            CAN_GROW_INTO(element) ||
12472            element == EL_QUICKSAND_EMPTY ||
12473            element == EL_QUICKSAND_FAST_EMPTY ||
12474            element == EL_ACID_SPLASH_LEFT ||
12475            element == EL_ACID_SPLASH_RIGHT))
12476       {
12477         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12478             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12479             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12480             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12481           Tile[x][y] = EL_AMOEBA_DROP;
12482       }
12483
12484       random = random * 129 + 1;
12485     }
12486   }
12487 #endif
12488
12489   game.explosions_delayed = FALSE;
12490
12491   SCAN_PLAYFIELD(x, y)
12492   {
12493     element = Tile[x][y];
12494
12495     if (ExplodeField[x][y])
12496       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12497     else if (element == EL_EXPLOSION)
12498       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12499
12500     ExplodeField[x][y] = EX_TYPE_NONE;
12501   }
12502
12503   game.explosions_delayed = TRUE;
12504
12505   if (game.magic_wall_active)
12506   {
12507     if (!(game.magic_wall_time_left % 4))
12508     {
12509       int element = Tile[magic_wall_x][magic_wall_y];
12510
12511       if (element == EL_BD_MAGIC_WALL_FULL ||
12512           element == EL_BD_MAGIC_WALL_ACTIVE ||
12513           element == EL_BD_MAGIC_WALL_EMPTYING)
12514         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12515       else if (element == EL_DC_MAGIC_WALL_FULL ||
12516                element == EL_DC_MAGIC_WALL_ACTIVE ||
12517                element == EL_DC_MAGIC_WALL_EMPTYING)
12518         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12519       else
12520         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12521     }
12522
12523     if (game.magic_wall_time_left > 0)
12524     {
12525       game.magic_wall_time_left--;
12526
12527       if (!game.magic_wall_time_left)
12528       {
12529         SCAN_PLAYFIELD(x, y)
12530         {
12531           element = Tile[x][y];
12532
12533           if (element == EL_MAGIC_WALL_ACTIVE ||
12534               element == EL_MAGIC_WALL_FULL)
12535           {
12536             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12537             TEST_DrawLevelField(x, y);
12538           }
12539           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12540                    element == EL_BD_MAGIC_WALL_FULL)
12541           {
12542             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12543             TEST_DrawLevelField(x, y);
12544           }
12545           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12546                    element == EL_DC_MAGIC_WALL_FULL)
12547           {
12548             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12549             TEST_DrawLevelField(x, y);
12550           }
12551         }
12552
12553         game.magic_wall_active = FALSE;
12554       }
12555     }
12556   }
12557
12558   if (game.light_time_left > 0)
12559   {
12560     game.light_time_left--;
12561
12562     if (game.light_time_left == 0)
12563       RedrawAllLightSwitchesAndInvisibleElements();
12564   }
12565
12566   if (game.timegate_time_left > 0)
12567   {
12568     game.timegate_time_left--;
12569
12570     if (game.timegate_time_left == 0)
12571       CloseAllOpenTimegates();
12572   }
12573
12574   if (game.lenses_time_left > 0)
12575   {
12576     game.lenses_time_left--;
12577
12578     if (game.lenses_time_left == 0)
12579       RedrawAllInvisibleElementsForLenses();
12580   }
12581
12582   if (game.magnify_time_left > 0)
12583   {
12584     game.magnify_time_left--;
12585
12586     if (game.magnify_time_left == 0)
12587       RedrawAllInvisibleElementsForMagnifier();
12588   }
12589
12590   for (i = 0; i < MAX_PLAYERS; i++)
12591   {
12592     struct PlayerInfo *player = &stored_player[i];
12593
12594     if (SHIELD_ON(player))
12595     {
12596       if (player->shield_deadly_time_left)
12597         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12598       else if (player->shield_normal_time_left)
12599         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12600     }
12601   }
12602
12603 #if USE_DELAYED_GFX_REDRAW
12604   SCAN_PLAYFIELD(x, y)
12605   {
12606     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12607     {
12608       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12609          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12610
12611       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12612         DrawLevelField(x, y);
12613
12614       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12615         DrawLevelFieldCrumbled(x, y);
12616
12617       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12618         DrawLevelFieldCrumbledNeighbours(x, y);
12619
12620       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12621         DrawTwinkleOnField(x, y);
12622     }
12623
12624     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12625   }
12626 #endif
12627
12628   DrawAllPlayers();
12629   PlayAllPlayersSound();
12630
12631   for (i = 0; i < MAX_PLAYERS; i++)
12632   {
12633     struct PlayerInfo *player = &stored_player[i];
12634
12635     if (player->show_envelope != 0 && (!player->active ||
12636                                        player->MovPos == 0))
12637     {
12638       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12639
12640       player->show_envelope = 0;
12641     }
12642   }
12643
12644   // use random number generator in every frame to make it less predictable
12645   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12646     RND(1);
12647
12648   mouse_action_last = mouse_action;
12649 }
12650
12651 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12652 {
12653   int min_x = x, min_y = y, max_x = x, max_y = y;
12654   int scr_fieldx = getScreenFieldSizeX();
12655   int scr_fieldy = getScreenFieldSizeY();
12656   int i;
12657
12658   for (i = 0; i < MAX_PLAYERS; i++)
12659   {
12660     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12661
12662     if (!stored_player[i].active || &stored_player[i] == player)
12663       continue;
12664
12665     min_x = MIN(min_x, jx);
12666     min_y = MIN(min_y, jy);
12667     max_x = MAX(max_x, jx);
12668     max_y = MAX(max_y, jy);
12669   }
12670
12671   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12672 }
12673
12674 static boolean AllPlayersInVisibleScreen(void)
12675 {
12676   int i;
12677
12678   for (i = 0; i < MAX_PLAYERS; i++)
12679   {
12680     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12681
12682     if (!stored_player[i].active)
12683       continue;
12684
12685     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12686       return FALSE;
12687   }
12688
12689   return TRUE;
12690 }
12691
12692 void ScrollLevel(int dx, int dy)
12693 {
12694   int scroll_offset = 2 * TILEX_VAR;
12695   int x, y;
12696
12697   BlitBitmap(drawto_field, drawto_field,
12698              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12699              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12700              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12701              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12702              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12703              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12704
12705   if (dx != 0)
12706   {
12707     x = (dx == 1 ? BX1 : BX2);
12708     for (y = BY1; y <= BY2; y++)
12709       DrawScreenField(x, y);
12710   }
12711
12712   if (dy != 0)
12713   {
12714     y = (dy == 1 ? BY1 : BY2);
12715     for (x = BX1; x <= BX2; x++)
12716       DrawScreenField(x, y);
12717   }
12718
12719   redraw_mask |= REDRAW_FIELD;
12720 }
12721
12722 static boolean canFallDown(struct PlayerInfo *player)
12723 {
12724   int jx = player->jx, jy = player->jy;
12725
12726   return (IN_LEV_FIELD(jx, jy + 1) &&
12727           (IS_FREE(jx, jy + 1) ||
12728            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12729           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12730           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12731 }
12732
12733 static boolean canPassField(int x, int y, int move_dir)
12734 {
12735   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12736   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12737   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12738   int nextx = x + dx;
12739   int nexty = y + dy;
12740   int element = Tile[x][y];
12741
12742   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12743           !CAN_MOVE(element) &&
12744           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12745           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12746           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12747 }
12748
12749 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12750 {
12751   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12752   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12753   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12754   int newx = x + dx;
12755   int newy = y + dy;
12756
12757   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12758           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12759           (IS_DIGGABLE(Tile[newx][newy]) ||
12760            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12761            canPassField(newx, newy, move_dir)));
12762 }
12763
12764 static void CheckGravityMovement(struct PlayerInfo *player)
12765 {
12766   if (player->gravity && !player->programmed_action)
12767   {
12768     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12769     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12770     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12771     int jx = player->jx, jy = player->jy;
12772     boolean player_is_moving_to_valid_field =
12773       (!player_is_snapping &&
12774        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12775         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12776     boolean player_can_fall_down = canFallDown(player);
12777
12778     if (player_can_fall_down &&
12779         !player_is_moving_to_valid_field)
12780       player->programmed_action = MV_DOWN;
12781   }
12782 }
12783
12784 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12785 {
12786   return CheckGravityMovement(player);
12787
12788   if (player->gravity && !player->programmed_action)
12789   {
12790     int jx = player->jx, jy = player->jy;
12791     boolean field_under_player_is_free =
12792       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12793     boolean player_is_standing_on_valid_field =
12794       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12795        (IS_WALKABLE(Tile[jx][jy]) &&
12796         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12797
12798     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12799       player->programmed_action = MV_DOWN;
12800   }
12801 }
12802
12803 /*
12804   MovePlayerOneStep()
12805   -----------------------------------------------------------------------------
12806   dx, dy:               direction (non-diagonal) to try to move the player to
12807   real_dx, real_dy:     direction as read from input device (can be diagonal)
12808 */
12809
12810 boolean MovePlayerOneStep(struct PlayerInfo *player,
12811                           int dx, int dy, int real_dx, int real_dy)
12812 {
12813   int jx = player->jx, jy = player->jy;
12814   int new_jx = jx + dx, new_jy = jy + dy;
12815   int can_move;
12816   boolean player_can_move = !player->cannot_move;
12817
12818   if (!player->active || (!dx && !dy))
12819     return MP_NO_ACTION;
12820
12821   player->MovDir = (dx < 0 ? MV_LEFT :
12822                     dx > 0 ? MV_RIGHT :
12823                     dy < 0 ? MV_UP :
12824                     dy > 0 ? MV_DOWN :  MV_NONE);
12825
12826   if (!IN_LEV_FIELD(new_jx, new_jy))
12827     return MP_NO_ACTION;
12828
12829   if (!player_can_move)
12830   {
12831     if (player->MovPos == 0)
12832     {
12833       player->is_moving = FALSE;
12834       player->is_digging = FALSE;
12835       player->is_collecting = FALSE;
12836       player->is_snapping = FALSE;
12837       player->is_pushing = FALSE;
12838     }
12839   }
12840
12841   if (!network.enabled && game.centered_player_nr == -1 &&
12842       !AllPlayersInSight(player, new_jx, new_jy))
12843     return MP_NO_ACTION;
12844
12845   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12846   if (can_move != MP_MOVING)
12847     return can_move;
12848
12849   // check if DigField() has caused relocation of the player
12850   if (player->jx != jx || player->jy != jy)
12851     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12852
12853   StorePlayer[jx][jy] = 0;
12854   player->last_jx = jx;
12855   player->last_jy = jy;
12856   player->jx = new_jx;
12857   player->jy = new_jy;
12858   StorePlayer[new_jx][new_jy] = player->element_nr;
12859
12860   if (player->move_delay_value_next != -1)
12861   {
12862     player->move_delay_value = player->move_delay_value_next;
12863     player->move_delay_value_next = -1;
12864   }
12865
12866   player->MovPos =
12867     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12868
12869   player->step_counter++;
12870
12871   PlayerVisit[jx][jy] = FrameCounter;
12872
12873   player->is_moving = TRUE;
12874
12875 #if 1
12876   // should better be called in MovePlayer(), but this breaks some tapes
12877   ScrollPlayer(player, SCROLL_INIT);
12878 #endif
12879
12880   return MP_MOVING;
12881 }
12882
12883 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12884 {
12885   int jx = player->jx, jy = player->jy;
12886   int old_jx = jx, old_jy = jy;
12887   int moved = MP_NO_ACTION;
12888
12889   if (!player->active)
12890     return FALSE;
12891
12892   if (!dx && !dy)
12893   {
12894     if (player->MovPos == 0)
12895     {
12896       player->is_moving = FALSE;
12897       player->is_digging = FALSE;
12898       player->is_collecting = FALSE;
12899       player->is_snapping = FALSE;
12900       player->is_pushing = FALSE;
12901     }
12902
12903     return FALSE;
12904   }
12905
12906   if (player->move_delay > 0)
12907     return FALSE;
12908
12909   player->move_delay = -1;              // set to "uninitialized" value
12910
12911   // store if player is automatically moved to next field
12912   player->is_auto_moving = (player->programmed_action != MV_NONE);
12913
12914   // remove the last programmed player action
12915   player->programmed_action = 0;
12916
12917   if (player->MovPos)
12918   {
12919     // should only happen if pre-1.2 tape recordings are played
12920     // this is only for backward compatibility
12921
12922     int original_move_delay_value = player->move_delay_value;
12923
12924 #if DEBUG
12925     Debug("game:playing:MovePlayer",
12926           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12927           tape.counter);
12928 #endif
12929
12930     // scroll remaining steps with finest movement resolution
12931     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12932
12933     while (player->MovPos)
12934     {
12935       ScrollPlayer(player, SCROLL_GO_ON);
12936       ScrollScreen(NULL, SCROLL_GO_ON);
12937
12938       AdvanceFrameAndPlayerCounters(player->index_nr);
12939
12940       DrawAllPlayers();
12941       BackToFront_WithFrameDelay(0);
12942     }
12943
12944     player->move_delay_value = original_move_delay_value;
12945   }
12946
12947   player->is_active = FALSE;
12948
12949   if (player->last_move_dir & MV_HORIZONTAL)
12950   {
12951     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12952       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12953   }
12954   else
12955   {
12956     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12957       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12958   }
12959
12960   if (!moved && !player->is_active)
12961   {
12962     player->is_moving = FALSE;
12963     player->is_digging = FALSE;
12964     player->is_collecting = FALSE;
12965     player->is_snapping = FALSE;
12966     player->is_pushing = FALSE;
12967   }
12968
12969   jx = player->jx;
12970   jy = player->jy;
12971
12972   if (moved & MP_MOVING && !ScreenMovPos &&
12973       (player->index_nr == game.centered_player_nr ||
12974        game.centered_player_nr == -1))
12975   {
12976     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12977
12978     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12979     {
12980       // actual player has left the screen -- scroll in that direction
12981       if (jx != old_jx)         // player has moved horizontally
12982         scroll_x += (jx - old_jx);
12983       else                      // player has moved vertically
12984         scroll_y += (jy - old_jy);
12985     }
12986     else
12987     {
12988       int offset_raw = game.scroll_delay_value;
12989
12990       if (jx != old_jx)         // player has moved horizontally
12991       {
12992         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12993         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12994         int new_scroll_x = jx - MIDPOSX + offset_x;
12995
12996         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12997             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12998           scroll_x = new_scroll_x;
12999
13000         // don't scroll over playfield boundaries
13001         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13002
13003         // don't scroll more than one field at a time
13004         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13005
13006         // don't scroll against the player's moving direction
13007         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13008             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13009           scroll_x = old_scroll_x;
13010       }
13011       else                      // player has moved vertically
13012       {
13013         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13014         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13015         int new_scroll_y = jy - MIDPOSY + offset_y;
13016
13017         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13018             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13019           scroll_y = new_scroll_y;
13020
13021         // don't scroll over playfield boundaries
13022         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13023
13024         // don't scroll more than one field at a time
13025         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13026
13027         // don't scroll against the player's moving direction
13028         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13029             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13030           scroll_y = old_scroll_y;
13031       }
13032     }
13033
13034     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13035     {
13036       if (!network.enabled && game.centered_player_nr == -1 &&
13037           !AllPlayersInVisibleScreen())
13038       {
13039         scroll_x = old_scroll_x;
13040         scroll_y = old_scroll_y;
13041       }
13042       else
13043       {
13044         ScrollScreen(player, SCROLL_INIT);
13045         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13046       }
13047     }
13048   }
13049
13050   player->StepFrame = 0;
13051
13052   if (moved & MP_MOVING)
13053   {
13054     if (old_jx != jx && old_jy == jy)
13055       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13056     else if (old_jx == jx && old_jy != jy)
13057       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13058
13059     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13060
13061     player->last_move_dir = player->MovDir;
13062     player->is_moving = TRUE;
13063     player->is_snapping = FALSE;
13064     player->is_switching = FALSE;
13065     player->is_dropping = FALSE;
13066     player->is_dropping_pressed = FALSE;
13067     player->drop_pressed_delay = 0;
13068
13069 #if 0
13070     // should better be called here than above, but this breaks some tapes
13071     ScrollPlayer(player, SCROLL_INIT);
13072 #endif
13073   }
13074   else
13075   {
13076     CheckGravityMovementWhenNotMoving(player);
13077
13078     player->is_moving = FALSE;
13079
13080     /* at this point, the player is allowed to move, but cannot move right now
13081        (e.g. because of something blocking the way) -- ensure that the player
13082        is also allowed to move in the next frame (in old versions before 3.1.1,
13083        the player was forced to wait again for eight frames before next try) */
13084
13085     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13086       player->move_delay = 0;   // allow direct movement in the next frame
13087   }
13088
13089   if (player->move_delay == -1)         // not yet initialized by DigField()
13090     player->move_delay = player->move_delay_value;
13091
13092   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13093   {
13094     TestIfPlayerTouchesBadThing(jx, jy);
13095     TestIfPlayerTouchesCustomElement(jx, jy);
13096   }
13097
13098   if (!player->active)
13099     RemovePlayer(player);
13100
13101   return moved;
13102 }
13103
13104 void ScrollPlayer(struct PlayerInfo *player, int mode)
13105 {
13106   int jx = player->jx, jy = player->jy;
13107   int last_jx = player->last_jx, last_jy = player->last_jy;
13108   int move_stepsize = TILEX / player->move_delay_value;
13109
13110   if (!player->active)
13111     return;
13112
13113   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13114     return;
13115
13116   if (mode == SCROLL_INIT)
13117   {
13118     player->actual_frame_counter.count = FrameCounter;
13119     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13120
13121     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13122         Tile[last_jx][last_jy] == EL_EMPTY)
13123     {
13124       int last_field_block_delay = 0;   // start with no blocking at all
13125       int block_delay_adjustment = player->block_delay_adjustment;
13126
13127       // if player blocks last field, add delay for exactly one move
13128       if (player->block_last_field)
13129       {
13130         last_field_block_delay += player->move_delay_value;
13131
13132         // when blocking enabled, prevent moving up despite gravity
13133         if (player->gravity && player->MovDir == MV_UP)
13134           block_delay_adjustment = -1;
13135       }
13136
13137       // add block delay adjustment (also possible when not blocking)
13138       last_field_block_delay += block_delay_adjustment;
13139
13140       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13141       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13142     }
13143
13144     if (player->MovPos != 0)    // player has not yet reached destination
13145       return;
13146   }
13147   else if (!FrameReached(&player->actual_frame_counter))
13148     return;
13149
13150   if (player->MovPos != 0)
13151   {
13152     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13153     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13154
13155     // before DrawPlayer() to draw correct player graphic for this case
13156     if (player->MovPos == 0)
13157       CheckGravityMovement(player);
13158   }
13159
13160   if (player->MovPos == 0)      // player reached destination field
13161   {
13162     if (player->move_delay_reset_counter > 0)
13163     {
13164       player->move_delay_reset_counter--;
13165
13166       if (player->move_delay_reset_counter == 0)
13167       {
13168         // continue with normal speed after quickly moving through gate
13169         HALVE_PLAYER_SPEED(player);
13170
13171         // be able to make the next move without delay
13172         player->move_delay = 0;
13173       }
13174     }
13175
13176     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13177         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13178         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13179         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13180         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13181         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13182         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13183         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13184     {
13185       ExitPlayer(player);
13186
13187       if (game.players_still_needed == 0 &&
13188           (game.friends_still_needed == 0 ||
13189            IS_SP_ELEMENT(Tile[jx][jy])))
13190         LevelSolved();
13191     }
13192
13193     player->last_jx = jx;
13194     player->last_jy = jy;
13195
13196     // this breaks one level: "machine", level 000
13197     {
13198       int move_direction = player->MovDir;
13199       int enter_side = MV_DIR_OPPOSITE(move_direction);
13200       int leave_side = move_direction;
13201       int old_jx = last_jx;
13202       int old_jy = last_jy;
13203       int old_element = Tile[old_jx][old_jy];
13204       int new_element = Tile[jx][jy];
13205
13206       if (IS_CUSTOM_ELEMENT(old_element))
13207         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13208                                    CE_LEFT_BY_PLAYER,
13209                                    player->index_bit, leave_side);
13210
13211       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13212                                           CE_PLAYER_LEAVES_X,
13213                                           player->index_bit, leave_side);
13214
13215       if (IS_CUSTOM_ELEMENT(new_element))
13216         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13217                                    player->index_bit, enter_side);
13218
13219       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13220                                           CE_PLAYER_ENTERS_X,
13221                                           player->index_bit, enter_side);
13222
13223       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13224                                         CE_MOVE_OF_X, move_direction);
13225     }
13226
13227     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13228     {
13229       TestIfPlayerTouchesBadThing(jx, jy);
13230       TestIfPlayerTouchesCustomElement(jx, jy);
13231
13232       /* needed because pushed element has not yet reached its destination,
13233          so it would trigger a change event at its previous field location */
13234       if (!player->is_pushing)
13235         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13236
13237       if (level.finish_dig_collect &&
13238           (player->is_digging || player->is_collecting))
13239       {
13240         int last_element = player->last_removed_element;
13241         int move_direction = player->MovDir;
13242         int enter_side = MV_DIR_OPPOSITE(move_direction);
13243         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13244                             CE_PLAYER_COLLECTS_X);
13245
13246         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13247                                             player->index_bit, enter_side);
13248
13249         player->last_removed_element = EL_UNDEFINED;
13250       }
13251
13252       if (!player->active)
13253         RemovePlayer(player);
13254     }
13255
13256     if (level.use_step_counter)
13257       CheckLevelTime_StepCounter();
13258
13259     if (tape.single_step && tape.recording && !tape.pausing &&
13260         !player->programmed_action)
13261       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13262
13263     if (!player->programmed_action)
13264       CheckSaveEngineSnapshot(player);
13265   }
13266 }
13267
13268 void ScrollScreen(struct PlayerInfo *player, int mode)
13269 {
13270   static DelayCounter screen_frame_counter = { 0 };
13271
13272   if (mode == SCROLL_INIT)
13273   {
13274     // set scrolling step size according to actual player's moving speed
13275     ScrollStepSize = TILEX / player->move_delay_value;
13276
13277     screen_frame_counter.count = FrameCounter;
13278     screen_frame_counter.value = 1;
13279
13280     ScreenMovDir = player->MovDir;
13281     ScreenMovPos = player->MovPos;
13282     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13283     return;
13284   }
13285   else if (!FrameReached(&screen_frame_counter))
13286     return;
13287
13288   if (ScreenMovPos)
13289   {
13290     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13291     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13292     redraw_mask |= REDRAW_FIELD;
13293   }
13294   else
13295     ScreenMovDir = MV_NONE;
13296 }
13297
13298 void CheckNextToConditions(int x, int y)
13299 {
13300   int element = Tile[x][y];
13301
13302   if (IS_PLAYER(x, y))
13303     TestIfPlayerNextToCustomElement(x, y);
13304
13305   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13306       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13307     TestIfElementNextToCustomElement(x, y);
13308 }
13309
13310 void TestIfPlayerNextToCustomElement(int x, int y)
13311 {
13312   struct XY *xy = xy_topdown;
13313   static int trigger_sides[4][2] =
13314   {
13315     // center side       border side
13316     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13317     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13318     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13319     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13320   };
13321   int i;
13322
13323   if (!IS_PLAYER(x, y))
13324     return;
13325
13326   struct PlayerInfo *player = PLAYERINFO(x, y);
13327
13328   if (player->is_moving)
13329     return;
13330
13331   for (i = 0; i < NUM_DIRECTIONS; i++)
13332   {
13333     int xx = x + xy[i].x;
13334     int yy = y + xy[i].y;
13335     int border_side = trigger_sides[i][1];
13336     int border_element;
13337
13338     if (!IN_LEV_FIELD(xx, yy))
13339       continue;
13340
13341     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13342       continue;         // center and border element not connected
13343
13344     border_element = Tile[xx][yy];
13345
13346     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13347                                player->index_bit, border_side);
13348     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13349                                         CE_PLAYER_NEXT_TO_X,
13350                                         player->index_bit, border_side);
13351
13352     /* use player element that is initially defined in the level playfield,
13353        not the player element that corresponds to the runtime player number
13354        (example: a level that contains EL_PLAYER_3 as the only player would
13355        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13356
13357     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13358                              CE_NEXT_TO_X, border_side);
13359   }
13360 }
13361
13362 void TestIfPlayerTouchesCustomElement(int x, int y)
13363 {
13364   struct XY *xy = xy_topdown;
13365   static int trigger_sides[4][2] =
13366   {
13367     // center side       border side
13368     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13369     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13370     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13371     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13372   };
13373   static int touch_dir[4] =
13374   {
13375     MV_LEFT | MV_RIGHT,
13376     MV_UP   | MV_DOWN,
13377     MV_UP   | MV_DOWN,
13378     MV_LEFT | MV_RIGHT
13379   };
13380   int center_element = Tile[x][y];      // should always be non-moving!
13381   int i;
13382
13383   for (i = 0; i < NUM_DIRECTIONS; i++)
13384   {
13385     int xx = x + xy[i].x;
13386     int yy = y + xy[i].y;
13387     int center_side = trigger_sides[i][0];
13388     int border_side = trigger_sides[i][1];
13389     int border_element;
13390
13391     if (!IN_LEV_FIELD(xx, yy))
13392       continue;
13393
13394     if (IS_PLAYER(x, y))                // player found at center element
13395     {
13396       struct PlayerInfo *player = PLAYERINFO(x, y);
13397
13398       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13399         border_element = Tile[xx][yy];          // may be moving!
13400       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13401         border_element = Tile[xx][yy];
13402       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13403         border_element = MovingOrBlocked2Element(xx, yy);
13404       else
13405         continue;               // center and border element do not touch
13406
13407       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13408                                  player->index_bit, border_side);
13409       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13410                                           CE_PLAYER_TOUCHES_X,
13411                                           player->index_bit, border_side);
13412
13413       {
13414         /* use player element that is initially defined in the level playfield,
13415            not the player element that corresponds to the runtime player number
13416            (example: a level that contains EL_PLAYER_3 as the only player would
13417            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13418         int player_element = PLAYERINFO(x, y)->initial_element;
13419
13420         CheckElementChangeBySide(xx, yy, border_element, player_element,
13421                                  CE_TOUCHING_X, border_side);
13422       }
13423     }
13424     else if (IS_PLAYER(xx, yy))         // player found at border element
13425     {
13426       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13427
13428       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13429       {
13430         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13431           continue;             // center and border element do not touch
13432       }
13433
13434       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13435                                  player->index_bit, center_side);
13436       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13437                                           CE_PLAYER_TOUCHES_X,
13438                                           player->index_bit, center_side);
13439
13440       {
13441         /* use player element that is initially defined in the level playfield,
13442            not the player element that corresponds to the runtime player number
13443            (example: a level that contains EL_PLAYER_3 as the only player would
13444            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13445         int player_element = PLAYERINFO(xx, yy)->initial_element;
13446
13447         CheckElementChangeBySide(x, y, center_element, player_element,
13448                                  CE_TOUCHING_X, center_side);
13449       }
13450
13451       break;
13452     }
13453   }
13454 }
13455
13456 void TestIfElementNextToCustomElement(int x, int y)
13457 {
13458   struct XY *xy = xy_topdown;
13459   static int trigger_sides[4][2] =
13460   {
13461     // center side      border side
13462     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13463     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13464     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13465     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13466   };
13467   int center_element = Tile[x][y];      // should always be non-moving!
13468   int i;
13469
13470   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13471     return;
13472
13473   for (i = 0; i < NUM_DIRECTIONS; i++)
13474   {
13475     int xx = x + xy[i].x;
13476     int yy = y + xy[i].y;
13477     int border_side = trigger_sides[i][1];
13478     int border_element;
13479
13480     if (!IN_LEV_FIELD(xx, yy))
13481       continue;
13482
13483     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13484       continue;                 // center and border element not connected
13485
13486     border_element = Tile[xx][yy];
13487
13488     // check for change of center element (but change it only once)
13489     if (CheckElementChangeBySide(x, y, center_element, border_element,
13490                                  CE_NEXT_TO_X, border_side))
13491       break;
13492   }
13493 }
13494
13495 void TestIfElementTouchesCustomElement(int x, int y)
13496 {
13497   struct XY *xy = xy_topdown;
13498   static int trigger_sides[4][2] =
13499   {
13500     // center side      border side
13501     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13502     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13503     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13504     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13505   };
13506   static int touch_dir[4] =
13507   {
13508     MV_LEFT | MV_RIGHT,
13509     MV_UP   | MV_DOWN,
13510     MV_UP   | MV_DOWN,
13511     MV_LEFT | MV_RIGHT
13512   };
13513   boolean change_center_element = FALSE;
13514   int center_element = Tile[x][y];      // should always be non-moving!
13515   int border_element_old[NUM_DIRECTIONS];
13516   int i;
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 border_element;
13523
13524     border_element_old[i] = -1;
13525
13526     if (!IN_LEV_FIELD(xx, yy))
13527       continue;
13528
13529     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13530       border_element = Tile[xx][yy];    // may be moving!
13531     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13532       border_element = Tile[xx][yy];
13533     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13534       border_element = MovingOrBlocked2Element(xx, yy);
13535     else
13536       continue;                 // center and border element do not touch
13537
13538     border_element_old[i] = border_element;
13539   }
13540
13541   for (i = 0; i < NUM_DIRECTIONS; i++)
13542   {
13543     int xx = x + xy[i].x;
13544     int yy = y + xy[i].y;
13545     int center_side = trigger_sides[i][0];
13546     int border_element = border_element_old[i];
13547
13548     if (border_element == -1)
13549       continue;
13550
13551     // check for change of border element
13552     CheckElementChangeBySide(xx, yy, border_element, center_element,
13553                              CE_TOUCHING_X, center_side);
13554
13555     // (center element cannot be player, so we dont have to check this here)
13556   }
13557
13558   for (i = 0; i < NUM_DIRECTIONS; i++)
13559   {
13560     int xx = x + xy[i].x;
13561     int yy = y + xy[i].y;
13562     int border_side = trigger_sides[i][1];
13563     int border_element = border_element_old[i];
13564
13565     if (border_element == -1)
13566       continue;
13567
13568     // check for change of center element (but change it only once)
13569     if (!change_center_element)
13570       change_center_element =
13571         CheckElementChangeBySide(x, y, center_element, border_element,
13572                                  CE_TOUCHING_X, border_side);
13573
13574     if (IS_PLAYER(xx, yy))
13575     {
13576       /* use player element that is initially defined in the level playfield,
13577          not the player element that corresponds to the runtime player number
13578          (example: a level that contains EL_PLAYER_3 as the only player would
13579          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13580       int player_element = PLAYERINFO(xx, yy)->initial_element;
13581
13582       CheckElementChangeBySide(x, y, center_element, player_element,
13583                                CE_TOUCHING_X, border_side);
13584     }
13585   }
13586 }
13587
13588 void TestIfElementHitsCustomElement(int x, int y, int direction)
13589 {
13590   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13591   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13592   int hitx = x + dx, hity = y + dy;
13593   int hitting_element = Tile[x][y];
13594   int touched_element;
13595
13596   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13597     return;
13598
13599   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13600                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13601
13602   if (IN_LEV_FIELD(hitx, hity))
13603   {
13604     int opposite_direction = MV_DIR_OPPOSITE(direction);
13605     int hitting_side = direction;
13606     int touched_side = opposite_direction;
13607     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13608                           MovDir[hitx][hity] != direction ||
13609                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13610
13611     object_hit = TRUE;
13612
13613     if (object_hit)
13614     {
13615       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13616                                CE_HITTING_X, touched_side);
13617
13618       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13619                                CE_HIT_BY_X, hitting_side);
13620
13621       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13622                                CE_HIT_BY_SOMETHING, opposite_direction);
13623
13624       if (IS_PLAYER(hitx, hity))
13625       {
13626         /* use player element that is initially defined in the level playfield,
13627            not the player element that corresponds to the runtime player number
13628            (example: a level that contains EL_PLAYER_3 as the only player would
13629            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13630         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13631
13632         CheckElementChangeBySide(x, y, hitting_element, player_element,
13633                                  CE_HITTING_X, touched_side);
13634       }
13635     }
13636   }
13637
13638   // "hitting something" is also true when hitting the playfield border
13639   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13640                            CE_HITTING_SOMETHING, direction);
13641 }
13642
13643 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13644 {
13645   int i, kill_x = -1, kill_y = -1;
13646
13647   int bad_element = -1;
13648   struct XY *test_xy = xy_topdown;
13649   static int test_dir[4] =
13650   {
13651     MV_UP,
13652     MV_LEFT,
13653     MV_RIGHT,
13654     MV_DOWN
13655   };
13656
13657   for (i = 0; i < NUM_DIRECTIONS; i++)
13658   {
13659     int test_x, test_y, test_move_dir, test_element;
13660
13661     test_x = good_x + test_xy[i].x;
13662     test_y = good_y + test_xy[i].y;
13663
13664     if (!IN_LEV_FIELD(test_x, test_y))
13665       continue;
13666
13667     test_move_dir =
13668       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13669
13670     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13671
13672     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13673        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13674     */
13675     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13676         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13677     {
13678       kill_x = test_x;
13679       kill_y = test_y;
13680       bad_element = test_element;
13681
13682       break;
13683     }
13684   }
13685
13686   if (kill_x != -1 || kill_y != -1)
13687   {
13688     if (IS_PLAYER(good_x, good_y))
13689     {
13690       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13691
13692       if (player->shield_deadly_time_left > 0 &&
13693           !IS_INDESTRUCTIBLE(bad_element))
13694         Bang(kill_x, kill_y);
13695       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13696         KillPlayer(player);
13697     }
13698     else
13699       Bang(good_x, good_y);
13700   }
13701 }
13702
13703 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13704 {
13705   int i, kill_x = -1, kill_y = -1;
13706   int bad_element = Tile[bad_x][bad_y];
13707   struct XY *test_xy = xy_topdown;
13708   static int touch_dir[4] =
13709   {
13710     MV_LEFT | MV_RIGHT,
13711     MV_UP   | MV_DOWN,
13712     MV_UP   | MV_DOWN,
13713     MV_LEFT | MV_RIGHT
13714   };
13715   static int test_dir[4] =
13716   {
13717     MV_UP,
13718     MV_LEFT,
13719     MV_RIGHT,
13720     MV_DOWN
13721   };
13722
13723   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13724     return;
13725
13726   for (i = 0; i < NUM_DIRECTIONS; i++)
13727   {
13728     int test_x, test_y, test_move_dir, test_element;
13729
13730     test_x = bad_x + test_xy[i].x;
13731     test_y = bad_y + test_xy[i].y;
13732
13733     if (!IN_LEV_FIELD(test_x, test_y))
13734       continue;
13735
13736     test_move_dir =
13737       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13738
13739     test_element = Tile[test_x][test_y];
13740
13741     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13742        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13743     */
13744     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13745         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13746     {
13747       // good thing is player or penguin that does not move away
13748       if (IS_PLAYER(test_x, test_y))
13749       {
13750         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13751
13752         if (bad_element == EL_ROBOT && player->is_moving)
13753           continue;     // robot does not kill player if he is moving
13754
13755         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13756         {
13757           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13758             continue;           // center and border element do not touch
13759         }
13760
13761         kill_x = test_x;
13762         kill_y = test_y;
13763
13764         break;
13765       }
13766       else if (test_element == EL_PENGUIN)
13767       {
13768         kill_x = test_x;
13769         kill_y = test_y;
13770
13771         break;
13772       }
13773     }
13774   }
13775
13776   if (kill_x != -1 || kill_y != -1)
13777   {
13778     if (IS_PLAYER(kill_x, kill_y))
13779     {
13780       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13781
13782       if (player->shield_deadly_time_left > 0 &&
13783           !IS_INDESTRUCTIBLE(bad_element))
13784         Bang(bad_x, bad_y);
13785       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13786         KillPlayer(player);
13787     }
13788     else
13789       Bang(kill_x, kill_y);
13790   }
13791 }
13792
13793 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13794 {
13795   int bad_element = Tile[bad_x][bad_y];
13796   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13797   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13798   int test_x = bad_x + dx, test_y = bad_y + dy;
13799   int test_move_dir, test_element;
13800   int kill_x = -1, kill_y = -1;
13801
13802   if (!IN_LEV_FIELD(test_x, test_y))
13803     return;
13804
13805   test_move_dir =
13806     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13807
13808   test_element = Tile[test_x][test_y];
13809
13810   if (test_move_dir != bad_move_dir)
13811   {
13812     // good thing can be player or penguin that does not move away
13813     if (IS_PLAYER(test_x, test_y))
13814     {
13815       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13816
13817       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13818          player as being hit when he is moving towards the bad thing, because
13819          the "get hit by" condition would be lost after the player stops) */
13820       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13821         return;         // player moves away from bad thing
13822
13823       kill_x = test_x;
13824       kill_y = test_y;
13825     }
13826     else if (test_element == EL_PENGUIN)
13827     {
13828       kill_x = test_x;
13829       kill_y = test_y;
13830     }
13831   }
13832
13833   if (kill_x != -1 || kill_y != -1)
13834   {
13835     if (IS_PLAYER(kill_x, kill_y))
13836     {
13837       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13838
13839       if (player->shield_deadly_time_left > 0 &&
13840           !IS_INDESTRUCTIBLE(bad_element))
13841         Bang(bad_x, bad_y);
13842       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13843         KillPlayer(player);
13844     }
13845     else
13846       Bang(kill_x, kill_y);
13847   }
13848 }
13849
13850 void TestIfPlayerTouchesBadThing(int x, int y)
13851 {
13852   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13853 }
13854
13855 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13856 {
13857   TestIfGoodThingHitsBadThing(x, y, move_dir);
13858 }
13859
13860 void TestIfBadThingTouchesPlayer(int x, int y)
13861 {
13862   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13863 }
13864
13865 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13866 {
13867   TestIfBadThingHitsGoodThing(x, y, move_dir);
13868 }
13869
13870 void TestIfFriendTouchesBadThing(int x, int y)
13871 {
13872   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13873 }
13874
13875 void TestIfBadThingTouchesFriend(int x, int y)
13876 {
13877   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13878 }
13879
13880 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13881 {
13882   int i, kill_x = bad_x, kill_y = bad_y;
13883   struct XY *xy = xy_topdown;
13884
13885   for (i = 0; i < NUM_DIRECTIONS; i++)
13886   {
13887     int x, y, element;
13888
13889     x = bad_x + xy[i].x;
13890     y = bad_y + xy[i].y;
13891     if (!IN_LEV_FIELD(x, y))
13892       continue;
13893
13894     element = Tile[x][y];
13895     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13896         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13897     {
13898       kill_x = x;
13899       kill_y = y;
13900       break;
13901     }
13902   }
13903
13904   if (kill_x != bad_x || kill_y != bad_y)
13905     Bang(bad_x, bad_y);
13906 }
13907
13908 void KillPlayer(struct PlayerInfo *player)
13909 {
13910   int jx = player->jx, jy = player->jy;
13911
13912   if (!player->active)
13913     return;
13914
13915 #if 0
13916   Debug("game:playing:KillPlayer",
13917         "0: killed == %d, active == %d, reanimated == %d",
13918         player->killed, player->active, player->reanimated);
13919 #endif
13920
13921   /* the following code was introduced to prevent an infinite loop when calling
13922      -> Bang()
13923      -> CheckTriggeredElementChangeExt()
13924      -> ExecuteCustomElementAction()
13925      -> KillPlayer()
13926      -> (infinitely repeating the above sequence of function calls)
13927      which occurs when killing the player while having a CE with the setting
13928      "kill player X when explosion of <player X>"; the solution using a new
13929      field "player->killed" was chosen for backwards compatibility, although
13930      clever use of the fields "player->active" etc. would probably also work */
13931 #if 1
13932   if (player->killed)
13933     return;
13934 #endif
13935
13936   player->killed = TRUE;
13937
13938   // remove accessible field at the player's position
13939   Tile[jx][jy] = EL_EMPTY;
13940
13941   // deactivate shield (else Bang()/Explode() would not work right)
13942   player->shield_normal_time_left = 0;
13943   player->shield_deadly_time_left = 0;
13944
13945 #if 0
13946   Debug("game:playing:KillPlayer",
13947         "1: killed == %d, active == %d, reanimated == %d",
13948         player->killed, player->active, player->reanimated);
13949 #endif
13950
13951   Bang(jx, jy);
13952
13953 #if 0
13954   Debug("game:playing:KillPlayer",
13955         "2: killed == %d, active == %d, reanimated == %d",
13956         player->killed, player->active, player->reanimated);
13957 #endif
13958
13959   if (player->reanimated)       // killed player may have been reanimated
13960     player->killed = player->reanimated = FALSE;
13961   else
13962     BuryPlayer(player);
13963 }
13964
13965 static void KillPlayerUnlessEnemyProtected(int x, int y)
13966 {
13967   if (!PLAYER_ENEMY_PROTECTED(x, y))
13968     KillPlayer(PLAYERINFO(x, y));
13969 }
13970
13971 static void KillPlayerUnlessExplosionProtected(int x, int y)
13972 {
13973   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13974     KillPlayer(PLAYERINFO(x, y));
13975 }
13976
13977 void BuryPlayer(struct PlayerInfo *player)
13978 {
13979   int jx = player->jx, jy = player->jy;
13980
13981   if (!player->active)
13982     return;
13983
13984   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13985   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13986
13987   RemovePlayer(player);
13988
13989   player->buried = TRUE;
13990
13991   if (game.all_players_gone)
13992     game.GameOver = TRUE;
13993 }
13994
13995 void RemovePlayer(struct PlayerInfo *player)
13996 {
13997   int jx = player->jx, jy = player->jy;
13998   int i, found = FALSE;
13999
14000   player->present = FALSE;
14001   player->active = FALSE;
14002
14003   // required for some CE actions (even if the player is not active anymore)
14004   player->MovPos = 0;
14005
14006   if (!ExplodeField[jx][jy])
14007     StorePlayer[jx][jy] = 0;
14008
14009   if (player->is_moving)
14010     TEST_DrawLevelField(player->last_jx, player->last_jy);
14011
14012   for (i = 0; i < MAX_PLAYERS; i++)
14013     if (stored_player[i].active)
14014       found = TRUE;
14015
14016   if (!found)
14017   {
14018     game.all_players_gone = TRUE;
14019     game.GameOver = TRUE;
14020   }
14021
14022   game.exit_x = game.robot_wheel_x = jx;
14023   game.exit_y = game.robot_wheel_y = jy;
14024 }
14025
14026 void ExitPlayer(struct PlayerInfo *player)
14027 {
14028   DrawPlayer(player);   // needed here only to cleanup last field
14029   RemovePlayer(player);
14030
14031   if (game.players_still_needed > 0)
14032     game.players_still_needed--;
14033 }
14034
14035 static void SetFieldForSnapping(int x, int y, int element, int direction,
14036                                 int player_index_bit)
14037 {
14038   struct ElementInfo *ei = &element_info[element];
14039   int direction_bit = MV_DIR_TO_BIT(direction);
14040   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14041   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14042                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14043
14044   Tile[x][y] = EL_ELEMENT_SNAPPING;
14045   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14046   MovDir[x][y] = direction;
14047   Store[x][y] = element;
14048   Store2[x][y] = player_index_bit;
14049
14050   ResetGfxAnimation(x, y);
14051
14052   GfxElement[x][y] = element;
14053   GfxAction[x][y] = action;
14054   GfxDir[x][y] = direction;
14055   GfxFrame[x][y] = -1;
14056 }
14057
14058 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14059                                    int player_index_bit)
14060 {
14061   TestIfElementTouchesCustomElement(x, y);      // for empty space
14062
14063   if (level.finish_dig_collect)
14064   {
14065     int dig_side = MV_DIR_OPPOSITE(direction);
14066     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14067                         CE_PLAYER_COLLECTS_X);
14068
14069     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14070                                         player_index_bit, dig_side);
14071     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14072                                         player_index_bit, dig_side);
14073   }
14074 }
14075
14076 /*
14077   =============================================================================
14078   checkDiagonalPushing()
14079   -----------------------------------------------------------------------------
14080   check if diagonal input device direction results in pushing of object
14081   (by checking if the alternative direction is walkable, diggable, ...)
14082   =============================================================================
14083 */
14084
14085 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14086                                     int x, int y, int real_dx, int real_dy)
14087 {
14088   int jx, jy, dx, dy, xx, yy;
14089
14090   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14091     return TRUE;
14092
14093   // diagonal direction: check alternative direction
14094   jx = player->jx;
14095   jy = player->jy;
14096   dx = x - jx;
14097   dy = y - jy;
14098   xx = jx + (dx == 0 ? real_dx : 0);
14099   yy = jy + (dy == 0 ? real_dy : 0);
14100
14101   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14102 }
14103
14104 /*
14105   =============================================================================
14106   DigField()
14107   -----------------------------------------------------------------------------
14108   x, y:                 field next to player (non-diagonal) to try to dig to
14109   real_dx, real_dy:     direction as read from input device (can be diagonal)
14110   =============================================================================
14111 */
14112
14113 static int DigField(struct PlayerInfo *player,
14114                     int oldx, int oldy, int x, int y,
14115                     int real_dx, int real_dy, int mode)
14116 {
14117   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14118   boolean player_was_pushing = player->is_pushing;
14119   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14120   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14121   int jx = oldx, jy = oldy;
14122   int dx = x - jx, dy = y - jy;
14123   int nextx = x + dx, nexty = y + dy;
14124   int move_direction = (dx == -1 ? MV_LEFT  :
14125                         dx == +1 ? MV_RIGHT :
14126                         dy == -1 ? MV_UP    :
14127                         dy == +1 ? MV_DOWN  : MV_NONE);
14128   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14129   int dig_side = MV_DIR_OPPOSITE(move_direction);
14130   int old_element = Tile[jx][jy];
14131   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14132   int collect_count;
14133
14134   if (is_player)                // function can also be called by EL_PENGUIN
14135   {
14136     if (player->MovPos == 0)
14137     {
14138       player->is_digging = FALSE;
14139       player->is_collecting = FALSE;
14140     }
14141
14142     if (player->MovPos == 0)    // last pushing move finished
14143       player->is_pushing = FALSE;
14144
14145     if (mode == DF_NO_PUSH)     // player just stopped pushing
14146     {
14147       player->is_switching = FALSE;
14148       player->push_delay = -1;
14149
14150       return MP_NO_ACTION;
14151     }
14152   }
14153   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14154     old_element = Back[jx][jy];
14155
14156   // in case of element dropped at player position, check background
14157   else if (Back[jx][jy] != EL_EMPTY &&
14158            game.engine_version >= VERSION_IDENT(2,2,0,0))
14159     old_element = Back[jx][jy];
14160
14161   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14162     return MP_NO_ACTION;        // field has no opening in this direction
14163
14164   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14165     return MP_NO_ACTION;        // field has no opening in this direction
14166
14167   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14168   {
14169     SplashAcid(x, y);
14170
14171     Tile[jx][jy] = player->artwork_element;
14172     InitMovingField(jx, jy, MV_DOWN);
14173     Store[jx][jy] = EL_ACID;
14174     ContinueMoving(jx, jy);
14175     BuryPlayer(player);
14176
14177     return MP_DONT_RUN_INTO;
14178   }
14179
14180   if (player_can_move && DONT_RUN_INTO(element))
14181   {
14182     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14183
14184     return MP_DONT_RUN_INTO;
14185   }
14186
14187   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14188     return MP_NO_ACTION;
14189
14190   collect_count = element_info[element].collect_count_initial;
14191
14192   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14193     return MP_NO_ACTION;
14194
14195   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14196     player_can_move = player_can_move_or_snap;
14197
14198   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14199       game.engine_version >= VERSION_IDENT(2,2,0,0))
14200   {
14201     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14202                                player->index_bit, dig_side);
14203     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14204                                         player->index_bit, dig_side);
14205
14206     if (element == EL_DC_LANDMINE)
14207       Bang(x, y);
14208
14209     if (Tile[x][y] != element)          // field changed by snapping
14210       return MP_ACTION;
14211
14212     return MP_NO_ACTION;
14213   }
14214
14215   if (player->gravity && is_player && !player->is_auto_moving &&
14216       canFallDown(player) && move_direction != MV_DOWN &&
14217       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14218     return MP_NO_ACTION;        // player cannot walk here due to gravity
14219
14220   if (player_can_move &&
14221       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14222   {
14223     int sound_element = SND_ELEMENT(element);
14224     int sound_action = ACTION_WALKING;
14225
14226     if (IS_RND_GATE(element))
14227     {
14228       if (!player->key[RND_GATE_NR(element)])
14229         return MP_NO_ACTION;
14230     }
14231     else if (IS_RND_GATE_GRAY(element))
14232     {
14233       if (!player->key[RND_GATE_GRAY_NR(element)])
14234         return MP_NO_ACTION;
14235     }
14236     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14237     {
14238       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14239         return MP_NO_ACTION;
14240     }
14241     else if (element == EL_EXIT_OPEN ||
14242              element == EL_EM_EXIT_OPEN ||
14243              element == EL_EM_EXIT_OPENING ||
14244              element == EL_STEEL_EXIT_OPEN ||
14245              element == EL_EM_STEEL_EXIT_OPEN ||
14246              element == EL_EM_STEEL_EXIT_OPENING ||
14247              element == EL_SP_EXIT_OPEN ||
14248              element == EL_SP_EXIT_OPENING)
14249     {
14250       sound_action = ACTION_PASSING;    // player is passing exit
14251     }
14252     else if (element == EL_EMPTY)
14253     {
14254       sound_action = ACTION_MOVING;             // nothing to walk on
14255     }
14256
14257     // play sound from background or player, whatever is available
14258     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14259       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14260     else
14261       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14262   }
14263   else if (player_can_move &&
14264            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14265   {
14266     if (!ACCESS_FROM(element, opposite_direction))
14267       return MP_NO_ACTION;      // field not accessible from this direction
14268
14269     if (CAN_MOVE(element))      // only fixed elements can be passed!
14270       return MP_NO_ACTION;
14271
14272     if (IS_EM_GATE(element))
14273     {
14274       if (!player->key[EM_GATE_NR(element)])
14275         return MP_NO_ACTION;
14276     }
14277     else if (IS_EM_GATE_GRAY(element))
14278     {
14279       if (!player->key[EM_GATE_GRAY_NR(element)])
14280         return MP_NO_ACTION;
14281     }
14282     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14283     {
14284       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14285         return MP_NO_ACTION;
14286     }
14287     else if (IS_EMC_GATE(element))
14288     {
14289       if (!player->key[EMC_GATE_NR(element)])
14290         return MP_NO_ACTION;
14291     }
14292     else if (IS_EMC_GATE_GRAY(element))
14293     {
14294       if (!player->key[EMC_GATE_GRAY_NR(element)])
14295         return MP_NO_ACTION;
14296     }
14297     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14298     {
14299       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14300         return MP_NO_ACTION;
14301     }
14302     else if (element == EL_DC_GATE_WHITE ||
14303              element == EL_DC_GATE_WHITE_GRAY ||
14304              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14305     {
14306       if (player->num_white_keys == 0)
14307         return MP_NO_ACTION;
14308
14309       player->num_white_keys--;
14310     }
14311     else if (IS_SP_PORT(element))
14312     {
14313       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14314           element == EL_SP_GRAVITY_PORT_RIGHT ||
14315           element == EL_SP_GRAVITY_PORT_UP ||
14316           element == EL_SP_GRAVITY_PORT_DOWN)
14317         player->gravity = !player->gravity;
14318       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14319                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14320                element == EL_SP_GRAVITY_ON_PORT_UP ||
14321                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14322         player->gravity = TRUE;
14323       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14324                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14325                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14326                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14327         player->gravity = FALSE;
14328     }
14329
14330     // automatically move to the next field with double speed
14331     player->programmed_action = move_direction;
14332
14333     if (player->move_delay_reset_counter == 0)
14334     {
14335       player->move_delay_reset_counter = 2;     // two double speed steps
14336
14337       DOUBLE_PLAYER_SPEED(player);
14338     }
14339
14340     PlayLevelSoundAction(x, y, ACTION_PASSING);
14341   }
14342   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14343   {
14344     RemoveField(x, y);
14345
14346     if (mode != DF_SNAP)
14347     {
14348       GfxElement[x][y] = GFX_ELEMENT(element);
14349       player->is_digging = TRUE;
14350     }
14351
14352     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14353
14354     // use old behaviour for old levels (digging)
14355     if (!level.finish_dig_collect)
14356     {
14357       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14358                                           player->index_bit, dig_side);
14359
14360       // if digging triggered player relocation, finish digging tile
14361       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14362         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14363     }
14364
14365     if (mode == DF_SNAP)
14366     {
14367       if (level.block_snap_field)
14368         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14369       else
14370         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14371
14372       // use old behaviour for old levels (snapping)
14373       if (!level.finish_dig_collect)
14374         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14375                                             player->index_bit, dig_side);
14376     }
14377   }
14378   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14379   {
14380     RemoveField(x, y);
14381
14382     if (is_player && mode != DF_SNAP)
14383     {
14384       GfxElement[x][y] = element;
14385       player->is_collecting = TRUE;
14386     }
14387
14388     if (element == EL_SPEED_PILL)
14389     {
14390       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14391     }
14392     else if (element == EL_EXTRA_TIME && level.time > 0)
14393     {
14394       TimeLeft += level.extra_time;
14395
14396       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14397
14398       DisplayGameControlValues();
14399     }
14400     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14401     {
14402       int shield_time = (element == EL_SHIELD_DEADLY ?
14403                          level.shield_deadly_time :
14404                          level.shield_normal_time);
14405
14406       player->shield_normal_time_left += shield_time;
14407       if (element == EL_SHIELD_DEADLY)
14408         player->shield_deadly_time_left += shield_time;
14409     }
14410     else if (element == EL_DYNAMITE ||
14411              element == EL_EM_DYNAMITE ||
14412              element == EL_SP_DISK_RED)
14413     {
14414       if (player->inventory_size < MAX_INVENTORY_SIZE)
14415         player->inventory_element[player->inventory_size++] = element;
14416
14417       DrawGameDoorValues();
14418     }
14419     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14420     {
14421       player->dynabomb_count++;
14422       player->dynabombs_left++;
14423     }
14424     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14425     {
14426       player->dynabomb_size++;
14427     }
14428     else if (element == EL_DYNABOMB_INCREASE_POWER)
14429     {
14430       player->dynabomb_xl = TRUE;
14431     }
14432     else if (IS_KEY(element))
14433     {
14434       player->key[KEY_NR(element)] = TRUE;
14435
14436       DrawGameDoorValues();
14437     }
14438     else if (element == EL_DC_KEY_WHITE)
14439     {
14440       player->num_white_keys++;
14441
14442       // display white keys?
14443       // DrawGameDoorValues();
14444     }
14445     else if (IS_ENVELOPE(element))
14446     {
14447       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14448
14449       if (!wait_for_snapping)
14450         player->show_envelope = element;
14451     }
14452     else if (element == EL_EMC_LENSES)
14453     {
14454       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14455
14456       RedrawAllInvisibleElementsForLenses();
14457     }
14458     else if (element == EL_EMC_MAGNIFIER)
14459     {
14460       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14461
14462       RedrawAllInvisibleElementsForMagnifier();
14463     }
14464     else if (IS_DROPPABLE(element) ||
14465              IS_THROWABLE(element))     // can be collected and dropped
14466     {
14467       int i;
14468
14469       if (collect_count == 0)
14470         player->inventory_infinite_element = element;
14471       else
14472         for (i = 0; i < collect_count; i++)
14473           if (player->inventory_size < MAX_INVENTORY_SIZE)
14474             player->inventory_element[player->inventory_size++] = element;
14475
14476       DrawGameDoorValues();
14477     }
14478     else if (collect_count > 0)
14479     {
14480       game.gems_still_needed -= collect_count;
14481       if (game.gems_still_needed < 0)
14482         game.gems_still_needed = 0;
14483
14484       game.snapshot.collected_item = TRUE;
14485
14486       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14487
14488       DisplayGameControlValues();
14489     }
14490
14491     RaiseScoreElement(element);
14492     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14493
14494     // use old behaviour for old levels (collecting)
14495     if (!level.finish_dig_collect && is_player)
14496     {
14497       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14498                                           player->index_bit, dig_side);
14499
14500       // if collecting triggered player relocation, finish collecting tile
14501       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14502         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14503     }
14504
14505     if (mode == DF_SNAP)
14506     {
14507       if (level.block_snap_field)
14508         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14509       else
14510         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14511
14512       // use old behaviour for old levels (snapping)
14513       if (!level.finish_dig_collect)
14514         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14515                                             player->index_bit, dig_side);
14516     }
14517   }
14518   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14519   {
14520     if (mode == DF_SNAP && element != EL_BD_ROCK)
14521       return MP_NO_ACTION;
14522
14523     if (CAN_FALL(element) && dy)
14524       return MP_NO_ACTION;
14525
14526     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14527         !(element == EL_SPRING && level.use_spring_bug))
14528       return MP_NO_ACTION;
14529
14530     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14531         ((move_direction & MV_VERTICAL &&
14532           ((element_info[element].move_pattern & MV_LEFT &&
14533             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14534            (element_info[element].move_pattern & MV_RIGHT &&
14535             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14536          (move_direction & MV_HORIZONTAL &&
14537           ((element_info[element].move_pattern & MV_UP &&
14538             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14539            (element_info[element].move_pattern & MV_DOWN &&
14540             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14541       return MP_NO_ACTION;
14542
14543     // do not push elements already moving away faster than player
14544     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14545         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14546       return MP_NO_ACTION;
14547
14548     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14549     {
14550       if (player->push_delay_value == -1 || !player_was_pushing)
14551         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14552     }
14553     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14554     {
14555       if (player->push_delay_value == -1)
14556         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14557     }
14558     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14559     {
14560       if (!player->is_pushing)
14561         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14562     }
14563
14564     player->is_pushing = TRUE;
14565     player->is_active = TRUE;
14566
14567     if (!(IN_LEV_FIELD(nextx, nexty) &&
14568           (IS_FREE(nextx, nexty) ||
14569            (IS_SB_ELEMENT(element) &&
14570             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14571            (IS_CUSTOM_ELEMENT(element) &&
14572             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14573       return MP_NO_ACTION;
14574
14575     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14576       return MP_NO_ACTION;
14577
14578     if (player->push_delay == -1)       // new pushing; restart delay
14579       player->push_delay = 0;
14580
14581     if (player->push_delay < player->push_delay_value &&
14582         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14583         element != EL_SPRING && element != EL_BALLOON)
14584     {
14585       // make sure that there is no move delay before next try to push
14586       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14587         player->move_delay = 0;
14588
14589       return MP_NO_ACTION;
14590     }
14591
14592     if (IS_CUSTOM_ELEMENT(element) &&
14593         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14594     {
14595       if (!DigFieldByCE(nextx, nexty, element))
14596         return MP_NO_ACTION;
14597     }
14598
14599     if (IS_SB_ELEMENT(element))
14600     {
14601       boolean sokoban_task_solved = FALSE;
14602
14603       if (element == EL_SOKOBAN_FIELD_FULL)
14604       {
14605         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14606
14607         IncrementSokobanFieldsNeeded();
14608         IncrementSokobanObjectsNeeded();
14609       }
14610
14611       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14612       {
14613         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14614
14615         DecrementSokobanFieldsNeeded();
14616         DecrementSokobanObjectsNeeded();
14617
14618         // sokoban object was pushed from empty field to sokoban field
14619         if (Back[x][y] == EL_EMPTY)
14620           sokoban_task_solved = TRUE;
14621       }
14622
14623       Tile[x][y] = EL_SOKOBAN_OBJECT;
14624
14625       if (Back[x][y] == Back[nextx][nexty])
14626         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14627       else if (Back[x][y] != 0)
14628         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14629                                     ACTION_EMPTYING);
14630       else
14631         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14632                                     ACTION_FILLING);
14633
14634       if (sokoban_task_solved &&
14635           game.sokoban_fields_still_needed == 0 &&
14636           game.sokoban_objects_still_needed == 0 &&
14637           level.auto_exit_sokoban)
14638       {
14639         game.players_still_needed = 0;
14640
14641         LevelSolved();
14642
14643         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14644       }
14645     }
14646     else
14647       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14648
14649     InitMovingField(x, y, move_direction);
14650     GfxAction[x][y] = ACTION_PUSHING;
14651
14652     if (mode == DF_SNAP)
14653       ContinueMoving(x, y);
14654     else
14655       MovPos[x][y] = (dx != 0 ? dx : dy);
14656
14657     Pushed[x][y] = TRUE;
14658     Pushed[nextx][nexty] = TRUE;
14659
14660     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14661       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14662     else
14663       player->push_delay_value = -1;    // get new value later
14664
14665     // check for element change _after_ element has been pushed
14666     if (game.use_change_when_pushing_bug)
14667     {
14668       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14669                                  player->index_bit, dig_side);
14670       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14671                                           player->index_bit, dig_side);
14672     }
14673   }
14674   else if (IS_SWITCHABLE(element))
14675   {
14676     if (PLAYER_SWITCHING(player, x, y))
14677     {
14678       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14679                                           player->index_bit, dig_side);
14680
14681       return MP_ACTION;
14682     }
14683
14684     player->is_switching = TRUE;
14685     player->switch_x = x;
14686     player->switch_y = y;
14687
14688     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14689
14690     if (element == EL_ROBOT_WHEEL)
14691     {
14692       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14693
14694       game.robot_wheel_x = x;
14695       game.robot_wheel_y = y;
14696       game.robot_wheel_active = TRUE;
14697
14698       TEST_DrawLevelField(x, y);
14699     }
14700     else if (element == EL_SP_TERMINAL)
14701     {
14702       int xx, yy;
14703
14704       SCAN_PLAYFIELD(xx, yy)
14705       {
14706         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14707         {
14708           Bang(xx, yy);
14709         }
14710         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14711         {
14712           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14713
14714           ResetGfxAnimation(xx, yy);
14715           TEST_DrawLevelField(xx, yy);
14716         }
14717       }
14718     }
14719     else if (IS_BELT_SWITCH(element))
14720     {
14721       ToggleBeltSwitch(x, y);
14722     }
14723     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14724              element == EL_SWITCHGATE_SWITCH_DOWN ||
14725              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14726              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14727     {
14728       ToggleSwitchgateSwitch();
14729     }
14730     else if (element == EL_LIGHT_SWITCH ||
14731              element == EL_LIGHT_SWITCH_ACTIVE)
14732     {
14733       ToggleLightSwitch(x, y);
14734     }
14735     else if (element == EL_TIMEGATE_SWITCH ||
14736              element == EL_DC_TIMEGATE_SWITCH)
14737     {
14738       ActivateTimegateSwitch(x, y);
14739     }
14740     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14741              element == EL_BALLOON_SWITCH_RIGHT ||
14742              element == EL_BALLOON_SWITCH_UP    ||
14743              element == EL_BALLOON_SWITCH_DOWN  ||
14744              element == EL_BALLOON_SWITCH_NONE  ||
14745              element == EL_BALLOON_SWITCH_ANY)
14746     {
14747       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14748                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14749                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14750                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14751                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14752                              move_direction);
14753     }
14754     else if (element == EL_LAMP)
14755     {
14756       Tile[x][y] = EL_LAMP_ACTIVE;
14757       game.lights_still_needed--;
14758
14759       ResetGfxAnimation(x, y);
14760       TEST_DrawLevelField(x, y);
14761     }
14762     else if (element == EL_TIME_ORB_FULL)
14763     {
14764       Tile[x][y] = EL_TIME_ORB_EMPTY;
14765
14766       if (level.time > 0 || level.use_time_orb_bug)
14767       {
14768         TimeLeft += level.time_orb_time;
14769         game.no_level_time_limit = FALSE;
14770
14771         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14772
14773         DisplayGameControlValues();
14774       }
14775
14776       ResetGfxAnimation(x, y);
14777       TEST_DrawLevelField(x, y);
14778     }
14779     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14780              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14781     {
14782       int xx, yy;
14783
14784       game.ball_active = !game.ball_active;
14785
14786       SCAN_PLAYFIELD(xx, yy)
14787       {
14788         int e = Tile[xx][yy];
14789
14790         if (game.ball_active)
14791         {
14792           if (e == EL_EMC_MAGIC_BALL)
14793             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14794           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14795             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14796         }
14797         else
14798         {
14799           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14800             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14801           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14802             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14803         }
14804       }
14805     }
14806
14807     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14808                                         player->index_bit, dig_side);
14809
14810     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14811                                         player->index_bit, dig_side);
14812
14813     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14814                                         player->index_bit, dig_side);
14815
14816     return MP_ACTION;
14817   }
14818   else
14819   {
14820     if (!PLAYER_SWITCHING(player, x, y))
14821     {
14822       player->is_switching = TRUE;
14823       player->switch_x = x;
14824       player->switch_y = y;
14825
14826       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14827                                  player->index_bit, dig_side);
14828       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14829                                           player->index_bit, dig_side);
14830
14831       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14832                                  player->index_bit, dig_side);
14833       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14834                                           player->index_bit, dig_side);
14835     }
14836
14837     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14838                                player->index_bit, dig_side);
14839     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14840                                         player->index_bit, dig_side);
14841
14842     return MP_NO_ACTION;
14843   }
14844
14845   player->push_delay = -1;
14846
14847   if (is_player)                // function can also be called by EL_PENGUIN
14848   {
14849     if (Tile[x][y] != element)          // really digged/collected something
14850     {
14851       player->is_collecting = !player->is_digging;
14852       player->is_active = TRUE;
14853
14854       player->last_removed_element = element;
14855     }
14856   }
14857
14858   return MP_MOVING;
14859 }
14860
14861 static boolean DigFieldByCE(int x, int y, int digging_element)
14862 {
14863   int element = Tile[x][y];
14864
14865   if (!IS_FREE(x, y))
14866   {
14867     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14868                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14869                   ACTION_BREAKING);
14870
14871     // no element can dig solid indestructible elements
14872     if (IS_INDESTRUCTIBLE(element) &&
14873         !IS_DIGGABLE(element) &&
14874         !IS_COLLECTIBLE(element))
14875       return FALSE;
14876
14877     if (AmoebaNr[x][y] &&
14878         (element == EL_AMOEBA_FULL ||
14879          element == EL_BD_AMOEBA ||
14880          element == EL_AMOEBA_GROWING))
14881     {
14882       AmoebaCnt[AmoebaNr[x][y]]--;
14883       AmoebaCnt2[AmoebaNr[x][y]]--;
14884     }
14885
14886     if (IS_MOVING(x, y))
14887       RemoveMovingField(x, y);
14888     else
14889     {
14890       RemoveField(x, y);
14891       TEST_DrawLevelField(x, y);
14892     }
14893
14894     // if digged element was about to explode, prevent the explosion
14895     ExplodeField[x][y] = EX_TYPE_NONE;
14896
14897     PlayLevelSoundAction(x, y, action);
14898   }
14899
14900   Store[x][y] = EL_EMPTY;
14901
14902   // this makes it possible to leave the removed element again
14903   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14904     Store[x][y] = element;
14905
14906   return TRUE;
14907 }
14908
14909 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14910 {
14911   int jx = player->jx, jy = player->jy;
14912   int x = jx + dx, y = jy + dy;
14913   int snap_direction = (dx == -1 ? MV_LEFT  :
14914                         dx == +1 ? MV_RIGHT :
14915                         dy == -1 ? MV_UP    :
14916                         dy == +1 ? MV_DOWN  : MV_NONE);
14917   boolean can_continue_snapping = (level.continuous_snapping &&
14918                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14919
14920   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14921     return FALSE;
14922
14923   if (!player->active || !IN_LEV_FIELD(x, y))
14924     return FALSE;
14925
14926   if (dx && dy)
14927     return FALSE;
14928
14929   if (!dx && !dy)
14930   {
14931     if (player->MovPos == 0)
14932       player->is_pushing = FALSE;
14933
14934     player->is_snapping = FALSE;
14935
14936     if (player->MovPos == 0)
14937     {
14938       player->is_moving = FALSE;
14939       player->is_digging = FALSE;
14940       player->is_collecting = FALSE;
14941     }
14942
14943     return FALSE;
14944   }
14945
14946   // prevent snapping with already pressed snap key when not allowed
14947   if (player->is_snapping && !can_continue_snapping)
14948     return FALSE;
14949
14950   player->MovDir = snap_direction;
14951
14952   if (player->MovPos == 0)
14953   {
14954     player->is_moving = FALSE;
14955     player->is_digging = FALSE;
14956     player->is_collecting = FALSE;
14957   }
14958
14959   player->is_dropping = FALSE;
14960   player->is_dropping_pressed = FALSE;
14961   player->drop_pressed_delay = 0;
14962
14963   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14964     return FALSE;
14965
14966   player->is_snapping = TRUE;
14967   player->is_active = TRUE;
14968
14969   if (player->MovPos == 0)
14970   {
14971     player->is_moving = FALSE;
14972     player->is_digging = FALSE;
14973     player->is_collecting = FALSE;
14974   }
14975
14976   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14977     TEST_DrawLevelField(player->last_jx, player->last_jy);
14978
14979   TEST_DrawLevelField(x, y);
14980
14981   return TRUE;
14982 }
14983
14984 static boolean DropElement(struct PlayerInfo *player)
14985 {
14986   int old_element, new_element;
14987   int dropx = player->jx, dropy = player->jy;
14988   int drop_direction = player->MovDir;
14989   int drop_side = drop_direction;
14990   int drop_element = get_next_dropped_element(player);
14991
14992   /* do not drop an element on top of another element; when holding drop key
14993      pressed without moving, dropped element must move away before the next
14994      element can be dropped (this is especially important if the next element
14995      is dynamite, which can be placed on background for historical reasons) */
14996   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14997     return MP_ACTION;
14998
14999   if (IS_THROWABLE(drop_element))
15000   {
15001     dropx += GET_DX_FROM_DIR(drop_direction);
15002     dropy += GET_DY_FROM_DIR(drop_direction);
15003
15004     if (!IN_LEV_FIELD(dropx, dropy))
15005       return FALSE;
15006   }
15007
15008   old_element = Tile[dropx][dropy];     // old element at dropping position
15009   new_element = drop_element;           // default: no change when dropping
15010
15011   // check if player is active, not moving and ready to drop
15012   if (!player->active || player->MovPos || player->drop_delay > 0)
15013     return FALSE;
15014
15015   // check if player has anything that can be dropped
15016   if (new_element == EL_UNDEFINED)
15017     return FALSE;
15018
15019   // only set if player has anything that can be dropped
15020   player->is_dropping_pressed = TRUE;
15021
15022   // check if drop key was pressed long enough for EM style dynamite
15023   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15024     return FALSE;
15025
15026   // check if anything can be dropped at the current position
15027   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15028     return FALSE;
15029
15030   // collected custom elements can only be dropped on empty fields
15031   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15032     return FALSE;
15033
15034   if (old_element != EL_EMPTY)
15035     Back[dropx][dropy] = old_element;   // store old element on this field
15036
15037   ResetGfxAnimation(dropx, dropy);
15038   ResetRandomAnimationValue(dropx, dropy);
15039
15040   if (player->inventory_size > 0 ||
15041       player->inventory_infinite_element != EL_UNDEFINED)
15042   {
15043     if (player->inventory_size > 0)
15044     {
15045       player->inventory_size--;
15046
15047       DrawGameDoorValues();
15048
15049       if (new_element == EL_DYNAMITE)
15050         new_element = EL_DYNAMITE_ACTIVE;
15051       else if (new_element == EL_EM_DYNAMITE)
15052         new_element = EL_EM_DYNAMITE_ACTIVE;
15053       else if (new_element == EL_SP_DISK_RED)
15054         new_element = EL_SP_DISK_RED_ACTIVE;
15055     }
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     // needed if previous element just changed to "empty" in the last frame
15066     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15067
15068     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15069                                player->index_bit, drop_side);
15070     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15071                                         CE_PLAYER_DROPS_X,
15072                                         player->index_bit, drop_side);
15073
15074     TestIfElementTouchesCustomElement(dropx, dropy);
15075   }
15076   else          // player is dropping a dyna bomb
15077   {
15078     player->dynabombs_left--;
15079
15080     Tile[dropx][dropy] = new_element;
15081
15082     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15083       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15084                           el2img(Tile[dropx][dropy]), 0);
15085
15086     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15087   }
15088
15089   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15090     InitField_WithBug1(dropx, dropy, FALSE);
15091
15092   new_element = Tile[dropx][dropy];     // element might have changed
15093
15094   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15095       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15096   {
15097     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15098       MovDir[dropx][dropy] = drop_direction;
15099
15100     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15101
15102     // do not cause impact style collision by dropping elements that can fall
15103     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15104   }
15105
15106   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15107   player->is_dropping = TRUE;
15108
15109   player->drop_pressed_delay = 0;
15110   player->is_dropping_pressed = FALSE;
15111
15112   player->drop_x = dropx;
15113   player->drop_y = dropy;
15114
15115   return TRUE;
15116 }
15117
15118 // ----------------------------------------------------------------------------
15119 // game sound playing functions
15120 // ----------------------------------------------------------------------------
15121
15122 static int *loop_sound_frame = NULL;
15123 static int *loop_sound_volume = NULL;
15124
15125 void InitPlayLevelSound(void)
15126 {
15127   int num_sounds = getSoundListSize();
15128
15129   checked_free(loop_sound_frame);
15130   checked_free(loop_sound_volume);
15131
15132   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15133   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15134 }
15135
15136 static void PlayLevelSound(int x, int y, int nr)
15137 {
15138   int sx = SCREENX(x), sy = SCREENY(y);
15139   int volume, stereo_position;
15140   int max_distance = 8;
15141   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15142
15143   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15144       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15145     return;
15146
15147   if (!IN_LEV_FIELD(x, y) ||
15148       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15149       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15150     return;
15151
15152   volume = SOUND_MAX_VOLUME;
15153
15154   if (!IN_SCR_FIELD(sx, sy))
15155   {
15156     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15157     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15158
15159     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15160   }
15161
15162   stereo_position = (SOUND_MAX_LEFT +
15163                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15164                      (SCR_FIELDX + 2 * max_distance));
15165
15166   if (IS_LOOP_SOUND(nr))
15167   {
15168     /* This assures that quieter loop sounds do not overwrite louder ones,
15169        while restarting sound volume comparison with each new game frame. */
15170
15171     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15172       return;
15173
15174     loop_sound_volume[nr] = volume;
15175     loop_sound_frame[nr] = FrameCounter;
15176   }
15177
15178   PlaySoundExt(nr, volume, stereo_position, type);
15179 }
15180
15181 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15182 {
15183   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15184                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15185                  y < LEVELY(BY1) ? LEVELY(BY1) :
15186                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15187                  sound_action);
15188 }
15189
15190 static void PlayLevelSoundAction(int x, int y, int action)
15191 {
15192   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15193 }
15194
15195 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15196 {
15197   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15198
15199   if (sound_effect != SND_UNDEFINED)
15200     PlayLevelSound(x, y, sound_effect);
15201 }
15202
15203 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15204                                               int action)
15205 {
15206   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15207
15208   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15209     PlayLevelSound(x, y, sound_effect);
15210 }
15211
15212 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15213 {
15214   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15215
15216   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15217     PlayLevelSound(x, y, sound_effect);
15218 }
15219
15220 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15221 {
15222   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15223
15224   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15225     StopSound(sound_effect);
15226 }
15227
15228 static int getLevelMusicNr(void)
15229 {
15230   if (levelset.music[level_nr] != MUS_UNDEFINED)
15231     return levelset.music[level_nr];            // from config file
15232   else
15233     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15234 }
15235
15236 static void FadeLevelSounds(void)
15237 {
15238   FadeSounds();
15239 }
15240
15241 static void FadeLevelMusic(void)
15242 {
15243   int music_nr = getLevelMusicNr();
15244   char *curr_music = getCurrentlyPlayingMusicFilename();
15245   char *next_music = getMusicInfoEntryFilename(music_nr);
15246
15247   if (!strEqual(curr_music, next_music))
15248     FadeMusic();
15249 }
15250
15251 void FadeLevelSoundsAndMusic(void)
15252 {
15253   FadeLevelSounds();
15254   FadeLevelMusic();
15255 }
15256
15257 static void PlayLevelMusic(void)
15258 {
15259   int music_nr = getLevelMusicNr();
15260   char *curr_music = getCurrentlyPlayingMusicFilename();
15261   char *next_music = getMusicInfoEntryFilename(music_nr);
15262
15263   if (!strEqual(curr_music, next_music))
15264     PlayMusicLoop(music_nr);
15265 }
15266
15267 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15268 {
15269   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15270   int offset = 0;
15271   int x = xx - offset;
15272   int y = yy - offset;
15273
15274   switch (sample)
15275   {
15276     case SOUND_blank:
15277       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15278       break;
15279
15280     case SOUND_roll:
15281       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15282       break;
15283
15284     case SOUND_stone:
15285       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15286       break;
15287
15288     case SOUND_nut:
15289       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15290       break;
15291
15292     case SOUND_crack:
15293       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15294       break;
15295
15296     case SOUND_bug:
15297       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15298       break;
15299
15300     case SOUND_tank:
15301       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15302       break;
15303
15304     case SOUND_android_clone:
15305       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15306       break;
15307
15308     case SOUND_android_move:
15309       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15310       break;
15311
15312     case SOUND_spring:
15313       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15314       break;
15315
15316     case SOUND_slurp:
15317       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15318       break;
15319
15320     case SOUND_eater:
15321       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15322       break;
15323
15324     case SOUND_eater_eat:
15325       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15326       break;
15327
15328     case SOUND_alien:
15329       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15330       break;
15331
15332     case SOUND_collect:
15333       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15334       break;
15335
15336     case SOUND_diamond:
15337       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15338       break;
15339
15340     case SOUND_squash:
15341       // !!! CHECK THIS !!!
15342 #if 1
15343       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15344 #else
15345       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15346 #endif
15347       break;
15348
15349     case SOUND_wonderfall:
15350       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15351       break;
15352
15353     case SOUND_drip:
15354       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15355       break;
15356
15357     case SOUND_push:
15358       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15359       break;
15360
15361     case SOUND_dirt:
15362       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15363       break;
15364
15365     case SOUND_acid:
15366       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15367       break;
15368
15369     case SOUND_ball:
15370       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15371       break;
15372
15373     case SOUND_slide:
15374       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15375       break;
15376
15377     case SOUND_wonder:
15378       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15379       break;
15380
15381     case SOUND_door:
15382       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15383       break;
15384
15385     case SOUND_exit_open:
15386       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15387       break;
15388
15389     case SOUND_exit_leave:
15390       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15391       break;
15392
15393     case SOUND_dynamite:
15394       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15395       break;
15396
15397     case SOUND_tick:
15398       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15399       break;
15400
15401     case SOUND_press:
15402       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15403       break;
15404
15405     case SOUND_wheel:
15406       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15407       break;
15408
15409     case SOUND_boom:
15410       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15411       break;
15412
15413     case SOUND_die:
15414       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15415       break;
15416
15417     case SOUND_time:
15418       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15419       break;
15420
15421     default:
15422       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15423       break;
15424   }
15425 }
15426
15427 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15428 {
15429   int element = map_element_SP_to_RND(element_sp);
15430   int action = map_action_SP_to_RND(action_sp);
15431   int offset = (setup.sp_show_border_elements ? 0 : 1);
15432   int x = xx - offset;
15433   int y = yy - offset;
15434
15435   PlayLevelSoundElementAction(x, y, element, action);
15436 }
15437
15438 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15439 {
15440   int element = map_element_MM_to_RND(element_mm);
15441   int action = map_action_MM_to_RND(action_mm);
15442   int offset = 0;
15443   int x = xx - offset;
15444   int y = yy - offset;
15445
15446   if (!IS_MM_ELEMENT(element))
15447     element = EL_MM_DEFAULT;
15448
15449   PlayLevelSoundElementAction(x, y, element, action);
15450 }
15451
15452 void PlaySound_MM(int sound_mm)
15453 {
15454   int sound = map_sound_MM_to_RND(sound_mm);
15455
15456   if (sound == SND_UNDEFINED)
15457     return;
15458
15459   PlaySound(sound);
15460 }
15461
15462 void PlaySoundLoop_MM(int sound_mm)
15463 {
15464   int sound = map_sound_MM_to_RND(sound_mm);
15465
15466   if (sound == SND_UNDEFINED)
15467     return;
15468
15469   PlaySoundLoop(sound);
15470 }
15471
15472 void StopSound_MM(int sound_mm)
15473 {
15474   int sound = map_sound_MM_to_RND(sound_mm);
15475
15476   if (sound == SND_UNDEFINED)
15477     return;
15478
15479   StopSound(sound);
15480 }
15481
15482 void RaiseScore(int value)
15483 {
15484   game.score += value;
15485
15486   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15487
15488   DisplayGameControlValues();
15489 }
15490
15491 void RaiseScoreElement(int element)
15492 {
15493   switch (element)
15494   {
15495     case EL_EMERALD:
15496     case EL_BD_DIAMOND:
15497     case EL_EMERALD_YELLOW:
15498     case EL_EMERALD_RED:
15499     case EL_EMERALD_PURPLE:
15500     case EL_SP_INFOTRON:
15501       RaiseScore(level.score[SC_EMERALD]);
15502       break;
15503     case EL_DIAMOND:
15504       RaiseScore(level.score[SC_DIAMOND]);
15505       break;
15506     case EL_CRYSTAL:
15507       RaiseScore(level.score[SC_CRYSTAL]);
15508       break;
15509     case EL_PEARL:
15510       RaiseScore(level.score[SC_PEARL]);
15511       break;
15512     case EL_BUG:
15513     case EL_BD_BUTTERFLY:
15514     case EL_SP_ELECTRON:
15515       RaiseScore(level.score[SC_BUG]);
15516       break;
15517     case EL_SPACESHIP:
15518     case EL_BD_FIREFLY:
15519     case EL_SP_SNIKSNAK:
15520       RaiseScore(level.score[SC_SPACESHIP]);
15521       break;
15522     case EL_YAMYAM:
15523     case EL_DARK_YAMYAM:
15524       RaiseScore(level.score[SC_YAMYAM]);
15525       break;
15526     case EL_ROBOT:
15527       RaiseScore(level.score[SC_ROBOT]);
15528       break;
15529     case EL_PACMAN:
15530       RaiseScore(level.score[SC_PACMAN]);
15531       break;
15532     case EL_NUT:
15533       RaiseScore(level.score[SC_NUT]);
15534       break;
15535     case EL_DYNAMITE:
15536     case EL_EM_DYNAMITE:
15537     case EL_SP_DISK_RED:
15538     case EL_DYNABOMB_INCREASE_NUMBER:
15539     case EL_DYNABOMB_INCREASE_SIZE:
15540     case EL_DYNABOMB_INCREASE_POWER:
15541       RaiseScore(level.score[SC_DYNAMITE]);
15542       break;
15543     case EL_SHIELD_NORMAL:
15544     case EL_SHIELD_DEADLY:
15545       RaiseScore(level.score[SC_SHIELD]);
15546       break;
15547     case EL_EXTRA_TIME:
15548       RaiseScore(level.extra_time_score);
15549       break;
15550     case EL_KEY_1:
15551     case EL_KEY_2:
15552     case EL_KEY_3:
15553     case EL_KEY_4:
15554     case EL_EM_KEY_1:
15555     case EL_EM_KEY_2:
15556     case EL_EM_KEY_3:
15557     case EL_EM_KEY_4:
15558     case EL_EMC_KEY_5:
15559     case EL_EMC_KEY_6:
15560     case EL_EMC_KEY_7:
15561     case EL_EMC_KEY_8:
15562     case EL_DC_KEY_WHITE:
15563       RaiseScore(level.score[SC_KEY]);
15564       break;
15565     default:
15566       RaiseScore(element_info[element].collect_score);
15567       break;
15568   }
15569 }
15570
15571 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15572 {
15573   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15574   {
15575     if (!quick_quit)
15576     {
15577       // prevent short reactivation of overlay buttons while closing door
15578       SetOverlayActive(FALSE);
15579       UnmapGameButtons();
15580
15581       // door may still be open due to skipped or envelope style request
15582       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15583     }
15584
15585     if (network.enabled)
15586       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15587     else
15588     {
15589       if (quick_quit)
15590         FadeSkipNextFadeIn();
15591
15592       SetGameStatus(GAME_MODE_MAIN);
15593
15594       DrawMainMenu();
15595     }
15596   }
15597   else          // continue playing the game
15598   {
15599     if (tape.playing && tape.deactivate_display)
15600       TapeDeactivateDisplayOff(TRUE);
15601
15602     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15603
15604     if (tape.playing && tape.deactivate_display)
15605       TapeDeactivateDisplayOn();
15606   }
15607 }
15608
15609 void RequestQuitGame(boolean escape_key_pressed)
15610 {
15611   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15612   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15613                         level_editor_test_game);
15614   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15615                           quick_quit || score_info_tape_play);
15616
15617   RequestQuitGameExt(skip_request, quick_quit,
15618                      "Do you really want to quit the game?");
15619 }
15620
15621 void RequestRestartGame(char *message)
15622 {
15623   game.restart_game_message = NULL;
15624
15625   boolean has_started_game = hasStartedNetworkGame();
15626   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15627
15628   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15629   {
15630     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15631   }
15632   else
15633   {
15634     // needed in case of envelope request to close game panel
15635     CloseDoor(DOOR_CLOSE_1);
15636
15637     SetGameStatus(GAME_MODE_MAIN);
15638
15639     DrawMainMenu();
15640   }
15641 }
15642
15643 void CheckGameOver(void)
15644 {
15645   static boolean last_game_over = FALSE;
15646   static int game_over_delay = 0;
15647   int game_over_delay_value = 50;
15648   boolean game_over = checkGameFailed();
15649
15650   // do not handle game over if request dialog is already active
15651   if (game.request_active)
15652     return;
15653
15654   // do not ask to play again if game was never actually played
15655   if (!game.GamePlayed)
15656     return;
15657
15658   if (!game_over)
15659   {
15660     last_game_over = FALSE;
15661     game_over_delay = game_over_delay_value;
15662
15663     return;
15664   }
15665
15666   if (game_over_delay > 0)
15667   {
15668     game_over_delay--;
15669
15670     return;
15671   }
15672
15673   if (last_game_over != game_over)
15674     game.restart_game_message = (hasStartedNetworkGame() ?
15675                                  "Game over! Play it again?" :
15676                                  "Game over!");
15677
15678   last_game_over = game_over;
15679 }
15680
15681 boolean checkGameSolved(void)
15682 {
15683   // set for all game engines if level was solved
15684   return game.LevelSolved_GameEnd;
15685 }
15686
15687 boolean checkGameFailed(void)
15688 {
15689   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15690     return (game_em.game_over && !game_em.level_solved);
15691   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15692     return (game_sp.game_over && !game_sp.level_solved);
15693   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15694     return (game_mm.game_over && !game_mm.level_solved);
15695   else                          // GAME_ENGINE_TYPE_RND
15696     return (game.GameOver && !game.LevelSolved);
15697 }
15698
15699 boolean checkGameEnded(void)
15700 {
15701   return (checkGameSolved() || checkGameFailed());
15702 }
15703
15704
15705 // ----------------------------------------------------------------------------
15706 // random generator functions
15707 // ----------------------------------------------------------------------------
15708
15709 unsigned int InitEngineRandom_RND(int seed)
15710 {
15711   game.num_random_calls = 0;
15712
15713   return InitEngineRandom(seed);
15714 }
15715
15716 unsigned int RND(int max)
15717 {
15718   if (max > 0)
15719   {
15720     game.num_random_calls++;
15721
15722     return GetEngineRandom(max);
15723   }
15724
15725   return 0;
15726 }
15727
15728
15729 // ----------------------------------------------------------------------------
15730 // game engine snapshot handling functions
15731 // ----------------------------------------------------------------------------
15732
15733 struct EngineSnapshotInfo
15734 {
15735   // runtime values for custom element collect score
15736   int collect_score[NUM_CUSTOM_ELEMENTS];
15737
15738   // runtime values for group element choice position
15739   int choice_pos[NUM_GROUP_ELEMENTS];
15740
15741   // runtime values for belt position animations
15742   int belt_graphic[4][NUM_BELT_PARTS];
15743   int belt_anim_mode[4][NUM_BELT_PARTS];
15744 };
15745
15746 static struct EngineSnapshotInfo engine_snapshot_rnd;
15747 static char *snapshot_level_identifier = NULL;
15748 static int snapshot_level_nr = -1;
15749
15750 static void SaveEngineSnapshotValues_RND(void)
15751 {
15752   static int belt_base_active_element[4] =
15753   {
15754     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15755     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15756     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15757     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15758   };
15759   int i, j;
15760
15761   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15762   {
15763     int element = EL_CUSTOM_START + i;
15764
15765     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15766   }
15767
15768   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15769   {
15770     int element = EL_GROUP_START + i;
15771
15772     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15773   }
15774
15775   for (i = 0; i < 4; i++)
15776   {
15777     for (j = 0; j < NUM_BELT_PARTS; j++)
15778     {
15779       int element = belt_base_active_element[i] + j;
15780       int graphic = el2img(element);
15781       int anim_mode = graphic_info[graphic].anim_mode;
15782
15783       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15784       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15785     }
15786   }
15787 }
15788
15789 static void LoadEngineSnapshotValues_RND(void)
15790 {
15791   unsigned int num_random_calls = game.num_random_calls;
15792   int i, j;
15793
15794   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15795   {
15796     int element = EL_CUSTOM_START + i;
15797
15798     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15799   }
15800
15801   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15802   {
15803     int element = EL_GROUP_START + i;
15804
15805     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15806   }
15807
15808   for (i = 0; i < 4; i++)
15809   {
15810     for (j = 0; j < NUM_BELT_PARTS; j++)
15811     {
15812       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15813       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15814
15815       graphic_info[graphic].anim_mode = anim_mode;
15816     }
15817   }
15818
15819   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15820   {
15821     InitRND(tape.random_seed);
15822     for (i = 0; i < num_random_calls; i++)
15823       RND(1);
15824   }
15825
15826   if (game.num_random_calls != num_random_calls)
15827   {
15828     Error("number of random calls out of sync");
15829     Error("number of random calls should be %d", num_random_calls);
15830     Error("number of random calls is %d", game.num_random_calls);
15831
15832     Fail("this should not happen -- please debug");
15833   }
15834 }
15835
15836 void FreeEngineSnapshotSingle(void)
15837 {
15838   FreeSnapshotSingle();
15839
15840   setString(&snapshot_level_identifier, NULL);
15841   snapshot_level_nr = -1;
15842 }
15843
15844 void FreeEngineSnapshotList(void)
15845 {
15846   FreeSnapshotList();
15847 }
15848
15849 static ListNode *SaveEngineSnapshotBuffers(void)
15850 {
15851   ListNode *buffers = NULL;
15852
15853   // copy some special values to a structure better suited for the snapshot
15854
15855   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15856     SaveEngineSnapshotValues_RND();
15857   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15858     SaveEngineSnapshotValues_EM();
15859   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15860     SaveEngineSnapshotValues_SP(&buffers);
15861   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15862     SaveEngineSnapshotValues_MM();
15863
15864   // save values stored in special snapshot structure
15865
15866   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15867     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15868   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15869     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15870   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15871     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15872   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15873     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15874
15875   // save further RND engine values
15876
15877   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15878   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15879   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15880
15881   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15882   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15883   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15884   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15885   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15886
15887   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15888   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15889   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15890
15891   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15892
15893   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15894   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15895
15896   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15897   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15898   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15899   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15900   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15901   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15902   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15903   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15904   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15905   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15906   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15907   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15908   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15909   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15910   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15911   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15912   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15913   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15914
15915   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15916   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15917
15918   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15919   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15920   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15921
15922   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15923   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15924
15925   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15926   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15927   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15928   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15929   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15930   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15931
15932   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15933   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15934
15935 #if 0
15936   ListNode *node = engine_snapshot_list_rnd;
15937   int num_bytes = 0;
15938
15939   while (node != NULL)
15940   {
15941     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15942
15943     node = node->next;
15944   }
15945
15946   Debug("game:playing:SaveEngineSnapshotBuffers",
15947         "size of engine snapshot: %d bytes", num_bytes);
15948 #endif
15949
15950   return buffers;
15951 }
15952
15953 void SaveEngineSnapshotSingle(void)
15954 {
15955   ListNode *buffers = SaveEngineSnapshotBuffers();
15956
15957   // finally save all snapshot buffers to single snapshot
15958   SaveSnapshotSingle(buffers);
15959
15960   // save level identification information
15961   setString(&snapshot_level_identifier, leveldir_current->identifier);
15962   snapshot_level_nr = level_nr;
15963 }
15964
15965 boolean CheckSaveEngineSnapshotToList(void)
15966 {
15967   boolean save_snapshot =
15968     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15969      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15970       game.snapshot.changed_action) ||
15971      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15972       game.snapshot.collected_item));
15973
15974   game.snapshot.changed_action = FALSE;
15975   game.snapshot.collected_item = FALSE;
15976   game.snapshot.save_snapshot = save_snapshot;
15977
15978   return save_snapshot;
15979 }
15980
15981 void SaveEngineSnapshotToList(void)
15982 {
15983   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15984       tape.quick_resume)
15985     return;
15986
15987   ListNode *buffers = SaveEngineSnapshotBuffers();
15988
15989   // finally save all snapshot buffers to snapshot list
15990   SaveSnapshotToList(buffers);
15991 }
15992
15993 void SaveEngineSnapshotToListInitial(void)
15994 {
15995   FreeEngineSnapshotList();
15996
15997   SaveEngineSnapshotToList();
15998 }
15999
16000 static void LoadEngineSnapshotValues(void)
16001 {
16002   // restore special values from snapshot structure
16003
16004   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16005     LoadEngineSnapshotValues_RND();
16006   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16007     LoadEngineSnapshotValues_EM();
16008   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16009     LoadEngineSnapshotValues_SP();
16010   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16011     LoadEngineSnapshotValues_MM();
16012 }
16013
16014 void LoadEngineSnapshotSingle(void)
16015 {
16016   LoadSnapshotSingle();
16017
16018   LoadEngineSnapshotValues();
16019 }
16020
16021 static void LoadEngineSnapshot_Undo(int steps)
16022 {
16023   LoadSnapshotFromList_Older(steps);
16024
16025   LoadEngineSnapshotValues();
16026 }
16027
16028 static void LoadEngineSnapshot_Redo(int steps)
16029 {
16030   LoadSnapshotFromList_Newer(steps);
16031
16032   LoadEngineSnapshotValues();
16033 }
16034
16035 boolean CheckEngineSnapshotSingle(void)
16036 {
16037   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16038           snapshot_level_nr == level_nr);
16039 }
16040
16041 boolean CheckEngineSnapshotList(void)
16042 {
16043   return CheckSnapshotList();
16044 }
16045
16046
16047 // ---------- new game button stuff -------------------------------------------
16048
16049 static struct
16050 {
16051   int graphic;
16052   struct XY *pos;
16053   int gadget_id;
16054   boolean *setup_value;
16055   boolean allowed_on_tape;
16056   boolean is_touch_button;
16057   char *infotext;
16058 } gamebutton_info[NUM_GAME_BUTTONS] =
16059 {
16060   {
16061     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16062     GAME_CTRL_ID_STOP,                          NULL,
16063     TRUE, FALSE,                                "stop game"
16064   },
16065   {
16066     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16067     GAME_CTRL_ID_PAUSE,                         NULL,
16068     TRUE, FALSE,                                "pause game"
16069   },
16070   {
16071     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16072     GAME_CTRL_ID_PLAY,                          NULL,
16073     TRUE, FALSE,                                "play game"
16074   },
16075   {
16076     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16077     GAME_CTRL_ID_UNDO,                          NULL,
16078     TRUE, FALSE,                                "undo step"
16079   },
16080   {
16081     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16082     GAME_CTRL_ID_REDO,                          NULL,
16083     TRUE, FALSE,                                "redo step"
16084   },
16085   {
16086     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16087     GAME_CTRL_ID_SAVE,                          NULL,
16088     TRUE, FALSE,                                "save game"
16089   },
16090   {
16091     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16092     GAME_CTRL_ID_PAUSE2,                        NULL,
16093     TRUE, FALSE,                                "pause game"
16094   },
16095   {
16096     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16097     GAME_CTRL_ID_LOAD,                          NULL,
16098     TRUE, FALSE,                                "load game"
16099   },
16100   {
16101     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16102     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16103     FALSE, FALSE,                               "stop game"
16104   },
16105   {
16106     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16107     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16108     FALSE, FALSE,                               "pause game"
16109   },
16110   {
16111     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16112     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16113     FALSE, FALSE,                               "play game"
16114   },
16115   {
16116     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16117     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16118     FALSE, TRUE,                                "stop game"
16119   },
16120   {
16121     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16122     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16123     FALSE, TRUE,                                "pause game"
16124   },
16125   {
16126     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16127     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16128     TRUE, FALSE,                                "background music on/off"
16129   },
16130   {
16131     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16132     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16133     TRUE, FALSE,                                "sound loops on/off"
16134   },
16135   {
16136     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16137     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16138     TRUE, FALSE,                                "normal sounds on/off"
16139   },
16140   {
16141     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16142     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16143     FALSE, FALSE,                               "background music on/off"
16144   },
16145   {
16146     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16147     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16148     FALSE, FALSE,                               "sound loops on/off"
16149   },
16150   {
16151     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16152     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16153     FALSE, FALSE,                               "normal sounds on/off"
16154   }
16155 };
16156
16157 void CreateGameButtons(void)
16158 {
16159   int i;
16160
16161   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16162   {
16163     int graphic = gamebutton_info[i].graphic;
16164     struct GraphicInfo *gfx = &graphic_info[graphic];
16165     struct XY *pos = gamebutton_info[i].pos;
16166     struct GadgetInfo *gi;
16167     int button_type;
16168     boolean checked;
16169     unsigned int event_mask;
16170     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16171     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16172     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16173     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16174     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16175     int gd_x   = gfx->src_x;
16176     int gd_y   = gfx->src_y;
16177     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16178     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16179     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16180     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16181     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16182     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16183     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16184     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16185     int id = i;
16186
16187     // do not use touch buttons if overlay touch buttons are disabled
16188     if (is_touch_button && !setup.touch.overlay_buttons)
16189       continue;
16190
16191     if (gfx->bitmap == NULL)
16192     {
16193       game_gadget[id] = NULL;
16194
16195       continue;
16196     }
16197
16198     if (id == GAME_CTRL_ID_STOP ||
16199         id == GAME_CTRL_ID_PANEL_STOP ||
16200         id == GAME_CTRL_ID_TOUCH_STOP ||
16201         id == GAME_CTRL_ID_PLAY ||
16202         id == GAME_CTRL_ID_PANEL_PLAY ||
16203         id == GAME_CTRL_ID_SAVE ||
16204         id == GAME_CTRL_ID_LOAD)
16205     {
16206       button_type = GD_TYPE_NORMAL_BUTTON;
16207       checked = FALSE;
16208       event_mask = GD_EVENT_RELEASED;
16209     }
16210     else if (id == GAME_CTRL_ID_UNDO ||
16211              id == GAME_CTRL_ID_REDO)
16212     {
16213       button_type = GD_TYPE_NORMAL_BUTTON;
16214       checked = FALSE;
16215       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16216     }
16217     else
16218     {
16219       button_type = GD_TYPE_CHECK_BUTTON;
16220       checked = (gamebutton_info[i].setup_value != NULL ?
16221                  *gamebutton_info[i].setup_value : FALSE);
16222       event_mask = GD_EVENT_PRESSED;
16223     }
16224
16225     gi = CreateGadget(GDI_CUSTOM_ID, id,
16226                       GDI_IMAGE_ID, graphic,
16227                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16228                       GDI_X, base_x + x,
16229                       GDI_Y, base_y + y,
16230                       GDI_WIDTH, gfx->width,
16231                       GDI_HEIGHT, gfx->height,
16232                       GDI_TYPE, button_type,
16233                       GDI_STATE, GD_BUTTON_UNPRESSED,
16234                       GDI_CHECKED, checked,
16235                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16236                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16237                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16238                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16239                       GDI_DIRECT_DRAW, FALSE,
16240                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16241                       GDI_EVENT_MASK, event_mask,
16242                       GDI_CALLBACK_ACTION, HandleGameButtons,
16243                       GDI_END);
16244
16245     if (gi == NULL)
16246       Fail("cannot create gadget");
16247
16248     game_gadget[id] = gi;
16249   }
16250 }
16251
16252 void FreeGameButtons(void)
16253 {
16254   int i;
16255
16256   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16257     FreeGadget(game_gadget[i]);
16258 }
16259
16260 static void UnmapGameButtonsAtSamePosition(int id)
16261 {
16262   int i;
16263
16264   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16265     if (i != id &&
16266         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16267         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16268       UnmapGadget(game_gadget[i]);
16269 }
16270
16271 static void UnmapGameButtonsAtSamePosition_All(void)
16272 {
16273   if (setup.show_load_save_buttons)
16274   {
16275     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16276     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16277     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16278   }
16279   else if (setup.show_undo_redo_buttons)
16280   {
16281     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16282     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16283     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16284   }
16285   else
16286   {
16287     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16288     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16289     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16290
16291     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16292     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16293     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16294   }
16295 }
16296
16297 void MapLoadSaveButtons(void)
16298 {
16299   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16300   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16301
16302   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16303   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16304 }
16305
16306 void MapUndoRedoButtons(void)
16307 {
16308   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16309   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16310
16311   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16312   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16313 }
16314
16315 void ModifyPauseButtons(void)
16316 {
16317   static int ids[] =
16318   {
16319     GAME_CTRL_ID_PAUSE,
16320     GAME_CTRL_ID_PAUSE2,
16321     GAME_CTRL_ID_PANEL_PAUSE,
16322     GAME_CTRL_ID_TOUCH_PAUSE,
16323     -1
16324   };
16325   int i;
16326
16327   for (i = 0; ids[i] > -1; i++)
16328     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16329 }
16330
16331 static void MapGameButtonsExt(boolean on_tape)
16332 {
16333   int i;
16334
16335   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16336   {
16337     if ((i == GAME_CTRL_ID_UNDO ||
16338          i == GAME_CTRL_ID_REDO) &&
16339         game_status != GAME_MODE_PLAYING)
16340       continue;
16341
16342     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16343       MapGadget(game_gadget[i]);
16344   }
16345
16346   UnmapGameButtonsAtSamePosition_All();
16347
16348   RedrawGameButtons();
16349 }
16350
16351 static void UnmapGameButtonsExt(boolean on_tape)
16352 {
16353   int i;
16354
16355   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16356     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16357       UnmapGadget(game_gadget[i]);
16358 }
16359
16360 static void RedrawGameButtonsExt(boolean on_tape)
16361 {
16362   int i;
16363
16364   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16365     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16366       RedrawGadget(game_gadget[i]);
16367 }
16368
16369 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16370 {
16371   if (gi == NULL)
16372     return;
16373
16374   gi->checked = state;
16375 }
16376
16377 static void RedrawSoundButtonGadget(int id)
16378 {
16379   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16380              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16381              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16382              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16383              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16384              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16385              id);
16386
16387   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16388   RedrawGadget(game_gadget[id2]);
16389 }
16390
16391 void MapGameButtons(void)
16392 {
16393   MapGameButtonsExt(FALSE);
16394 }
16395
16396 void UnmapGameButtons(void)
16397 {
16398   UnmapGameButtonsExt(FALSE);
16399 }
16400
16401 void RedrawGameButtons(void)
16402 {
16403   RedrawGameButtonsExt(FALSE);
16404 }
16405
16406 void MapGameButtonsOnTape(void)
16407 {
16408   MapGameButtonsExt(TRUE);
16409 }
16410
16411 void UnmapGameButtonsOnTape(void)
16412 {
16413   UnmapGameButtonsExt(TRUE);
16414 }
16415
16416 void RedrawGameButtonsOnTape(void)
16417 {
16418   RedrawGameButtonsExt(TRUE);
16419 }
16420
16421 static void GameUndoRedoExt(void)
16422 {
16423   ClearPlayerAction();
16424
16425   tape.pausing = TRUE;
16426
16427   RedrawPlayfield();
16428   UpdateAndDisplayGameControlValues();
16429
16430   DrawCompleteVideoDisplay();
16431   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16432   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16433   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16434
16435   ModifyPauseButtons();
16436
16437   BackToFront();
16438 }
16439
16440 static void GameUndo(int steps)
16441 {
16442   if (!CheckEngineSnapshotList())
16443     return;
16444
16445   int tape_property_bits = tape.property_bits;
16446
16447   LoadEngineSnapshot_Undo(steps);
16448
16449   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16450
16451   GameUndoRedoExt();
16452 }
16453
16454 static void GameRedo(int steps)
16455 {
16456   if (!CheckEngineSnapshotList())
16457     return;
16458
16459   int tape_property_bits = tape.property_bits;
16460
16461   LoadEngineSnapshot_Redo(steps);
16462
16463   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16464
16465   GameUndoRedoExt();
16466 }
16467
16468 static void HandleGameButtonsExt(int id, int button)
16469 {
16470   static boolean game_undo_executed = FALSE;
16471   int steps = BUTTON_STEPSIZE(button);
16472   boolean handle_game_buttons =
16473     (game_status == GAME_MODE_PLAYING ||
16474      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16475
16476   if (!handle_game_buttons)
16477     return;
16478
16479   switch (id)
16480   {
16481     case GAME_CTRL_ID_STOP:
16482     case GAME_CTRL_ID_PANEL_STOP:
16483     case GAME_CTRL_ID_TOUCH_STOP:
16484       TapeStopGame();
16485
16486       break;
16487
16488     case GAME_CTRL_ID_PAUSE:
16489     case GAME_CTRL_ID_PAUSE2:
16490     case GAME_CTRL_ID_PANEL_PAUSE:
16491     case GAME_CTRL_ID_TOUCH_PAUSE:
16492       if (network.enabled && game_status == GAME_MODE_PLAYING)
16493       {
16494         if (tape.pausing)
16495           SendToServer_ContinuePlaying();
16496         else
16497           SendToServer_PausePlaying();
16498       }
16499       else
16500         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16501
16502       game_undo_executed = FALSE;
16503
16504       break;
16505
16506     case GAME_CTRL_ID_PLAY:
16507     case GAME_CTRL_ID_PANEL_PLAY:
16508       if (game_status == GAME_MODE_MAIN)
16509       {
16510         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16511       }
16512       else if (tape.pausing)
16513       {
16514         if (network.enabled)
16515           SendToServer_ContinuePlaying();
16516         else
16517           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16518       }
16519       break;
16520
16521     case GAME_CTRL_ID_UNDO:
16522       // Important: When using "save snapshot when collecting an item" mode,
16523       // load last (current) snapshot for first "undo" after pressing "pause"
16524       // (else the last-but-one snapshot would be loaded, because the snapshot
16525       // pointer already points to the last snapshot when pressing "pause",
16526       // which is fine for "every step/move" mode, but not for "every collect")
16527       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16528           !game_undo_executed)
16529         steps--;
16530
16531       game_undo_executed = TRUE;
16532
16533       GameUndo(steps);
16534       break;
16535
16536     case GAME_CTRL_ID_REDO:
16537       GameRedo(steps);
16538       break;
16539
16540     case GAME_CTRL_ID_SAVE:
16541       TapeQuickSave();
16542       break;
16543
16544     case GAME_CTRL_ID_LOAD:
16545       TapeQuickLoad();
16546       break;
16547
16548     case SOUND_CTRL_ID_MUSIC:
16549     case SOUND_CTRL_ID_PANEL_MUSIC:
16550       if (setup.sound_music)
16551       { 
16552         setup.sound_music = FALSE;
16553
16554         FadeMusic();
16555       }
16556       else if (audio.music_available)
16557       { 
16558         setup.sound = setup.sound_music = TRUE;
16559
16560         SetAudioMode(setup.sound);
16561
16562         if (game_status == GAME_MODE_PLAYING)
16563           PlayLevelMusic();
16564       }
16565
16566       RedrawSoundButtonGadget(id);
16567
16568       break;
16569
16570     case SOUND_CTRL_ID_LOOPS:
16571     case SOUND_CTRL_ID_PANEL_LOOPS:
16572       if (setup.sound_loops)
16573         setup.sound_loops = FALSE;
16574       else if (audio.loops_available)
16575       {
16576         setup.sound = setup.sound_loops = TRUE;
16577
16578         SetAudioMode(setup.sound);
16579       }
16580
16581       RedrawSoundButtonGadget(id);
16582
16583       break;
16584
16585     case SOUND_CTRL_ID_SIMPLE:
16586     case SOUND_CTRL_ID_PANEL_SIMPLE:
16587       if (setup.sound_simple)
16588         setup.sound_simple = FALSE;
16589       else if (audio.sound_available)
16590       {
16591         setup.sound = setup.sound_simple = TRUE;
16592
16593         SetAudioMode(setup.sound);
16594       }
16595
16596       RedrawSoundButtonGadget(id);
16597
16598       break;
16599
16600     default:
16601       break;
16602   }
16603 }
16604
16605 static void HandleGameButtons(struct GadgetInfo *gi)
16606 {
16607   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16608 }
16609
16610 void HandleSoundButtonKeys(Key key)
16611 {
16612   if (key == setup.shortcut.sound_simple)
16613     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16614   else if (key == setup.shortcut.sound_loops)
16615     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16616   else if (key == setup.shortcut.sound_music)
16617     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16618 }