moved flag for unique score entry per player name to function parameter
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void TestIfPlayerTouchesCustomElement(int, int);
1062 static void TestIfElementTouchesCustomElement(int, int);
1063 static void TestIfElementHitsCustomElement(int, int, int);
1064
1065 static void HandleElementChange(int, int, int);
1066 static void ExecuteCustomElementAction(int, int, int, int);
1067 static boolean ChangeElement(int, int, int, int);
1068
1069 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1070 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1072 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1073         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1074 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1075         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1076 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1077         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1078 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1079         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1080
1081 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1082 #define CheckElementChange(x, y, e, te, ev)                             \
1083         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1084 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1085         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1086 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1087         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1088 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1089         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1090
1091 static void PlayLevelSound(int, int, int);
1092 static void PlayLevelSoundNearest(int, int, int);
1093 static void PlayLevelSoundAction(int, int, int);
1094 static void PlayLevelSoundElementAction(int, int, int, int);
1095 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1096 static void PlayLevelSoundActionIfLoop(int, int, int);
1097 static void StopLevelSoundActionIfLoop(int, int, int);
1098 static void PlayLevelMusic(void);
1099 static void FadeLevelSoundsAndMusic(void);
1100
1101 static void HandleGameButtons(struct GadgetInfo *);
1102
1103 int AmoebaNeighbourNr(int, int);
1104 void AmoebaToDiamond(int, int);
1105 void ContinueMoving(int, int);
1106 void Bang(int, int);
1107 void InitMovDir(int, int);
1108 void InitAmoebaNr(int, int);
1109 void NewHighScore(int);
1110
1111 void TestIfGoodThingHitsBadThing(int, int, int);
1112 void TestIfBadThingHitsGoodThing(int, int, int);
1113 void TestIfPlayerTouchesBadThing(int, int);
1114 void TestIfPlayerRunsIntoBadThing(int, int, int);
1115 void TestIfBadThingTouchesPlayer(int, int);
1116 void TestIfBadThingRunsIntoPlayer(int, int, int);
1117 void TestIfFriendTouchesBadThing(int, int);
1118 void TestIfBadThingTouchesFriend(int, int);
1119 void TestIfBadThingTouchesOtherBadThing(int, int);
1120 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1121
1122 void KillPlayer(struct PlayerInfo *);
1123 void BuryPlayer(struct PlayerInfo *);
1124 void RemovePlayer(struct PlayerInfo *);
1125 void ExitPlayer(struct PlayerInfo *);
1126
1127 static int getInvisibleActiveFromInvisibleElement(int);
1128 static int getInvisibleFromInvisibleActiveElement(int);
1129
1130 static void TestFieldAfterSnapping(int, int, int, int, int);
1131
1132 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1133
1134 // for detection of endless loops, caused by custom element programming
1135 // (using maximal playfield width x 10 is just a rough approximation)
1136 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1137
1138 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1139 {                                                                       \
1140   if (recursion_loop_detected)                                          \
1141     return (rc);                                                        \
1142                                                                         \
1143   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1144   {                                                                     \
1145     recursion_loop_detected = TRUE;                                     \
1146     recursion_loop_element = (e);                                       \
1147   }                                                                     \
1148                                                                         \
1149   recursion_loop_depth++;                                               \
1150 }
1151
1152 #define RECURSION_LOOP_DETECTION_END()                                  \
1153 {                                                                       \
1154   recursion_loop_depth--;                                               \
1155 }
1156
1157 static int recursion_loop_depth;
1158 static boolean recursion_loop_detected;
1159 static boolean recursion_loop_element;
1160
1161 static int map_player_action[MAX_PLAYERS];
1162
1163
1164 // ----------------------------------------------------------------------------
1165 // definition of elements that automatically change to other elements after
1166 // a specified time, eventually calling a function when changing
1167 // ----------------------------------------------------------------------------
1168
1169 // forward declaration for changer functions
1170 static void InitBuggyBase(int, int);
1171 static void WarnBuggyBase(int, int);
1172
1173 static void InitTrap(int, int);
1174 static void ActivateTrap(int, int);
1175 static void ChangeActiveTrap(int, int);
1176
1177 static void InitRobotWheel(int, int);
1178 static void RunRobotWheel(int, int);
1179 static void StopRobotWheel(int, int);
1180
1181 static void InitTimegateWheel(int, int);
1182 static void RunTimegateWheel(int, int);
1183
1184 static void InitMagicBallDelay(int, int);
1185 static void ActivateMagicBall(int, int);
1186
1187 struct ChangingElementInfo
1188 {
1189   int element;
1190   int target_element;
1191   int change_delay;
1192   void (*pre_change_function)(int x, int y);
1193   void (*change_function)(int x, int y);
1194   void (*post_change_function)(int x, int y);
1195 };
1196
1197 static struct ChangingElementInfo change_delay_list[] =
1198 {
1199   {
1200     EL_NUT_BREAKING,
1201     EL_EMERALD,
1202     6,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_PEARL_BREAKING,
1209     EL_EMPTY,
1210     8,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_OPENING,
1217     EL_EXIT_OPEN,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_EXIT_CLOSING,
1225     EL_EXIT_CLOSED,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_OPENING,
1233     EL_STEEL_EXIT_OPEN,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_STEEL_EXIT_CLOSING,
1241     EL_STEEL_EXIT_CLOSED,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_OPENING,
1249     EL_EM_EXIT_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_EXIT_CLOSING,
1257     EL_EMPTY,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_OPENING,
1265     EL_EM_STEEL_EXIT_OPEN,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_STEEL_EXIT_CLOSING,
1273     EL_STEELWALL,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_OPENING,
1281     EL_SP_EXIT_OPEN,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_EXIT_CLOSING,
1289     EL_SP_EXIT_CLOSED,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_OPENING,
1297     EL_SWITCHGATE_OPEN,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SWITCHGATE_CLOSING,
1305     EL_SWITCHGATE_CLOSED,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_OPENING,
1313     EL_TIMEGATE_OPEN,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_TIMEGATE_CLOSING,
1321     EL_TIMEGATE_CLOSED,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327
1328   {
1329     EL_ACID_SPLASH_LEFT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_ACID_SPLASH_RIGHT,
1338     EL_EMPTY,
1339     8,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE,
1346     EL_SP_BUGGY_BASE_ACTIVATING,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVATING,
1354     EL_SP_BUGGY_BASE_ACTIVE,
1355     0,
1356     InitBuggyBase,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_SP_BUGGY_BASE_ACTIVE,
1362     EL_SP_BUGGY_BASE,
1363     0,
1364     InitBuggyBase,
1365     WarnBuggyBase,
1366     NULL
1367   },
1368   {
1369     EL_TRAP,
1370     EL_TRAP_ACTIVE,
1371     0,
1372     InitTrap,
1373     NULL,
1374     ActivateTrap
1375   },
1376   {
1377     EL_TRAP_ACTIVE,
1378     EL_TRAP,
1379     31,
1380     NULL,
1381     ChangeActiveTrap,
1382     NULL
1383   },
1384   {
1385     EL_ROBOT_WHEEL_ACTIVE,
1386     EL_ROBOT_WHEEL,
1387     0,
1388     InitRobotWheel,
1389     RunRobotWheel,
1390     StopRobotWheel
1391   },
1392   {
1393     EL_TIMEGATE_SWITCH_ACTIVE,
1394     EL_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1402     EL_DC_TIMEGATE_SWITCH,
1403     0,
1404     InitTimegateWheel,
1405     RunTimegateWheel,
1406     NULL
1407   },
1408   {
1409     EL_EMC_MAGIC_BALL_ACTIVE,
1410     EL_EMC_MAGIC_BALL_ACTIVE,
1411     0,
1412     InitMagicBallDelay,
1413     NULL,
1414     ActivateMagicBall
1415   },
1416   {
1417     EL_EMC_SPRING_BUMPER_ACTIVE,
1418     EL_EMC_SPRING_BUMPER,
1419     8,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_SHRINKING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL
1431   },
1432   {
1433     EL_DIAGONAL_GROWING,
1434     EL_UNDEFINED,
1435     0,
1436     NULL,
1437     NULL,
1438     NULL,
1439   },
1440
1441   {
1442     EL_UNDEFINED,
1443     EL_UNDEFINED,
1444     -1,
1445     NULL,
1446     NULL,
1447     NULL
1448   }
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int push_delay_fixed, push_delay_random;
1455 }
1456 push_delay_list[] =
1457 {
1458   { EL_SPRING,                  0, 0 },
1459   { EL_BALLOON,                 0, 0 },
1460
1461   { EL_SOKOBAN_OBJECT,          2, 0 },
1462   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1463   { EL_SATELLITE,               2, 0 },
1464   { EL_SP_DISK_YELLOW,          2, 0 },
1465
1466   { EL_UNDEFINED,               0, 0 },
1467 };
1468
1469 struct
1470 {
1471   int element;
1472   int move_stepsize;
1473 }
1474 move_stepsize_list[] =
1475 {
1476   { EL_AMOEBA_DROP,             2 },
1477   { EL_AMOEBA_DROPPING,         2 },
1478   { EL_QUICKSAND_FILLING,       1 },
1479   { EL_QUICKSAND_EMPTYING,      1 },
1480   { EL_QUICKSAND_FAST_FILLING,  2 },
1481   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1482   { EL_MAGIC_WALL_FILLING,      2 },
1483   { EL_MAGIC_WALL_EMPTYING,     2 },
1484   { EL_BD_MAGIC_WALL_FILLING,   2 },
1485   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1486   { EL_DC_MAGIC_WALL_FILLING,   2 },
1487   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1488
1489   { EL_UNDEFINED,               0 },
1490 };
1491
1492 struct
1493 {
1494   int element;
1495   int count;
1496 }
1497 collect_count_list[] =
1498 {
1499   { EL_EMERALD,                 1 },
1500   { EL_BD_DIAMOND,              1 },
1501   { EL_EMERALD_YELLOW,          1 },
1502   { EL_EMERALD_RED,             1 },
1503   { EL_EMERALD_PURPLE,          1 },
1504   { EL_DIAMOND,                 3 },
1505   { EL_SP_INFOTRON,             1 },
1506   { EL_PEARL,                   5 },
1507   { EL_CRYSTAL,                 8 },
1508
1509   { EL_UNDEFINED,               0 },
1510 };
1511
1512 struct
1513 {
1514   int element;
1515   int direction;
1516 }
1517 access_direction_list[] =
1518 {
1519   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1521   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1522   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1525   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1526   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1527   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1528   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1529   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1530
1531   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1532   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1533   { EL_SP_PORT_UP,                                                   MV_DOWN },
1534   { EL_SP_PORT_DOWN,                                         MV_UP           },
1535   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1536   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1537   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1538   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1539   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1540   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1542   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1543   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1544   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1545   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1546   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1547   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1548   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1549   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1550
1551   { EL_UNDEFINED,                       MV_NONE                              }
1552 };
1553
1554 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1555
1556 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1557 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1558 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1559                                  IS_JUST_CHANGING(x, y))
1560
1561 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1562
1563 // static variables for playfield scan mode (scanning forward or backward)
1564 static int playfield_scan_start_x = 0;
1565 static int playfield_scan_start_y = 0;
1566 static int playfield_scan_delta_x = 1;
1567 static int playfield_scan_delta_y = 1;
1568
1569 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1570                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1571                                      (y) += playfield_scan_delta_y)     \
1572                                 for ((x) = playfield_scan_start_x;      \
1573                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1574                                      (x) += playfield_scan_delta_x)
1575
1576 #ifdef DEBUG
1577 void DEBUG_SetMaximumDynamite(void)
1578 {
1579   int i;
1580
1581   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1582     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1583       local_player->inventory_element[local_player->inventory_size++] =
1584         EL_DYNAMITE;
1585 }
1586 #endif
1587
1588 static void InitPlayfieldScanModeVars(void)
1589 {
1590   if (game.use_reverse_scan_direction)
1591   {
1592     playfield_scan_start_x = lev_fieldx - 1;
1593     playfield_scan_start_y = lev_fieldy - 1;
1594
1595     playfield_scan_delta_x = -1;
1596     playfield_scan_delta_y = -1;
1597   }
1598   else
1599   {
1600     playfield_scan_start_x = 0;
1601     playfield_scan_start_y = 0;
1602
1603     playfield_scan_delta_x = 1;
1604     playfield_scan_delta_y = 1;
1605   }
1606 }
1607
1608 static void InitPlayfieldScanMode(int mode)
1609 {
1610   game.use_reverse_scan_direction =
1611     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1612
1613   InitPlayfieldScanModeVars();
1614 }
1615
1616 static int get_move_delay_from_stepsize(int move_stepsize)
1617 {
1618   move_stepsize =
1619     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1620
1621   // make sure that stepsize value is always a power of 2
1622   move_stepsize = (1 << log_2(move_stepsize));
1623
1624   return TILEX / move_stepsize;
1625 }
1626
1627 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1628                                boolean init_game)
1629 {
1630   int player_nr = player->index_nr;
1631   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1632   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1633
1634   // do no immediately change move delay -- the player might just be moving
1635   player->move_delay_value_next = move_delay;
1636
1637   // information if player can move must be set separately
1638   player->cannot_move = cannot_move;
1639
1640   if (init_game)
1641   {
1642     player->move_delay       = game.initial_move_delay[player_nr];
1643     player->move_delay_value = game.initial_move_delay_value[player_nr];
1644
1645     player->move_delay_value_next = -1;
1646
1647     player->move_delay_reset_counter = 0;
1648   }
1649 }
1650
1651 void GetPlayerConfig(void)
1652 {
1653   GameFrameDelay = setup.game_frame_delay;
1654
1655   if (!audio.sound_available)
1656     setup.sound_simple = FALSE;
1657
1658   if (!audio.loops_available)
1659     setup.sound_loops = FALSE;
1660
1661   if (!audio.music_available)
1662     setup.sound_music = FALSE;
1663
1664   if (!video.fullscreen_available)
1665     setup.fullscreen = FALSE;
1666
1667   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1668
1669   SetAudioMode(setup.sound);
1670 }
1671
1672 int GetElementFromGroupElement(int element)
1673 {
1674   if (IS_GROUP_ELEMENT(element))
1675   {
1676     struct ElementGroupInfo *group = element_info[element].group;
1677     int last_anim_random_frame = gfx.anim_random_frame;
1678     int element_pos;
1679
1680     if (group->choice_mode == ANIM_RANDOM)
1681       gfx.anim_random_frame = RND(group->num_elements_resolved);
1682
1683     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1684                                     group->choice_mode, 0,
1685                                     group->choice_pos);
1686
1687     if (group->choice_mode == ANIM_RANDOM)
1688       gfx.anim_random_frame = last_anim_random_frame;
1689
1690     group->choice_pos++;
1691
1692     element = group->element_resolved[element_pos];
1693   }
1694
1695   return element;
1696 }
1697
1698 static void IncrementSokobanFieldsNeeded(void)
1699 {
1700   if (level.sb_fields_needed)
1701     game.sokoban_fields_still_needed++;
1702 }
1703
1704 static void IncrementSokobanObjectsNeeded(void)
1705 {
1706   if (level.sb_objects_needed)
1707     game.sokoban_objects_still_needed++;
1708 }
1709
1710 static void DecrementSokobanFieldsNeeded(void)
1711 {
1712   if (game.sokoban_fields_still_needed > 0)
1713     game.sokoban_fields_still_needed--;
1714 }
1715
1716 static void DecrementSokobanObjectsNeeded(void)
1717 {
1718   if (game.sokoban_objects_still_needed > 0)
1719     game.sokoban_objects_still_needed--;
1720 }
1721
1722 static void InitPlayerField(int x, int y, int element, boolean init_game)
1723 {
1724   if (element == EL_SP_MURPHY)
1725   {
1726     if (init_game)
1727     {
1728       if (stored_player[0].present)
1729       {
1730         Tile[x][y] = EL_SP_MURPHY_CLONE;
1731
1732         return;
1733       }
1734       else
1735       {
1736         stored_player[0].initial_element = element;
1737         stored_player[0].use_murphy = TRUE;
1738
1739         if (!level.use_artwork_element[0])
1740           stored_player[0].artwork_element = EL_SP_MURPHY;
1741       }
1742
1743       Tile[x][y] = EL_PLAYER_1;
1744     }
1745   }
1746
1747   if (init_game)
1748   {
1749     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1750     int jx = player->jx, jy = player->jy;
1751
1752     player->present = TRUE;
1753
1754     player->block_last_field = (element == EL_SP_MURPHY ?
1755                                 level.sp_block_last_field :
1756                                 level.block_last_field);
1757
1758     // ---------- initialize player's last field block delay ------------------
1759
1760     // always start with reliable default value (no adjustment needed)
1761     player->block_delay_adjustment = 0;
1762
1763     // special case 1: in Supaplex, Murphy blocks last field one more frame
1764     if (player->block_last_field && element == EL_SP_MURPHY)
1765       player->block_delay_adjustment = 1;
1766
1767     // special case 2: in game engines before 3.1.1, blocking was different
1768     if (game.use_block_last_field_bug)
1769       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1770
1771     if (!network.enabled || player->connected_network)
1772     {
1773       player->active = TRUE;
1774
1775       // remove potentially duplicate players
1776       if (StorePlayer[jx][jy] == Tile[x][y])
1777         StorePlayer[jx][jy] = 0;
1778
1779       StorePlayer[x][y] = Tile[x][y];
1780
1781 #if DEBUG_INIT_PLAYER
1782       Debug("game:init:player", "- player element %d activated",
1783             player->element_nr);
1784       Debug("game:init:player", "  (local player is %d and currently %s)",
1785             local_player->element_nr,
1786             local_player->active ? "active" : "not active");
1787     }
1788 #endif
1789
1790     Tile[x][y] = EL_EMPTY;
1791
1792     player->jx = player->last_jx = x;
1793     player->jy = player->last_jy = y;
1794   }
1795
1796   // always check if player was just killed and should be reanimated
1797   {
1798     int player_nr = GET_PLAYER_NR(element);
1799     struct PlayerInfo *player = &stored_player[player_nr];
1800
1801     if (player->active && player->killed)
1802       player->reanimated = TRUE; // if player was just killed, reanimate him
1803   }
1804 }
1805
1806 static void InitField(int x, int y, boolean init_game)
1807 {
1808   int element = Tile[x][y];
1809
1810   switch (element)
1811   {
1812     case EL_SP_MURPHY:
1813     case EL_PLAYER_1:
1814     case EL_PLAYER_2:
1815     case EL_PLAYER_3:
1816     case EL_PLAYER_4:
1817       InitPlayerField(x, y, element, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_PLAYER:
1821       element = Tile[x][y] = EL_PLAYER_1;
1822       InitField(x, y, init_game);
1823
1824       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1825       InitField(x, y, init_game);
1826       break;
1827
1828     case EL_SOKOBAN_FIELD_EMPTY:
1829       IncrementSokobanFieldsNeeded();
1830       break;
1831
1832     case EL_SOKOBAN_OBJECT:
1833       IncrementSokobanObjectsNeeded();
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1838         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1840         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1844         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888     case EL_SPRING_LEFT:
1889     case EL_SPRING_RIGHT:
1890       InitMovDir(x, y);
1891       break;
1892
1893     case EL_AMOEBA_FULL:
1894     case EL_BD_AMOEBA:
1895       InitAmoebaNr(x, y);
1896       break;
1897
1898     case EL_AMOEBA_DROP:
1899       if (y == lev_fieldy - 1)
1900       {
1901         Tile[x][y] = EL_AMOEBA_GROWING;
1902         Store[x][y] = EL_AMOEBA_WET;
1903       }
1904       break;
1905
1906     case EL_DYNAMITE_ACTIVE:
1907     case EL_SP_DISK_RED_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1910     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1912       MovDelay[x][y] = 96;
1913       break;
1914
1915     case EL_EM_DYNAMITE_ACTIVE:
1916       MovDelay[x][y] = 32;
1917       break;
1918
1919     case EL_LAMP:
1920       game.lights_still_needed++;
1921       break;
1922
1923     case EL_PENGUIN:
1924       game.friends_still_needed++;
1925       break;
1926
1927     case EL_PIG:
1928     case EL_DRAGON:
1929       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930       break;
1931
1932     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1933     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1934     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1935     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944       if (init_game)
1945       {
1946         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1947         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1948         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1949
1950         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1951         {
1952           game.belt_dir[belt_nr] = belt_dir;
1953           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1954         }
1955         else    // more than one switch -- set it like the first switch
1956         {
1957           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1958         }
1959       }
1960       break;
1961
1962     case EL_LIGHT_SWITCH_ACTIVE:
1963       if (init_game)
1964         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1965       break;
1966
1967     case EL_INVISIBLE_STEELWALL:
1968     case EL_INVISIBLE_WALL:
1969     case EL_INVISIBLE_SAND:
1970       if (game.light_time_left > 0 ||
1971           game.lenses_time_left > 0)
1972         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1973       break;
1974
1975     case EL_EMC_MAGIC_BALL:
1976       if (game.ball_active)
1977         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1978       break;
1979
1980     case EL_EMC_MAGIC_BALL_SWITCH:
1981       if (game.ball_active)
1982         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1983       break;
1984
1985     case EL_TRIGGER_PLAYER:
1986     case EL_TRIGGER_ELEMENT:
1987     case EL_TRIGGER_CE_VALUE:
1988     case EL_TRIGGER_CE_SCORE:
1989     case EL_SELF:
1990     case EL_ANY_ELEMENT:
1991     case EL_CURRENT_CE_VALUE:
1992     case EL_CURRENT_CE_SCORE:
1993     case EL_PREV_CE_1:
1994     case EL_PREV_CE_2:
1995     case EL_PREV_CE_3:
1996     case EL_PREV_CE_4:
1997     case EL_PREV_CE_5:
1998     case EL_PREV_CE_6:
1999     case EL_PREV_CE_7:
2000     case EL_PREV_CE_8:
2001     case EL_NEXT_CE_1:
2002     case EL_NEXT_CE_2:
2003     case EL_NEXT_CE_3:
2004     case EL_NEXT_CE_4:
2005     case EL_NEXT_CE_5:
2006     case EL_NEXT_CE_6:
2007     case EL_NEXT_CE_7:
2008     case EL_NEXT_CE_8:
2009       // reference elements should not be used on the playfield
2010       Tile[x][y] = EL_EMPTY;
2011       break;
2012
2013     default:
2014       if (IS_CUSTOM_ELEMENT(element))
2015       {
2016         if (CAN_MOVE(element))
2017           InitMovDir(x, y);
2018
2019         if (!element_info[element].use_last_ce_value || init_game)
2020           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2021       }
2022       else if (IS_GROUP_ELEMENT(element))
2023       {
2024         Tile[x][y] = GetElementFromGroupElement(element);
2025
2026         InitField(x, y, init_game);
2027       }
2028
2029       break;
2030   }
2031
2032   if (!init_game)
2033     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2034 }
2035
2036 static void InitField_WithBug1(int x, int y, boolean init_game)
2037 {
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(Tile[x][y]))
2043     InitMovDir(x, y);
2044 }
2045
2046 static void InitField_WithBug2(int x, int y, boolean init_game)
2047 {
2048   int old_element = Tile[x][y];
2049
2050   InitField(x, y, init_game);
2051
2052   // not needed to call InitMovDir() -- already done by InitField()!
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(old_element) &&
2055       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2056     InitMovDir(x, y);
2057
2058   /* this case is in fact a combination of not less than three bugs:
2059      first, it calls InitMovDir() for elements that can move, although this is
2060      already done by InitField(); then, it checks the element that was at this
2061      field _before_ the call to InitField() (which can change it); lastly, it
2062      was not called for "mole with direction" elements, which were treated as
2063      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2064   */
2065 }
2066
2067 static int get_key_element_from_nr(int key_nr)
2068 {
2069   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2070                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2071                           EL_EM_KEY_1 : EL_KEY_1);
2072
2073   return key_base_element + key_nr;
2074 }
2075
2076 static int get_next_dropped_element(struct PlayerInfo *player)
2077 {
2078   return (player->inventory_size > 0 ?
2079           player->inventory_element[player->inventory_size - 1] :
2080           player->inventory_infinite_element != EL_UNDEFINED ?
2081           player->inventory_infinite_element :
2082           player->dynabombs_left > 0 ?
2083           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2084           EL_UNDEFINED);
2085 }
2086
2087 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2088 {
2089   // pos >= 0: get element from bottom of the stack;
2090   // pos <  0: get element from top of the stack
2091
2092   if (pos < 0)
2093   {
2094     int min_inventory_size = -pos;
2095     int inventory_pos = player->inventory_size - min_inventory_size;
2096     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2097
2098     return (player->inventory_size >= min_inventory_size ?
2099             player->inventory_element[inventory_pos] :
2100             player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             EL_UNDEFINED);
2105   }
2106   else
2107   {
2108     int min_dynabombs_left = pos + 1;
2109     int min_inventory_size = pos + 1 - player->dynabombs_left;
2110     int inventory_pos = pos - player->dynabombs_left;
2111
2112     return (player->inventory_infinite_element != EL_UNDEFINED ?
2113             player->inventory_infinite_element :
2114             player->dynabombs_left >= min_dynabombs_left ?
2115             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2116             player->inventory_size >= min_inventory_size ?
2117             player->inventory_element[inventory_pos] :
2118             EL_UNDEFINED);
2119   }
2120 }
2121
2122 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2123 {
2124   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2125   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2126   int compare_result;
2127
2128   if (gpo1->sort_priority != gpo2->sort_priority)
2129     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2130   else
2131     compare_result = gpo1->nr - gpo2->nr;
2132
2133   return compare_result;
2134 }
2135
2136 int getPlayerInventorySize(int player_nr)
2137 {
2138   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2139     return game_em.ply[player_nr]->dynamite;
2140   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2141     return game_sp.red_disk_count;
2142   else
2143     return stored_player[player_nr].inventory_size;
2144 }
2145
2146 static void InitGameControlValues(void)
2147 {
2148   int i;
2149
2150   for (i = 0; game_panel_controls[i].nr != -1; i++)
2151   {
2152     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2153     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2154     struct TextPosInfo *pos = gpc->pos;
2155     int nr = gpc->nr;
2156     int type = gpc->type;
2157
2158     if (nr != i)
2159     {
2160       Error("'game_panel_controls' structure corrupted at %d", i);
2161
2162       Fail("this should not happen -- please debug");
2163     }
2164
2165     // force update of game controls after initialization
2166     gpc->value = gpc->last_value = -1;
2167     gpc->frame = gpc->last_frame = -1;
2168     gpc->gfx_frame = -1;
2169
2170     // determine panel value width for later calculation of alignment
2171     if (type == TYPE_INTEGER || type == TYPE_STRING)
2172     {
2173       pos->width = pos->size * getFontWidth(pos->font);
2174       pos->height = getFontHeight(pos->font);
2175     }
2176     else if (type == TYPE_ELEMENT)
2177     {
2178       pos->width = pos->size;
2179       pos->height = pos->size;
2180     }
2181
2182     // fill structure for game panel draw order
2183     gpo->nr = gpc->nr;
2184     gpo->sort_priority = pos->sort_priority;
2185   }
2186
2187   // sort game panel controls according to sort_priority and control number
2188   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2189         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2190 }
2191
2192 static void UpdatePlayfieldElementCount(void)
2193 {
2194   boolean use_element_count = FALSE;
2195   int i, j, x, y;
2196
2197   // first check if it is needed at all to calculate playfield element count
2198   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2199     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2200       use_element_count = TRUE;
2201
2202   if (!use_element_count)
2203     return;
2204
2205   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2206     element_info[i].element_count = 0;
2207
2208   SCAN_PLAYFIELD(x, y)
2209   {
2210     element_info[Tile[x][y]].element_count++;
2211   }
2212
2213   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2214     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2215       if (IS_IN_GROUP(j, i))
2216         element_info[EL_GROUP_START + i].element_count +=
2217           element_info[j].element_count;
2218 }
2219
2220 static void UpdateGameControlValues(void)
2221 {
2222   int i, k;
2223   int time = (game.LevelSolved ?
2224               game.LevelSolved_CountingTime :
2225               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226               game_em.lev->time :
2227               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2228               game_sp.time_played :
2229               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2230               game_mm.energy_left :
2231               game.no_time_limit ? TimePlayed : TimeLeft);
2232   int score = (game.LevelSolved ?
2233                game.LevelSolved_CountingScore :
2234                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2235                game_em.lev->score :
2236                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2237                game_sp.score :
2238                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2239                game_mm.score :
2240                game.score);
2241   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2242               game_em.lev->gems_needed :
2243               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2244               game_sp.infotrons_still_needed :
2245               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2246               game_mm.kettles_still_needed :
2247               game.gems_still_needed);
2248   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2249                      game_em.lev->gems_needed > 0 :
2250                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2251                      game_sp.infotrons_still_needed > 0 :
2252                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2253                      game_mm.kettles_still_needed > 0 ||
2254                      game_mm.lights_still_needed > 0 :
2255                      game.gems_still_needed > 0 ||
2256                      game.sokoban_fields_still_needed > 0 ||
2257                      game.sokoban_objects_still_needed > 0 ||
2258                      game.lights_still_needed > 0);
2259   int health = (game.LevelSolved ?
2260                 game.LevelSolved_CountingHealth :
2261                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2262                 MM_HEALTH(game_mm.laser_overload_value) :
2263                 game.health);
2264   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2265
2266   UpdatePlayfieldElementCount();
2267
2268   // update game panel control values
2269
2270   // used instead of "level_nr" (for network games)
2271   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2272   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2273
2274   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2275   for (i = 0; i < MAX_NUM_KEYS; i++)
2276     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2277   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2278   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2279
2280   if (game.centered_player_nr == -1)
2281   {
2282     for (i = 0; i < MAX_PLAYERS; i++)
2283     {
2284       // only one player in Supaplex game engine
2285       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2286         break;
2287
2288       for (k = 0; k < MAX_NUM_KEYS; k++)
2289       {
2290         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291         {
2292           if (game_em.ply[i]->keys & (1 << k))
2293             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294               get_key_element_from_nr(k);
2295         }
2296         else if (stored_player[i].key[k])
2297           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298             get_key_element_from_nr(k);
2299       }
2300
2301       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302         getPlayerInventorySize(i);
2303
2304       if (stored_player[i].num_white_keys > 0)
2305         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2306           EL_DC_KEY_WHITE;
2307
2308       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2309         stored_player[i].num_white_keys;
2310     }
2311   }
2312   else
2313   {
2314     int player_nr = game.centered_player_nr;
2315
2316     for (k = 0; k < MAX_NUM_KEYS; k++)
2317     {
2318       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2319       {
2320         if (game_em.ply[player_nr]->keys & (1 << k))
2321           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2322             get_key_element_from_nr(k);
2323       }
2324       else if (stored_player[player_nr].key[k])
2325         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2326           get_key_element_from_nr(k);
2327     }
2328
2329     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330       getPlayerInventorySize(player_nr);
2331
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338
2339   // re-arrange keys on game panel, if needed or if defined by style settings
2340   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2341   {
2342     int nr = GAME_PANEL_KEY_1 + i;
2343     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2344     struct TextPosInfo *pos = gpc->pos;
2345
2346     // skip check if key is not in the player's inventory
2347     if (gpc->value == EL_EMPTY)
2348       continue;
2349
2350     // check if keys should be arranged on panel from left to right
2351     if (pos->style == STYLE_LEFTMOST_POSITION)
2352     {
2353       // check previous key positions (left from current key)
2354       for (k = 0; k < i; k++)
2355       {
2356         int nr_new = GAME_PANEL_KEY_1 + k;
2357
2358         if (game_panel_controls[nr_new].value == EL_EMPTY)
2359         {
2360           game_panel_controls[nr_new].value = gpc->value;
2361           gpc->value = EL_EMPTY;
2362
2363           break;
2364         }
2365       }
2366     }
2367
2368     // check if "undefined" keys can be placed at some other position
2369     if (pos->x == -1 && pos->y == -1)
2370     {
2371       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2372
2373       // 1st try: display key at the same position as normal or EM keys
2374       if (game_panel_controls[nr_new].value == EL_EMPTY)
2375       {
2376         game_panel_controls[nr_new].value = gpc->value;
2377       }
2378       else
2379       {
2380         // 2nd try: display key at the next free position in the key panel
2381         for (k = 0; k < STD_NUM_KEYS; k++)
2382         {
2383           nr_new = GAME_PANEL_KEY_1 + k;
2384
2385           if (game_panel_controls[nr_new].value == EL_EMPTY)
2386           {
2387             game_panel_controls[nr_new].value = gpc->value;
2388
2389             break;
2390           }
2391         }
2392       }
2393     }
2394   }
2395
2396   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2397   {
2398     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2399       get_inventory_element_from_pos(local_player, i);
2400     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2401       get_inventory_element_from_pos(local_player, -i - 1);
2402   }
2403
2404   game_panel_controls[GAME_PANEL_SCORE].value = score;
2405   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2406
2407   game_panel_controls[GAME_PANEL_TIME].value = time;
2408
2409   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2410   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2411   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2412
2413   if (level.time == 0)
2414     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2415   else
2416     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2417
2418   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2419   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2420
2421   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2422
2423   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2424     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2425      EL_EMPTY);
2426   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2427     local_player->shield_normal_time_left;
2428   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2429     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2430      EL_EMPTY);
2431   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2432     local_player->shield_deadly_time_left;
2433
2434   game_panel_controls[GAME_PANEL_EXIT].value =
2435     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2436
2437   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2438     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2439   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2440     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2441      EL_EMC_MAGIC_BALL_SWITCH);
2442
2443   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2444     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2445   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2446     game.light_time_left;
2447
2448   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2449     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2450   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2451     game.timegate_time_left;
2452
2453   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2454     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2455
2456   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2457     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2458   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2459     game.lenses_time_left;
2460
2461   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2462     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2463   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2464     game.magnify_time_left;
2465
2466   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2467     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2468      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2469      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2470      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2471      EL_BALLOON_SWITCH_NONE);
2472
2473   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2474     local_player->dynabomb_count;
2475   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2476     local_player->dynabomb_size;
2477   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2478     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2479
2480   game_panel_controls[GAME_PANEL_PENGUINS].value =
2481     game.friends_still_needed;
2482
2483   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2484     game.sokoban_objects_still_needed;
2485   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2486     game.sokoban_fields_still_needed;
2487
2488   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2489     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2490
2491   for (i = 0; i < NUM_BELTS; i++)
2492   {
2493     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2494       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2495        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2496     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2497       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2498   }
2499
2500   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2501     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2502   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2503     game.magic_wall_time_left;
2504
2505   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2506     local_player->gravity;
2507
2508   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2509     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2510
2511   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2512     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2513       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2514        game.panel.element[i].id : EL_UNDEFINED);
2515
2516   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2517     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2518       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2519        element_info[game.panel.element_count[i].id].element_count : 0);
2520
2521   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2522     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2523       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2524        element_info[game.panel.ce_score[i].id].collect_score : 0);
2525
2526   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2527     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2528       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2529        element_info[game.panel.ce_score_element[i].id].collect_score :
2530        EL_UNDEFINED);
2531
2532   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2533   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2534   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2535
2536   // update game panel control frames
2537
2538   for (i = 0; game_panel_controls[i].nr != -1; i++)
2539   {
2540     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2541
2542     if (gpc->type == TYPE_ELEMENT)
2543     {
2544       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2545       {
2546         int last_anim_random_frame = gfx.anim_random_frame;
2547         int element = gpc->value;
2548         int graphic = el2panelimg(element);
2549         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2550                                sync_random_frame : INIT_GFX_RANDOM());
2551
2552         if (gpc->value != gpc->last_value)
2553         {
2554           gpc->gfx_frame = 0;
2555           gpc->gfx_random = init_gfx_random;
2556         }
2557         else
2558         {
2559           gpc->gfx_frame++;
2560
2561           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2562               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2563             gpc->gfx_random = init_gfx_random;
2564         }
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = gpc->gfx_random;
2568
2569         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2570           gpc->gfx_frame = element_info[element].collect_score;
2571
2572         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2573
2574         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2575           gfx.anim_random_frame = last_anim_random_frame;
2576       }
2577     }
2578     else if (gpc->type == TYPE_GRAPHIC)
2579     {
2580       if (gpc->graphic != IMG_UNDEFINED)
2581       {
2582         int last_anim_random_frame = gfx.anim_random_frame;
2583         int graphic = gpc->graphic;
2584         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2585                                sync_random_frame : INIT_GFX_RANDOM());
2586
2587         if (gpc->value != gpc->last_value)
2588         {
2589           gpc->gfx_frame = 0;
2590           gpc->gfx_random = init_gfx_random;
2591         }
2592         else
2593         {
2594           gpc->gfx_frame++;
2595
2596           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2597               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2598             gpc->gfx_random = init_gfx_random;
2599         }
2600
2601         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2602           gfx.anim_random_frame = gpc->gfx_random;
2603
2604         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2605
2606         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2607           gfx.anim_random_frame = last_anim_random_frame;
2608       }
2609     }
2610   }
2611 }
2612
2613 static void DisplayGameControlValues(void)
2614 {
2615   boolean redraw_panel = FALSE;
2616   int i;
2617
2618   for (i = 0; game_panel_controls[i].nr != -1; i++)
2619   {
2620     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2621
2622     if (PANEL_DEACTIVATED(gpc->pos))
2623       continue;
2624
2625     if (gpc->value == gpc->last_value &&
2626         gpc->frame == gpc->last_frame)
2627       continue;
2628
2629     redraw_panel = TRUE;
2630   }
2631
2632   if (!redraw_panel)
2633     return;
2634
2635   // copy default game door content to main double buffer
2636
2637   // !!! CHECK AGAIN !!!
2638   SetPanelBackground();
2639   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2640   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2641
2642   // redraw game control buttons
2643   RedrawGameButtons();
2644
2645   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2646
2647   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2648   {
2649     int nr = game_panel_order[i].nr;
2650     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2651     struct TextPosInfo *pos = gpc->pos;
2652     int type = gpc->type;
2653     int value = gpc->value;
2654     int frame = gpc->frame;
2655     int size = pos->size;
2656     int font = pos->font;
2657     boolean draw_masked = pos->draw_masked;
2658     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2659
2660     if (PANEL_DEACTIVATED(pos))
2661       continue;
2662
2663     if (pos->class == get_hash_from_key("extra_panel_items") &&
2664         !setup.prefer_extra_panel_items)
2665       continue;
2666
2667     gpc->last_value = value;
2668     gpc->last_frame = frame;
2669
2670     if (type == TYPE_INTEGER)
2671     {
2672       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2673           nr == GAME_PANEL_TIME)
2674       {
2675         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2676
2677         if (use_dynamic_size)           // use dynamic number of digits
2678         {
2679           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2680           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2681           int size2 = size1 + 1;
2682           int font1 = pos->font;
2683           int font2 = pos->font_alt;
2684
2685           size = (value < value_change ? size1 : size2);
2686           font = (value < value_change ? font1 : font2);
2687         }
2688       }
2689
2690       // correct text size if "digits" is zero or less
2691       if (size <= 0)
2692         size = strlen(int2str(value, size));
2693
2694       // dynamically correct text alignment
2695       pos->width = size * getFontWidth(font);
2696
2697       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2698                   int2str(value, size), font, mask_mode);
2699     }
2700     else if (type == TYPE_ELEMENT)
2701     {
2702       int element, graphic;
2703       Bitmap *src_bitmap;
2704       int src_x, src_y;
2705       int width, height;
2706       int dst_x = PANEL_XPOS(pos);
2707       int dst_y = PANEL_YPOS(pos);
2708
2709       if (value != EL_UNDEFINED && value != EL_EMPTY)
2710       {
2711         element = value;
2712         graphic = el2panelimg(value);
2713
2714 #if 0
2715         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2716               element, EL_NAME(element), size);
2717 #endif
2718
2719         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2720           size = TILESIZE;
2721
2722         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2723                               &src_x, &src_y);
2724
2725         width  = graphic_info[graphic].width  * size / TILESIZE;
2726         height = graphic_info[graphic].height * size / TILESIZE;
2727
2728         if (draw_masked)
2729           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2730                            dst_x, dst_y);
2731         else
2732           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2733                      dst_x, dst_y);
2734       }
2735     }
2736     else if (type == TYPE_GRAPHIC)
2737     {
2738       int graphic        = gpc->graphic;
2739       int graphic_active = gpc->graphic_active;
2740       Bitmap *src_bitmap;
2741       int src_x, src_y;
2742       int width, height;
2743       int dst_x = PANEL_XPOS(pos);
2744       int dst_y = PANEL_YPOS(pos);
2745       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2746                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2747
2748       if (graphic != IMG_UNDEFINED && !skip)
2749       {
2750         if (pos->style == STYLE_REVERSE)
2751           value = 100 - value;
2752
2753         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2754
2755         if (pos->direction & MV_HORIZONTAL)
2756         {
2757           width  = graphic_info[graphic_active].width * value / 100;
2758           height = graphic_info[graphic_active].height;
2759
2760           if (pos->direction == MV_LEFT)
2761           {
2762             src_x += graphic_info[graphic_active].width - width;
2763             dst_x += graphic_info[graphic_active].width - width;
2764           }
2765         }
2766         else
2767         {
2768           width  = graphic_info[graphic_active].width;
2769           height = graphic_info[graphic_active].height * value / 100;
2770
2771           if (pos->direction == MV_UP)
2772           {
2773             src_y += graphic_info[graphic_active].height - height;
2774             dst_y += graphic_info[graphic_active].height - height;
2775           }
2776         }
2777
2778         if (draw_masked)
2779           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2780                            dst_x, dst_y);
2781         else
2782           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2783                      dst_x, dst_y);
2784
2785         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2786
2787         if (pos->direction & MV_HORIZONTAL)
2788         {
2789           if (pos->direction == MV_RIGHT)
2790           {
2791             src_x += width;
2792             dst_x += width;
2793           }
2794           else
2795           {
2796             dst_x = PANEL_XPOS(pos);
2797           }
2798
2799           width = graphic_info[graphic].width - width;
2800         }
2801         else
2802         {
2803           if (pos->direction == MV_DOWN)
2804           {
2805             src_y += height;
2806             dst_y += height;
2807           }
2808           else
2809           {
2810             dst_y = PANEL_YPOS(pos);
2811           }
2812
2813           height = graphic_info[graphic].height - height;
2814         }
2815
2816         if (draw_masked)
2817           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2818                            dst_x, dst_y);
2819         else
2820           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2821                      dst_x, dst_y);
2822       }
2823     }
2824     else if (type == TYPE_STRING)
2825     {
2826       boolean active = (value != 0);
2827       char *state_normal = "off";
2828       char *state_active = "on";
2829       char *state = (active ? state_active : state_normal);
2830       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2831                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2832                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2833                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2834
2835       if (nr == GAME_PANEL_GRAVITY_STATE)
2836       {
2837         int font1 = pos->font;          // (used for normal state)
2838         int font2 = pos->font_alt;      // (used for active state)
2839
2840         font = (active ? font2 : font1);
2841       }
2842
2843       if (s != NULL)
2844       {
2845         char *s_cut;
2846
2847         if (size <= 0)
2848         {
2849           // don't truncate output if "chars" is zero or less
2850           size = strlen(s);
2851
2852           // dynamically correct text alignment
2853           pos->width = size * getFontWidth(font);
2854         }
2855
2856         s_cut = getStringCopyN(s, size);
2857
2858         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2859                     s_cut, font, mask_mode);
2860
2861         free(s_cut);
2862       }
2863     }
2864
2865     redraw_mask |= REDRAW_DOOR_1;
2866   }
2867
2868   SetGameStatus(GAME_MODE_PLAYING);
2869 }
2870
2871 void UpdateAndDisplayGameControlValues(void)
2872 {
2873   if (tape.deactivate_display)
2874     return;
2875
2876   UpdateGameControlValues();
2877   DisplayGameControlValues();
2878 }
2879
2880 void UpdateGameDoorValues(void)
2881 {
2882   UpdateGameControlValues();
2883 }
2884
2885 void DrawGameDoorValues(void)
2886 {
2887   DisplayGameControlValues();
2888 }
2889
2890
2891 // ============================================================================
2892 // InitGameEngine()
2893 // ----------------------------------------------------------------------------
2894 // initialize game engine due to level / tape version number
2895 // ============================================================================
2896
2897 static void InitGameEngine(void)
2898 {
2899   int i, j, k, l, x, y;
2900
2901   // set game engine from tape file when re-playing, else from level file
2902   game.engine_version = (tape.playing ? tape.engine_version :
2903                          level.game_version);
2904
2905   // set single or multi-player game mode (needed for re-playing tapes)
2906   game.team_mode = setup.team_mode;
2907
2908   if (tape.playing)
2909   {
2910     int num_players = 0;
2911
2912     for (i = 0; i < MAX_PLAYERS; i++)
2913       if (tape.player_participates[i])
2914         num_players++;
2915
2916     // multi-player tapes contain input data for more than one player
2917     game.team_mode = (num_players > 1);
2918   }
2919
2920 #if 0
2921   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2922         level.game_version);
2923   Debug("game:init:level", "          tape.file_version   == %06d",
2924         tape.file_version);
2925   Debug("game:init:level", "          tape.game_version   == %06d",
2926         tape.game_version);
2927   Debug("game:init:level", "          tape.engine_version == %06d",
2928         tape.engine_version);
2929   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2930         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2931 #endif
2932
2933   // --------------------------------------------------------------------------
2934   // set flags for bugs and changes according to active game engine version
2935   // --------------------------------------------------------------------------
2936
2937   /*
2938     Summary of bugfix:
2939     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2940
2941     Bug was introduced in version:
2942     2.0.1
2943
2944     Bug was fixed in version:
2945     4.2.0.0
2946
2947     Description:
2948     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2949     but the property "can fall" was missing, which caused some levels to be
2950     unsolvable. This was fixed in version 4.2.0.0.
2951
2952     Affected levels/tapes:
2953     An example for a tape that was fixed by this bugfix is tape 029 from the
2954     level set "rnd_sam_bateman".
2955     The wrong behaviour will still be used for all levels or tapes that were
2956     created/recorded with it. An example for this is tape 023 from the level
2957     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2958   */
2959
2960   boolean use_amoeba_dropping_cannot_fall_bug =
2961     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2962       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2963      (tape.playing &&
2964       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2965       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2966
2967   /*
2968     Summary of bugfix/change:
2969     Fixed move speed of elements entering or leaving magic wall.
2970
2971     Fixed/changed in version:
2972     2.0.1
2973
2974     Description:
2975     Before 2.0.1, move speed of elements entering or leaving magic wall was
2976     twice as fast as it is now.
2977     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2978
2979     Affected levels/tapes:
2980     The first condition is generally needed for all levels/tapes before version
2981     2.0.1, which might use the old behaviour before it was changed; known tapes
2982     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2983     The second condition is an exception from the above case and is needed for
2984     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2985     above, but before it was known that this change would break tapes like the
2986     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2987     although the engine version while recording maybe was before 2.0.1. There
2988     are a lot of tapes that are affected by this exception, like tape 006 from
2989     the level set "rnd_conor_mancone".
2990   */
2991
2992   boolean use_old_move_stepsize_for_magic_wall =
2993     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2994      !(tape.playing &&
2995        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2996        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2997
2998   /*
2999     Summary of bugfix/change:
3000     Fixed handling for custom elements that change when pushed by the player.
3001
3002     Fixed/changed in version:
3003     3.1.0
3004
3005     Description:
3006     Before 3.1.0, custom elements that "change when pushing" changed directly
3007     after the player started pushing them (until then handled in "DigField()").
3008     Since 3.1.0, these custom elements are not changed until the "pushing"
3009     move of the element is finished (now handled in "ContinueMoving()").
3010
3011     Affected levels/tapes:
3012     The first condition is generally needed for all levels/tapes before version
3013     3.1.0, which might use the old behaviour before it was changed; known tapes
3014     that are affected are some tapes from the level set "Walpurgis Gardens" by
3015     Jamie Cullen.
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3018     above (including some development versions of 3.1.0), but before it was
3019     known that this change would break tapes like the above and was fixed in
3020     3.1.1, so that the changed behaviour was active although the engine version
3021     while recording maybe was before 3.1.0. There is at least one tape that is
3022     affected by this exception, which is the tape for the one-level set "Bug
3023     Machine" by Juergen Bonhagen.
3024   */
3025
3026   game.use_change_when_pushing_bug =
3027     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3028      !(tape.playing &&
3029        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3030        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3031
3032   /*
3033     Summary of bugfix/change:
3034     Fixed handling for blocking the field the player leaves when moving.
3035
3036     Fixed/changed in version:
3037     3.1.1
3038
3039     Description:
3040     Before 3.1.1, when "block last field when moving" was enabled, the field
3041     the player is leaving when moving was blocked for the time of the move,
3042     and was directly unblocked afterwards. This resulted in the last field
3043     being blocked for exactly one less than the number of frames of one player
3044     move. Additionally, even when blocking was disabled, the last field was
3045     blocked for exactly one frame.
3046     Since 3.1.1, due to changes in player movement handling, the last field
3047     is not blocked at all when blocking is disabled. When blocking is enabled,
3048     the last field is blocked for exactly the number of frames of one player
3049     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3050     last field is blocked for exactly one more than the number of frames of
3051     one player move.
3052
3053     Affected levels/tapes:
3054     (!!! yet to be determined -- probably many !!!)
3055   */
3056
3057   game.use_block_last_field_bug =
3058     (game.engine_version < VERSION_IDENT(3,1,1,0));
3059
3060   /* various special flags and settings for native Emerald Mine game engine */
3061
3062   game_em.use_single_button =
3063     (game.engine_version > VERSION_IDENT(4,0,0,2));
3064
3065   game_em.use_snap_key_bug =
3066     (game.engine_version < VERSION_IDENT(4,0,1,0));
3067
3068   game_em.use_random_bug =
3069     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3070
3071   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3072
3073   game_em.use_old_explosions            = use_old_em_engine;
3074   game_em.use_old_android               = use_old_em_engine;
3075   game_em.use_old_push_elements         = use_old_em_engine;
3076   game_em.use_old_push_into_acid        = use_old_em_engine;
3077
3078   game_em.use_wrap_around               = !use_old_em_engine;
3079
3080   // --------------------------------------------------------------------------
3081
3082   // set maximal allowed number of custom element changes per game frame
3083   game.max_num_changes_per_frame = 1;
3084
3085   // default scan direction: scan playfield from top/left to bottom/right
3086   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3087
3088   // dynamically adjust element properties according to game engine version
3089   InitElementPropertiesEngine(game.engine_version);
3090
3091   // ---------- initialize special element properties -------------------------
3092
3093   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3094   if (use_amoeba_dropping_cannot_fall_bug)
3095     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3096
3097   // ---------- initialize player's initial move delay ------------------------
3098
3099   // dynamically adjust player properties according to level information
3100   for (i = 0; i < MAX_PLAYERS; i++)
3101     game.initial_move_delay_value[i] =
3102       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3103
3104   // dynamically adjust player properties according to game engine version
3105   for (i = 0; i < MAX_PLAYERS; i++)
3106     game.initial_move_delay[i] =
3107       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3108        game.initial_move_delay_value[i] : 0);
3109
3110   // ---------- initialize player's initial push delay ------------------------
3111
3112   // dynamically adjust player properties according to game engine version
3113   game.initial_push_delay_value =
3114     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3115
3116   // ---------- initialize changing elements ----------------------------------
3117
3118   // initialize changing elements information
3119   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3120   {
3121     struct ElementInfo *ei = &element_info[i];
3122
3123     // this pointer might have been changed in the level editor
3124     ei->change = &ei->change_page[0];
3125
3126     if (!IS_CUSTOM_ELEMENT(i))
3127     {
3128       ei->change->target_element = EL_EMPTY_SPACE;
3129       ei->change->delay_fixed = 0;
3130       ei->change->delay_random = 0;
3131       ei->change->delay_frames = 1;
3132     }
3133
3134     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3135     {
3136       ei->has_change_event[j] = FALSE;
3137
3138       ei->event_page_nr[j] = 0;
3139       ei->event_page[j] = &ei->change_page[0];
3140     }
3141   }
3142
3143   // add changing elements from pre-defined list
3144   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3145   {
3146     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3147     struct ElementInfo *ei = &element_info[ch_delay->element];
3148
3149     ei->change->target_element       = ch_delay->target_element;
3150     ei->change->delay_fixed          = ch_delay->change_delay;
3151
3152     ei->change->pre_change_function  = ch_delay->pre_change_function;
3153     ei->change->change_function      = ch_delay->change_function;
3154     ei->change->post_change_function = ch_delay->post_change_function;
3155
3156     ei->change->can_change = TRUE;
3157     ei->change->can_change_or_has_action = TRUE;
3158
3159     ei->has_change_event[CE_DELAY] = TRUE;
3160
3161     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3162     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3163   }
3164
3165   // ---------- initialize internal run-time variables ------------------------
3166
3167   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3168   {
3169     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3170
3171     for (j = 0; j < ei->num_change_pages; j++)
3172     {
3173       ei->change_page[j].can_change_or_has_action =
3174         (ei->change_page[j].can_change |
3175          ei->change_page[j].has_action);
3176     }
3177   }
3178
3179   // add change events from custom element configuration
3180   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3181   {
3182     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3183
3184     for (j = 0; j < ei->num_change_pages; j++)
3185     {
3186       if (!ei->change_page[j].can_change_or_has_action)
3187         continue;
3188
3189       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3190       {
3191         // only add event page for the first page found with this event
3192         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3193         {
3194           ei->has_change_event[k] = TRUE;
3195
3196           ei->event_page_nr[k] = j;
3197           ei->event_page[k] = &ei->change_page[j];
3198         }
3199       }
3200     }
3201   }
3202
3203   // ---------- initialize reference elements in change conditions ------------
3204
3205   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3206   {
3207     int element = EL_CUSTOM_START + i;
3208     struct ElementInfo *ei = &element_info[element];
3209
3210     for (j = 0; j < ei->num_change_pages; j++)
3211     {
3212       int trigger_element = ei->change_page[j].initial_trigger_element;
3213
3214       if (trigger_element >= EL_PREV_CE_8 &&
3215           trigger_element <= EL_NEXT_CE_8)
3216         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3217
3218       ei->change_page[j].trigger_element = trigger_element;
3219     }
3220   }
3221
3222   // ---------- initialize run-time trigger player and element ----------------
3223
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3231       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3232       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3233       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3234       ei->change_page[j].actual_trigger_ce_value = 0;
3235       ei->change_page[j].actual_trigger_ce_score = 0;
3236     }
3237   }
3238
3239   // ---------- initialize trigger events -------------------------------------
3240
3241   // initialize trigger events information
3242   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3243     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3244       trigger_events[i][j] = FALSE;
3245
3246   // add trigger events from element change event properties
3247   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3248   {
3249     struct ElementInfo *ei = &element_info[i];
3250
3251     for (j = 0; j < ei->num_change_pages; j++)
3252     {
3253       if (!ei->change_page[j].can_change_or_has_action)
3254         continue;
3255
3256       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3257       {
3258         int trigger_element = ei->change_page[j].trigger_element;
3259
3260         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3261         {
3262           if (ei->change_page[j].has_event[k])
3263           {
3264             if (IS_GROUP_ELEMENT(trigger_element))
3265             {
3266               struct ElementGroupInfo *group =
3267                 element_info[trigger_element].group;
3268
3269               for (l = 0; l < group->num_elements_resolved; l++)
3270                 trigger_events[group->element_resolved[l]][k] = TRUE;
3271             }
3272             else if (trigger_element == EL_ANY_ELEMENT)
3273               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3274                 trigger_events[l][k] = TRUE;
3275             else
3276               trigger_events[trigger_element][k] = TRUE;
3277           }
3278         }
3279       }
3280     }
3281   }
3282
3283   // ---------- initialize push delay -----------------------------------------
3284
3285   // initialize push delay values to default
3286   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3287   {
3288     if (!IS_CUSTOM_ELEMENT(i))
3289     {
3290       // set default push delay values (corrected since version 3.0.7-1)
3291       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3292       {
3293         element_info[i].push_delay_fixed = 2;
3294         element_info[i].push_delay_random = 8;
3295       }
3296       else
3297       {
3298         element_info[i].push_delay_fixed = 8;
3299         element_info[i].push_delay_random = 8;
3300       }
3301     }
3302   }
3303
3304   // set push delay value for certain elements from pre-defined list
3305   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3306   {
3307     int e = push_delay_list[i].element;
3308
3309     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3310     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3311   }
3312
3313   // set push delay value for Supaplex elements for newer engine versions
3314   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3315   {
3316     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3317     {
3318       if (IS_SP_ELEMENT(i))
3319       {
3320         // set SP push delay to just enough to push under a falling zonk
3321         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3322
3323         element_info[i].push_delay_fixed  = delay;
3324         element_info[i].push_delay_random = 0;
3325       }
3326     }
3327   }
3328
3329   // ---------- initialize move stepsize --------------------------------------
3330
3331   // initialize move stepsize values to default
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333     if (!IS_CUSTOM_ELEMENT(i))
3334       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3335
3336   // set move stepsize value for certain elements from pre-defined list
3337   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3338   {
3339     int e = move_stepsize_list[i].element;
3340
3341     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3342
3343     // set move stepsize value for certain elements for older engine versions
3344     if (use_old_move_stepsize_for_magic_wall)
3345     {
3346       if (e == EL_MAGIC_WALL_FILLING ||
3347           e == EL_MAGIC_WALL_EMPTYING ||
3348           e == EL_BD_MAGIC_WALL_FILLING ||
3349           e == EL_BD_MAGIC_WALL_EMPTYING)
3350         element_info[e].move_stepsize *= 2;
3351     }
3352   }
3353
3354   // ---------- initialize collect score --------------------------------------
3355
3356   // initialize collect score values for custom elements from initial value
3357   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3358     if (IS_CUSTOM_ELEMENT(i))
3359       element_info[i].collect_score = element_info[i].collect_score_initial;
3360
3361   // ---------- initialize collect count --------------------------------------
3362
3363   // initialize collect count values for non-custom elements
3364   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     if (!IS_CUSTOM_ELEMENT(i))
3366       element_info[i].collect_count_initial = 0;
3367
3368   // add collect count values for all elements from pre-defined list
3369   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3370     element_info[collect_count_list[i].element].collect_count_initial =
3371       collect_count_list[i].count;
3372
3373   // ---------- initialize access direction -----------------------------------
3374
3375   // initialize access direction values to default (access from every side)
3376   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3377     if (!IS_CUSTOM_ELEMENT(i))
3378       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3379
3380   // set access direction value for certain elements from pre-defined list
3381   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3382     element_info[access_direction_list[i].element].access_direction =
3383       access_direction_list[i].direction;
3384
3385   // ---------- initialize explosion content ----------------------------------
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387   {
3388     if (IS_CUSTOM_ELEMENT(i))
3389       continue;
3390
3391     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3392     {
3393       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3394
3395       element_info[i].content.e[x][y] =
3396         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3397          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3398          i == EL_PLAYER_3 ? EL_EMERALD :
3399          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3400          i == EL_MOLE ? EL_EMERALD_RED :
3401          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3402          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3403          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3404          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3405          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3406          i == EL_WALL_EMERALD ? EL_EMERALD :
3407          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3408          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3409          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3410          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3411          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3412          i == EL_WALL_PEARL ? EL_PEARL :
3413          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3414          EL_EMPTY);
3415     }
3416   }
3417
3418   // ---------- initialize recursion detection --------------------------------
3419   recursion_loop_depth = 0;
3420   recursion_loop_detected = FALSE;
3421   recursion_loop_element = EL_UNDEFINED;
3422
3423   // ---------- initialize graphics engine ------------------------------------
3424   game.scroll_delay_value =
3425     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3426      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3427      !setup.forced_scroll_delay           ? 0 :
3428      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3429   game.scroll_delay_value =
3430     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3431
3432   // ---------- initialize game engine snapshots ------------------------------
3433   for (i = 0; i < MAX_PLAYERS; i++)
3434     game.snapshot.last_action[i] = 0;
3435   game.snapshot.changed_action = FALSE;
3436   game.snapshot.collected_item = FALSE;
3437   game.snapshot.mode =
3438     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3439      SNAPSHOT_MODE_EVERY_STEP :
3440      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3441      SNAPSHOT_MODE_EVERY_MOVE :
3442      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3443      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3444   game.snapshot.save_snapshot = FALSE;
3445
3446   // ---------- initialize level time for Supaplex engine ---------------------
3447   // Supaplex levels with time limit currently unsupported -- should be added
3448   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3449     level.time = 0;
3450
3451   // ---------- initialize flags for handling game actions --------------------
3452
3453   // set flags for game actions to default values
3454   game.use_key_actions = TRUE;
3455   game.use_mouse_actions = FALSE;
3456
3457   // when using Mirror Magic game engine, handle mouse events only
3458   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3459   {
3460     game.use_key_actions = FALSE;
3461     game.use_mouse_actions = TRUE;
3462   }
3463
3464   // check for custom elements with mouse click events
3465   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3466   {
3467     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3468     {
3469       int element = EL_CUSTOM_START + i;
3470
3471       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3472           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3473           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3474           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3475         game.use_mouse_actions = TRUE;
3476     }
3477   }
3478 }
3479
3480 static int get_num_special_action(int element, int action_first,
3481                                   int action_last)
3482 {
3483   int num_special_action = 0;
3484   int i, j;
3485
3486   for (i = action_first; i <= action_last; i++)
3487   {
3488     boolean found = FALSE;
3489
3490     for (j = 0; j < NUM_DIRECTIONS; j++)
3491       if (el_act_dir2img(element, i, j) !=
3492           el_act_dir2img(element, ACTION_DEFAULT, j))
3493         found = TRUE;
3494
3495     if (found)
3496       num_special_action++;
3497     else
3498       break;
3499   }
3500
3501   return num_special_action;
3502 }
3503
3504
3505 // ============================================================================
3506 // InitGame()
3507 // ----------------------------------------------------------------------------
3508 // initialize and start new game
3509 // ============================================================================
3510
3511 #if DEBUG_INIT_PLAYER
3512 static void DebugPrintPlayerStatus(char *message)
3513 {
3514   int i;
3515
3516   if (!options.debug)
3517     return;
3518
3519   Debug("game:init:player", "%s:", message);
3520
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     Debug("game:init:player",
3526           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3527           i + 1,
3528           player->present,
3529           player->connected,
3530           player->connected_locally,
3531           player->connected_network,
3532           player->active,
3533           (local_player == player ? " (local player)" : ""));
3534   }
3535 }
3536 #endif
3537
3538 void InitGame(void)
3539 {
3540   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3541   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3542   int fade_mask = REDRAW_FIELD;
3543
3544   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3545   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3546   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3547   int initial_move_dir = MV_DOWN;
3548   int i, j, x, y;
3549
3550   // required here to update video display before fading (FIX THIS)
3551   DrawMaskedBorder(REDRAW_DOOR_2);
3552
3553   if (!game.restart_level)
3554     CloseDoor(DOOR_CLOSE_1);
3555
3556   SetGameStatus(GAME_MODE_PLAYING);
3557
3558   if (level_editor_test_game)
3559     FadeSkipNextFadeOut();
3560   else
3561     FadeSetEnterScreen();
3562
3563   if (CheckFadeAll())
3564     fade_mask = REDRAW_ALL;
3565
3566   FadeLevelSoundsAndMusic();
3567
3568   ExpireSoundLoops(TRUE);
3569
3570   FadeOut(fade_mask);
3571
3572   if (level_editor_test_game)
3573     FadeSkipNextFadeIn();
3574
3575   // needed if different viewport properties defined for playing
3576   ChangeViewportPropertiesIfNeeded();
3577
3578   ClearField();
3579
3580   DrawCompleteVideoDisplay();
3581
3582   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3583
3584   InitGameEngine();
3585   InitGameControlValues();
3586
3587   if (tape.recording)
3588   {
3589     // initialize tape actions from game when recording tape
3590     tape.use_key_actions   = game.use_key_actions;
3591     tape.use_mouse_actions = game.use_mouse_actions;
3592
3593     // initialize visible playfield size when recording tape (for team mode)
3594     tape.scr_fieldx = SCR_FIELDX;
3595     tape.scr_fieldy = SCR_FIELDY;
3596   }
3597
3598   // don't play tapes over network
3599   network_playing = (network.enabled && !tape.playing);
3600
3601   for (i = 0; i < MAX_PLAYERS; i++)
3602   {
3603     struct PlayerInfo *player = &stored_player[i];
3604
3605     player->index_nr = i;
3606     player->index_bit = (1 << i);
3607     player->element_nr = EL_PLAYER_1 + i;
3608
3609     player->present = FALSE;
3610     player->active = FALSE;
3611     player->mapped = FALSE;
3612
3613     player->killed = FALSE;
3614     player->reanimated = FALSE;
3615     player->buried = FALSE;
3616
3617     player->action = 0;
3618     player->effective_action = 0;
3619     player->programmed_action = 0;
3620     player->snap_action = 0;
3621
3622     player->mouse_action.lx = 0;
3623     player->mouse_action.ly = 0;
3624     player->mouse_action.button = 0;
3625     player->mouse_action.button_hint = 0;
3626
3627     player->effective_mouse_action.lx = 0;
3628     player->effective_mouse_action.ly = 0;
3629     player->effective_mouse_action.button = 0;
3630     player->effective_mouse_action.button_hint = 0;
3631
3632     for (j = 0; j < MAX_NUM_KEYS; j++)
3633       player->key[j] = FALSE;
3634
3635     player->num_white_keys = 0;
3636
3637     player->dynabomb_count = 0;
3638     player->dynabomb_size = 1;
3639     player->dynabombs_left = 0;
3640     player->dynabomb_xl = FALSE;
3641
3642     player->MovDir = initial_move_dir;
3643     player->MovPos = 0;
3644     player->GfxPos = 0;
3645     player->GfxDir = initial_move_dir;
3646     player->GfxAction = ACTION_DEFAULT;
3647     player->Frame = 0;
3648     player->StepFrame = 0;
3649
3650     player->initial_element = player->element_nr;
3651     player->artwork_element =
3652       (level.use_artwork_element[i] ? level.artwork_element[i] :
3653        player->element_nr);
3654     player->use_murphy = FALSE;
3655
3656     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3657     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3658
3659     player->gravity = level.initial_player_gravity[i];
3660
3661     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3662
3663     player->actual_frame_counter = 0;
3664
3665     player->step_counter = 0;
3666
3667     player->last_move_dir = initial_move_dir;
3668
3669     player->is_active = FALSE;
3670
3671     player->is_waiting = FALSE;
3672     player->is_moving = FALSE;
3673     player->is_auto_moving = FALSE;
3674     player->is_digging = FALSE;
3675     player->is_snapping = FALSE;
3676     player->is_collecting = FALSE;
3677     player->is_pushing = FALSE;
3678     player->is_switching = FALSE;
3679     player->is_dropping = FALSE;
3680     player->is_dropping_pressed = FALSE;
3681
3682     player->is_bored = FALSE;
3683     player->is_sleeping = FALSE;
3684
3685     player->was_waiting = TRUE;
3686     player->was_moving = FALSE;
3687     player->was_snapping = FALSE;
3688     player->was_dropping = FALSE;
3689
3690     player->force_dropping = FALSE;
3691
3692     player->frame_counter_bored = -1;
3693     player->frame_counter_sleeping = -1;
3694
3695     player->anim_delay_counter = 0;
3696     player->post_delay_counter = 0;
3697
3698     player->dir_waiting = initial_move_dir;
3699     player->action_waiting = ACTION_DEFAULT;
3700     player->last_action_waiting = ACTION_DEFAULT;
3701     player->special_action_bored = ACTION_DEFAULT;
3702     player->special_action_sleeping = ACTION_DEFAULT;
3703
3704     player->switch_x = -1;
3705     player->switch_y = -1;
3706
3707     player->drop_x = -1;
3708     player->drop_y = -1;
3709
3710     player->show_envelope = 0;
3711
3712     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3713
3714     player->push_delay       = -1;      // initialized when pushing starts
3715     player->push_delay_value = game.initial_push_delay_value;
3716
3717     player->drop_delay = 0;
3718     player->drop_pressed_delay = 0;
3719
3720     player->last_jx = -1;
3721     player->last_jy = -1;
3722     player->jx = -1;
3723     player->jy = -1;
3724
3725     player->shield_normal_time_left = 0;
3726     player->shield_deadly_time_left = 0;
3727
3728     player->last_removed_element = EL_UNDEFINED;
3729
3730     player->inventory_infinite_element = EL_UNDEFINED;
3731     player->inventory_size = 0;
3732
3733     if (level.use_initial_inventory[i])
3734     {
3735       for (j = 0; j < level.initial_inventory_size[i]; j++)
3736       {
3737         int element = level.initial_inventory_content[i][j];
3738         int collect_count = element_info[element].collect_count_initial;
3739         int k;
3740
3741         if (!IS_CUSTOM_ELEMENT(element))
3742           collect_count = 1;
3743
3744         if (collect_count == 0)
3745           player->inventory_infinite_element = element;
3746         else
3747           for (k = 0; k < collect_count; k++)
3748             if (player->inventory_size < MAX_INVENTORY_SIZE)
3749               player->inventory_element[player->inventory_size++] = element;
3750       }
3751     }
3752
3753     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3754     SnapField(player, 0, 0);
3755
3756     map_player_action[i] = i;
3757   }
3758
3759   network_player_action_received = FALSE;
3760
3761   // initial null action
3762   if (network_playing)
3763     SendToServer_MovePlayer(MV_NONE);
3764
3765   FrameCounter = 0;
3766   TimeFrames = 0;
3767   TimePlayed = 0;
3768   TimeLeft = level.time;
3769   TapeTime = 0;
3770
3771   ScreenMovDir = MV_NONE;
3772   ScreenMovPos = 0;
3773   ScreenGfxPos = 0;
3774
3775   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3776
3777   game.robot_wheel_x = -1;
3778   game.robot_wheel_y = -1;
3779
3780   game.exit_x = -1;
3781   game.exit_y = -1;
3782
3783   game.all_players_gone = FALSE;
3784
3785   game.LevelSolved = FALSE;
3786   game.GameOver = FALSE;
3787
3788   game.GamePlayed = !tape.playing;
3789
3790   game.LevelSolved_GameWon = FALSE;
3791   game.LevelSolved_GameEnd = FALSE;
3792   game.LevelSolved_SaveTape = FALSE;
3793   game.LevelSolved_SaveScore = FALSE;
3794
3795   game.LevelSolved_CountingTime = 0;
3796   game.LevelSolved_CountingScore = 0;
3797   game.LevelSolved_CountingHealth = 0;
3798
3799   game.panel.active = TRUE;
3800
3801   game.no_time_limit = (level.time == 0);
3802
3803   game.yamyam_content_nr = 0;
3804   game.robot_wheel_active = FALSE;
3805   game.magic_wall_active = FALSE;
3806   game.magic_wall_time_left = 0;
3807   game.light_time_left = 0;
3808   game.timegate_time_left = 0;
3809   game.switchgate_pos = 0;
3810   game.wind_direction = level.wind_direction_initial;
3811
3812   game.time_final = 0;
3813   game.score_time_final = 0;
3814
3815   game.score = 0;
3816   game.score_final = 0;
3817
3818   game.health = MAX_HEALTH;
3819   game.health_final = MAX_HEALTH;
3820
3821   game.gems_still_needed = level.gems_needed;
3822   game.sokoban_fields_still_needed = 0;
3823   game.sokoban_objects_still_needed = 0;
3824   game.lights_still_needed = 0;
3825   game.players_still_needed = 0;
3826   game.friends_still_needed = 0;
3827
3828   game.lenses_time_left = 0;
3829   game.magnify_time_left = 0;
3830
3831   game.ball_active = level.ball_active_initial;
3832   game.ball_content_nr = 0;
3833
3834   game.explosions_delayed = TRUE;
3835
3836   game.envelope_active = FALSE;
3837
3838   for (i = 0; i < NUM_BELTS; i++)
3839   {
3840     game.belt_dir[i] = MV_NONE;
3841     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3842   }
3843
3844   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3845     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3846
3847 #if DEBUG_INIT_PLAYER
3848   DebugPrintPlayerStatus("Player status at level initialization");
3849 #endif
3850
3851   SCAN_PLAYFIELD(x, y)
3852   {
3853     Tile[x][y] = Last[x][y] = level.field[x][y];
3854     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3855     ChangeDelay[x][y] = 0;
3856     ChangePage[x][y] = -1;
3857     CustomValue[x][y] = 0;              // initialized in InitField()
3858     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3859     AmoebaNr[x][y] = 0;
3860     WasJustMoving[x][y] = 0;
3861     WasJustFalling[x][y] = 0;
3862     CheckCollision[x][y] = 0;
3863     CheckImpact[x][y] = 0;
3864     Stop[x][y] = FALSE;
3865     Pushed[x][y] = FALSE;
3866
3867     ChangeCount[x][y] = 0;
3868     ChangeEvent[x][y] = -1;
3869
3870     ExplodePhase[x][y] = 0;
3871     ExplodeDelay[x][y] = 0;
3872     ExplodeField[x][y] = EX_TYPE_NONE;
3873
3874     RunnerVisit[x][y] = 0;
3875     PlayerVisit[x][y] = 0;
3876
3877     GfxFrame[x][y] = 0;
3878     GfxRandom[x][y] = INIT_GFX_RANDOM();
3879     GfxElement[x][y] = EL_UNDEFINED;
3880     GfxAction[x][y] = ACTION_DEFAULT;
3881     GfxDir[x][y] = MV_NONE;
3882     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3883   }
3884
3885   SCAN_PLAYFIELD(x, y)
3886   {
3887     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3888       emulate_bd = FALSE;
3889     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3890       emulate_sb = FALSE;
3891     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3892       emulate_sp = FALSE;
3893
3894     InitField(x, y, TRUE);
3895
3896     ResetGfxAnimation(x, y);
3897   }
3898
3899   InitBeltMovement();
3900
3901   for (i = 0; i < MAX_PLAYERS; i++)
3902   {
3903     struct PlayerInfo *player = &stored_player[i];
3904
3905     // set number of special actions for bored and sleeping animation
3906     player->num_special_action_bored =
3907       get_num_special_action(player->artwork_element,
3908                              ACTION_BORING_1, ACTION_BORING_LAST);
3909     player->num_special_action_sleeping =
3910       get_num_special_action(player->artwork_element,
3911                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3912   }
3913
3914   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3915                     emulate_sb ? EMU_SOKOBAN :
3916                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3917
3918   // initialize type of slippery elements
3919   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3920   {
3921     if (!IS_CUSTOM_ELEMENT(i))
3922     {
3923       // default: elements slip down either to the left or right randomly
3924       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3925
3926       // SP style elements prefer to slip down on the left side
3927       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3928         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3929
3930       // BD style elements prefer to slip down on the left side
3931       if (game.emulation == EMU_BOULDERDASH)
3932         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3933     }
3934   }
3935
3936   // initialize explosion and ignition delay
3937   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3938   {
3939     if (!IS_CUSTOM_ELEMENT(i))
3940     {
3941       int num_phase = 8;
3942       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3943                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3944                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3945       int last_phase = (num_phase + 1) * delay;
3946       int half_phase = (num_phase / 2) * delay;
3947
3948       element_info[i].explosion_delay = last_phase - 1;
3949       element_info[i].ignition_delay = half_phase;
3950
3951       if (i == EL_BLACK_ORB)
3952         element_info[i].ignition_delay = 1;
3953     }
3954   }
3955
3956   // correct non-moving belts to start moving left
3957   for (i = 0; i < NUM_BELTS; i++)
3958     if (game.belt_dir[i] == MV_NONE)
3959       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3960
3961 #if USE_NEW_PLAYER_ASSIGNMENTS
3962   // use preferred player also in local single-player mode
3963   if (!network.enabled && !game.team_mode)
3964   {
3965     int new_index_nr = setup.network_player_nr;
3966
3967     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3968     {
3969       for (i = 0; i < MAX_PLAYERS; i++)
3970         stored_player[i].connected_locally = FALSE;
3971
3972       stored_player[new_index_nr].connected_locally = TRUE;
3973     }
3974   }
3975
3976   for (i = 0; i < MAX_PLAYERS; i++)
3977   {
3978     stored_player[i].connected = FALSE;
3979
3980     // in network game mode, the local player might not be the first player
3981     if (stored_player[i].connected_locally)
3982       local_player = &stored_player[i];
3983   }
3984
3985   if (!network.enabled)
3986     local_player->connected = TRUE;
3987
3988   if (tape.playing)
3989   {
3990     for (i = 0; i < MAX_PLAYERS; i++)
3991       stored_player[i].connected = tape.player_participates[i];
3992   }
3993   else if (network.enabled)
3994   {
3995     // add team mode players connected over the network (needed for correct
3996     // assignment of player figures from level to locally playing players)
3997
3998     for (i = 0; i < MAX_PLAYERS; i++)
3999       if (stored_player[i].connected_network)
4000         stored_player[i].connected = TRUE;
4001   }
4002   else if (game.team_mode)
4003   {
4004     // try to guess locally connected team mode players (needed for correct
4005     // assignment of player figures from level to locally playing players)
4006
4007     for (i = 0; i < MAX_PLAYERS; i++)
4008       if (setup.input[i].use_joystick ||
4009           setup.input[i].key.left != KSYM_UNDEFINED)
4010         stored_player[i].connected = TRUE;
4011   }
4012
4013 #if DEBUG_INIT_PLAYER
4014   DebugPrintPlayerStatus("Player status after level initialization");
4015 #endif
4016
4017 #if DEBUG_INIT_PLAYER
4018   Debug("game:init:player", "Reassigning players ...");
4019 #endif
4020
4021   // check if any connected player was not found in playfield
4022   for (i = 0; i < MAX_PLAYERS; i++)
4023   {
4024     struct PlayerInfo *player = &stored_player[i];
4025
4026     if (player->connected && !player->present)
4027     {
4028       struct PlayerInfo *field_player = NULL;
4029
4030 #if DEBUG_INIT_PLAYER
4031       Debug("game:init:player",
4032             "- looking for field player for player %d ...", i + 1);
4033 #endif
4034
4035       // assign first free player found that is present in the playfield
4036
4037       // first try: look for unmapped playfield player that is not connected
4038       for (j = 0; j < MAX_PLAYERS; j++)
4039         if (field_player == NULL &&
4040             stored_player[j].present &&
4041             !stored_player[j].mapped &&
4042             !stored_player[j].connected)
4043           field_player = &stored_player[j];
4044
4045       // second try: look for *any* unmapped playfield player
4046       for (j = 0; j < MAX_PLAYERS; j++)
4047         if (field_player == NULL &&
4048             stored_player[j].present &&
4049             !stored_player[j].mapped)
4050           field_player = &stored_player[j];
4051
4052       if (field_player != NULL)
4053       {
4054         int jx = field_player->jx, jy = field_player->jy;
4055
4056 #if DEBUG_INIT_PLAYER
4057         Debug("game:init:player", "- found player %d",
4058               field_player->index_nr + 1);
4059 #endif
4060
4061         player->present = FALSE;
4062         player->active = FALSE;
4063
4064         field_player->present = TRUE;
4065         field_player->active = TRUE;
4066
4067         /*
4068         player->initial_element = field_player->initial_element;
4069         player->artwork_element = field_player->artwork_element;
4070
4071         player->block_last_field       = field_player->block_last_field;
4072         player->block_delay_adjustment = field_player->block_delay_adjustment;
4073         */
4074
4075         StorePlayer[jx][jy] = field_player->element_nr;
4076
4077         field_player->jx = field_player->last_jx = jx;
4078         field_player->jy = field_player->last_jy = jy;
4079
4080         if (local_player == player)
4081           local_player = field_player;
4082
4083         map_player_action[field_player->index_nr] = i;
4084
4085         field_player->mapped = TRUE;
4086
4087 #if DEBUG_INIT_PLAYER
4088         Debug("game:init:player", "- map_player_action[%d] == %d",
4089               field_player->index_nr + 1, i + 1);
4090 #endif
4091       }
4092     }
4093
4094     if (player->connected && player->present)
4095       player->mapped = TRUE;
4096   }
4097
4098 #if DEBUG_INIT_PLAYER
4099   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4100 #endif
4101
4102 #else
4103
4104   // check if any connected player was not found in playfield
4105   for (i = 0; i < MAX_PLAYERS; i++)
4106   {
4107     struct PlayerInfo *player = &stored_player[i];
4108
4109     if (player->connected && !player->present)
4110     {
4111       for (j = 0; j < MAX_PLAYERS; j++)
4112       {
4113         struct PlayerInfo *field_player = &stored_player[j];
4114         int jx = field_player->jx, jy = field_player->jy;
4115
4116         // assign first free player found that is present in the playfield
4117         if (field_player->present && !field_player->connected)
4118         {
4119           player->present = TRUE;
4120           player->active = TRUE;
4121
4122           field_player->present = FALSE;
4123           field_player->active = FALSE;
4124
4125           player->initial_element = field_player->initial_element;
4126           player->artwork_element = field_player->artwork_element;
4127
4128           player->block_last_field       = field_player->block_last_field;
4129           player->block_delay_adjustment = field_player->block_delay_adjustment;
4130
4131           StorePlayer[jx][jy] = player->element_nr;
4132
4133           player->jx = player->last_jx = jx;
4134           player->jy = player->last_jy = jy;
4135
4136           break;
4137         }
4138       }
4139     }
4140   }
4141 #endif
4142
4143 #if 0
4144   Debug("game:init:player", "local_player->present == %d",
4145         local_player->present);
4146 #endif
4147
4148   // set focus to local player for network games, else to all players
4149   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4150   game.centered_player_nr_next = game.centered_player_nr;
4151   game.set_centered_player = FALSE;
4152   game.set_centered_player_wrap = FALSE;
4153
4154   if (network_playing && tape.recording)
4155   {
4156     // store client dependent player focus when recording network games
4157     tape.centered_player_nr_next = game.centered_player_nr_next;
4158     tape.set_centered_player = TRUE;
4159   }
4160
4161   if (tape.playing)
4162   {
4163     // when playing a tape, eliminate all players who do not participate
4164
4165 #if USE_NEW_PLAYER_ASSIGNMENTS
4166
4167     if (!game.team_mode)
4168     {
4169       for (i = 0; i < MAX_PLAYERS; i++)
4170       {
4171         if (stored_player[i].active &&
4172             !tape.player_participates[map_player_action[i]])
4173         {
4174           struct PlayerInfo *player = &stored_player[i];
4175           int jx = player->jx, jy = player->jy;
4176
4177 #if DEBUG_INIT_PLAYER
4178           Debug("game:init:player", "Removing player %d at (%d, %d)",
4179                 i + 1, jx, jy);
4180 #endif
4181
4182           player->active = FALSE;
4183           StorePlayer[jx][jy] = 0;
4184           Tile[jx][jy] = EL_EMPTY;
4185         }
4186       }
4187     }
4188
4189 #else
4190
4191     for (i = 0; i < MAX_PLAYERS; i++)
4192     {
4193       if (stored_player[i].active &&
4194           !tape.player_participates[i])
4195       {
4196         struct PlayerInfo *player = &stored_player[i];
4197         int jx = player->jx, jy = player->jy;
4198
4199         player->active = FALSE;
4200         StorePlayer[jx][jy] = 0;
4201         Tile[jx][jy] = EL_EMPTY;
4202       }
4203     }
4204 #endif
4205   }
4206   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4207   {
4208     // when in single player mode, eliminate all but the local player
4209
4210     for (i = 0; i < MAX_PLAYERS; i++)
4211     {
4212       struct PlayerInfo *player = &stored_player[i];
4213
4214       if (player->active && player != local_player)
4215       {
4216         int jx = player->jx, jy = player->jy;
4217
4218         player->active = FALSE;
4219         player->present = FALSE;
4220
4221         StorePlayer[jx][jy] = 0;
4222         Tile[jx][jy] = EL_EMPTY;
4223       }
4224     }
4225   }
4226
4227   for (i = 0; i < MAX_PLAYERS; i++)
4228     if (stored_player[i].active)
4229       game.players_still_needed++;
4230
4231   if (level.solved_by_one_player)
4232     game.players_still_needed = 1;
4233
4234   // when recording the game, store which players take part in the game
4235   if (tape.recording)
4236   {
4237 #if USE_NEW_PLAYER_ASSIGNMENTS
4238     for (i = 0; i < MAX_PLAYERS; i++)
4239       if (stored_player[i].connected)
4240         tape.player_participates[i] = TRUE;
4241 #else
4242     for (i = 0; i < MAX_PLAYERS; i++)
4243       if (stored_player[i].active)
4244         tape.player_participates[i] = TRUE;
4245 #endif
4246   }
4247
4248 #if DEBUG_INIT_PLAYER
4249   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4250 #endif
4251
4252   if (BorderElement == EL_EMPTY)
4253   {
4254     SBX_Left = 0;
4255     SBX_Right = lev_fieldx - SCR_FIELDX;
4256     SBY_Upper = 0;
4257     SBY_Lower = lev_fieldy - SCR_FIELDY;
4258   }
4259   else
4260   {
4261     SBX_Left = -1;
4262     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4263     SBY_Upper = -1;
4264     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4265   }
4266
4267   if (full_lev_fieldx <= SCR_FIELDX)
4268     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4269   if (full_lev_fieldy <= SCR_FIELDY)
4270     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4271
4272   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4273     SBX_Left--;
4274   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4275     SBY_Upper--;
4276
4277   // if local player not found, look for custom element that might create
4278   // the player (make some assumptions about the right custom element)
4279   if (!local_player->present)
4280   {
4281     int start_x = 0, start_y = 0;
4282     int found_rating = 0;
4283     int found_element = EL_UNDEFINED;
4284     int player_nr = local_player->index_nr;
4285
4286     SCAN_PLAYFIELD(x, y)
4287     {
4288       int element = Tile[x][y];
4289       int content;
4290       int xx, yy;
4291       boolean is_player;
4292
4293       if (level.use_start_element[player_nr] &&
4294           level.start_element[player_nr] == element &&
4295           found_rating < 4)
4296       {
4297         start_x = x;
4298         start_y = y;
4299
4300         found_rating = 4;
4301         found_element = element;
4302       }
4303
4304       if (!IS_CUSTOM_ELEMENT(element))
4305         continue;
4306
4307       if (CAN_CHANGE(element))
4308       {
4309         for (i = 0; i < element_info[element].num_change_pages; i++)
4310         {
4311           // check for player created from custom element as single target
4312           content = element_info[element].change_page[i].target_element;
4313           is_player = ELEM_IS_PLAYER(content);
4314
4315           if (is_player && (found_rating < 3 ||
4316                             (found_rating == 3 && element < found_element)))
4317           {
4318             start_x = x;
4319             start_y = y;
4320
4321             found_rating = 3;
4322             found_element = element;
4323           }
4324         }
4325       }
4326
4327       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4328       {
4329         // check for player created from custom element as explosion content
4330         content = element_info[element].content.e[xx][yy];
4331         is_player = ELEM_IS_PLAYER(content);
4332
4333         if (is_player && (found_rating < 2 ||
4334                           (found_rating == 2 && element < found_element)))
4335         {
4336           start_x = x + xx - 1;
4337           start_y = y + yy - 1;
4338
4339           found_rating = 2;
4340           found_element = element;
4341         }
4342
4343         if (!CAN_CHANGE(element))
4344           continue;
4345
4346         for (i = 0; i < element_info[element].num_change_pages; i++)
4347         {
4348           // check for player created from custom element as extended target
4349           content =
4350             element_info[element].change_page[i].target_content.e[xx][yy];
4351
4352           is_player = ELEM_IS_PLAYER(content);
4353
4354           if (is_player && (found_rating < 1 ||
4355                             (found_rating == 1 && element < found_element)))
4356           {
4357             start_x = x + xx - 1;
4358             start_y = y + yy - 1;
4359
4360             found_rating = 1;
4361             found_element = element;
4362           }
4363         }
4364       }
4365     }
4366
4367     scroll_x = SCROLL_POSITION_X(start_x);
4368     scroll_y = SCROLL_POSITION_Y(start_y);
4369   }
4370   else
4371   {
4372     scroll_x = SCROLL_POSITION_X(local_player->jx);
4373     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4374   }
4375
4376   // !!! FIX THIS (START) !!!
4377   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4378   {
4379     InitGameEngine_EM();
4380   }
4381   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4382   {
4383     InitGameEngine_SP();
4384   }
4385   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4386   {
4387     InitGameEngine_MM();
4388   }
4389   else
4390   {
4391     DrawLevel(REDRAW_FIELD);
4392     DrawAllPlayers();
4393
4394     // after drawing the level, correct some elements
4395     if (game.timegate_time_left == 0)
4396       CloseAllOpenTimegates();
4397   }
4398
4399   // blit playfield from scroll buffer to normal back buffer for fading in
4400   BlitScreenToBitmap(backbuffer);
4401   // !!! FIX THIS (END) !!!
4402
4403   DrawMaskedBorder(fade_mask);
4404
4405   FadeIn(fade_mask);
4406
4407 #if 1
4408   // full screen redraw is required at this point in the following cases:
4409   // - special editor door undrawn when game was started from level editor
4410   // - drawing area (playfield) was changed and has to be removed completely
4411   redraw_mask = REDRAW_ALL;
4412   BackToFront();
4413 #endif
4414
4415   if (!game.restart_level)
4416   {
4417     // copy default game door content to main double buffer
4418
4419     // !!! CHECK AGAIN !!!
4420     SetPanelBackground();
4421     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4422     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4423   }
4424
4425   SetPanelBackground();
4426   SetDrawBackgroundMask(REDRAW_DOOR_1);
4427
4428   UpdateAndDisplayGameControlValues();
4429
4430   if (!game.restart_level)
4431   {
4432     UnmapGameButtons();
4433     UnmapTapeButtons();
4434
4435     FreeGameButtons();
4436     CreateGameButtons();
4437
4438     MapGameButtons();
4439     MapTapeButtons();
4440
4441     // copy actual game door content to door double buffer for OpenDoor()
4442     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4443
4444     OpenDoor(DOOR_OPEN_ALL);
4445
4446     KeyboardAutoRepeatOffUnlessAutoplay();
4447
4448 #if DEBUG_INIT_PLAYER
4449     DebugPrintPlayerStatus("Player status (final)");
4450 #endif
4451   }
4452
4453   UnmapAllGadgets();
4454
4455   MapGameButtons();
4456   MapTapeButtons();
4457
4458   if (!game.restart_level && !tape.playing)
4459   {
4460     LevelStats_incPlayed(level_nr);
4461
4462     SaveLevelSetup_SeriesInfo();
4463   }
4464
4465   game.restart_level = FALSE;
4466   game.restart_game_message = NULL;
4467
4468   game.request_active = FALSE;
4469   game.request_active_or_moving = FALSE;
4470
4471   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4472     InitGameActions_MM();
4473
4474   SaveEngineSnapshotToListInitial();
4475
4476   if (!game.restart_level)
4477   {
4478     PlaySound(SND_GAME_STARTING);
4479
4480     if (setup.sound_music)
4481       PlayLevelMusic();
4482   }
4483 }
4484
4485 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4486                         int actual_player_x, int actual_player_y)
4487 {
4488   // this is used for non-R'n'D game engines to update certain engine values
4489
4490   // needed to determine if sounds are played within the visible screen area
4491   scroll_x = actual_scroll_x;
4492   scroll_y = actual_scroll_y;
4493
4494   // needed to get player position for "follow finger" playing input method
4495   local_player->jx = actual_player_x;
4496   local_player->jy = actual_player_y;
4497 }
4498
4499 void InitMovDir(int x, int y)
4500 {
4501   int i, element = Tile[x][y];
4502   static int xy[4][2] =
4503   {
4504     {  0, +1 },
4505     { +1,  0 },
4506     {  0, -1 },
4507     { -1,  0 }
4508   };
4509   static int direction[3][4] =
4510   {
4511     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4512     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4513     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4514   };
4515
4516   switch (element)
4517   {
4518     case EL_BUG_RIGHT:
4519     case EL_BUG_UP:
4520     case EL_BUG_LEFT:
4521     case EL_BUG_DOWN:
4522       Tile[x][y] = EL_BUG;
4523       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4524       break;
4525
4526     case EL_SPACESHIP_RIGHT:
4527     case EL_SPACESHIP_UP:
4528     case EL_SPACESHIP_LEFT:
4529     case EL_SPACESHIP_DOWN:
4530       Tile[x][y] = EL_SPACESHIP;
4531       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4532       break;
4533
4534     case EL_BD_BUTTERFLY_RIGHT:
4535     case EL_BD_BUTTERFLY_UP:
4536     case EL_BD_BUTTERFLY_LEFT:
4537     case EL_BD_BUTTERFLY_DOWN:
4538       Tile[x][y] = EL_BD_BUTTERFLY;
4539       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4540       break;
4541
4542     case EL_BD_FIREFLY_RIGHT:
4543     case EL_BD_FIREFLY_UP:
4544     case EL_BD_FIREFLY_LEFT:
4545     case EL_BD_FIREFLY_DOWN:
4546       Tile[x][y] = EL_BD_FIREFLY;
4547       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4548       break;
4549
4550     case EL_PACMAN_RIGHT:
4551     case EL_PACMAN_UP:
4552     case EL_PACMAN_LEFT:
4553     case EL_PACMAN_DOWN:
4554       Tile[x][y] = EL_PACMAN;
4555       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4556       break;
4557
4558     case EL_YAMYAM_LEFT:
4559     case EL_YAMYAM_RIGHT:
4560     case EL_YAMYAM_UP:
4561     case EL_YAMYAM_DOWN:
4562       Tile[x][y] = EL_YAMYAM;
4563       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4564       break;
4565
4566     case EL_SP_SNIKSNAK:
4567       MovDir[x][y] = MV_UP;
4568       break;
4569
4570     case EL_SP_ELECTRON:
4571       MovDir[x][y] = MV_LEFT;
4572       break;
4573
4574     case EL_MOLE_LEFT:
4575     case EL_MOLE_RIGHT:
4576     case EL_MOLE_UP:
4577     case EL_MOLE_DOWN:
4578       Tile[x][y] = EL_MOLE;
4579       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4580       break;
4581
4582     case EL_SPRING_LEFT:
4583     case EL_SPRING_RIGHT:
4584       Tile[x][y] = EL_SPRING;
4585       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4586       break;
4587
4588     default:
4589       if (IS_CUSTOM_ELEMENT(element))
4590       {
4591         struct ElementInfo *ei = &element_info[element];
4592         int move_direction_initial = ei->move_direction_initial;
4593         int move_pattern = ei->move_pattern;
4594
4595         if (move_direction_initial == MV_START_PREVIOUS)
4596         {
4597           if (MovDir[x][y] != MV_NONE)
4598             return;
4599
4600           move_direction_initial = MV_START_AUTOMATIC;
4601         }
4602
4603         if (move_direction_initial == MV_START_RANDOM)
4604           MovDir[x][y] = 1 << RND(4);
4605         else if (move_direction_initial & MV_ANY_DIRECTION)
4606           MovDir[x][y] = move_direction_initial;
4607         else if (move_pattern == MV_ALL_DIRECTIONS ||
4608                  move_pattern == MV_TURNING_LEFT ||
4609                  move_pattern == MV_TURNING_RIGHT ||
4610                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4611                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4612                  move_pattern == MV_TURNING_RANDOM)
4613           MovDir[x][y] = 1 << RND(4);
4614         else if (move_pattern == MV_HORIZONTAL)
4615           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4616         else if (move_pattern == MV_VERTICAL)
4617           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4618         else if (move_pattern & MV_ANY_DIRECTION)
4619           MovDir[x][y] = element_info[element].move_pattern;
4620         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4621                  move_pattern == MV_ALONG_RIGHT_SIDE)
4622         {
4623           // use random direction as default start direction
4624           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4625             MovDir[x][y] = 1 << RND(4);
4626
4627           for (i = 0; i < NUM_DIRECTIONS; i++)
4628           {
4629             int x1 = x + xy[i][0];
4630             int y1 = y + xy[i][1];
4631
4632             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4633             {
4634               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4635                 MovDir[x][y] = direction[0][i];
4636               else
4637                 MovDir[x][y] = direction[1][i];
4638
4639               break;
4640             }
4641           }
4642         }                
4643       }
4644       else
4645       {
4646         MovDir[x][y] = 1 << RND(4);
4647
4648         if (element != EL_BUG &&
4649             element != EL_SPACESHIP &&
4650             element != EL_BD_BUTTERFLY &&
4651             element != EL_BD_FIREFLY)
4652           break;
4653
4654         for (i = 0; i < NUM_DIRECTIONS; i++)
4655         {
4656           int x1 = x + xy[i][0];
4657           int y1 = y + xy[i][1];
4658
4659           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4660           {
4661             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4662             {
4663               MovDir[x][y] = direction[0][i];
4664               break;
4665             }
4666             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4667                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4668             {
4669               MovDir[x][y] = direction[1][i];
4670               break;
4671             }
4672           }
4673         }
4674       }
4675       break;
4676   }
4677
4678   GfxDir[x][y] = MovDir[x][y];
4679 }
4680
4681 void InitAmoebaNr(int x, int y)
4682 {
4683   int i;
4684   int group_nr = AmoebaNeighbourNr(x, y);
4685
4686   if (group_nr == 0)
4687   {
4688     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4689     {
4690       if (AmoebaCnt[i] == 0)
4691       {
4692         group_nr = i;
4693         break;
4694       }
4695     }
4696   }
4697
4698   AmoebaNr[x][y] = group_nr;
4699   AmoebaCnt[group_nr]++;
4700   AmoebaCnt2[group_nr]++;
4701 }
4702
4703 static void LevelSolved(void)
4704 {
4705   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4706       game.players_still_needed > 0)
4707     return;
4708
4709   game.LevelSolved = TRUE;
4710   game.GameOver = TRUE;
4711 }
4712
4713 void GameWon(void)
4714 {
4715   static int time_count_steps;
4716   static int time, time_final;
4717   static float score, score_final; // needed for time score < 10 for 10 seconds
4718   static int health, health_final;
4719   static int game_over_delay_1 = 0;
4720   static int game_over_delay_2 = 0;
4721   static int game_over_delay_3 = 0;
4722   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4723   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4724
4725   if (!game.LevelSolved_GameWon)
4726   {
4727     int i;
4728
4729     // do not start end game actions before the player stops moving (to exit)
4730     if (local_player->active && local_player->MovPos)
4731       return;
4732
4733     game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4734     game.score_time_final = (level.use_step_counter ? TimePlayed :
4735                              TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4736
4737     game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4738                         game_em.lev->score :
4739                         level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4740                         game_mm.score :
4741                         game.score);
4742
4743     game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4744                          MM_HEALTH(game_mm.laser_overload_value) :
4745                          game.health);
4746
4747     game.LevelSolved_CountingTime = game.time_final;
4748     game.LevelSolved_CountingScore = game.score_final;
4749     game.LevelSolved_CountingHealth = game.health_final;
4750
4751     game.LevelSolved_GameWon = TRUE;
4752     game.LevelSolved_SaveTape = tape.recording;
4753     game.LevelSolved_SaveScore = !tape.playing;
4754
4755     if (!tape.playing)
4756     {
4757       LevelStats_incSolved(level_nr);
4758
4759       SaveLevelSetup_SeriesInfo();
4760     }
4761
4762     if (tape.auto_play)         // tape might already be stopped here
4763       tape.auto_play_level_solved = TRUE;
4764
4765     TapeStop();
4766
4767     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4768     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4769     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4770
4771     time = time_final = game.time_final;
4772     score = score_final = game.score_final;
4773     health = health_final = game.health_final;
4774
4775     if (time_score > 0)
4776     {
4777       int time_final_max = 999;
4778       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4779       int time_frames = 0;
4780       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4781       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4782
4783       if (TimeLeft > 0)
4784       {
4785         time_final = 0;
4786         time_frames = time_frames_left;
4787       }
4788       else if (game.no_time_limit && TimePlayed < time_final_max)
4789       {
4790         time_final = time_final_max;
4791         time_frames = time_frames_final_max - time_frames_played;
4792       }
4793
4794       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4795
4796       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4797
4798       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4799       {
4800         health_final = 0;
4801         score_final += health * time_score;
4802       }
4803
4804       game.score_final = score_final;
4805       game.health_final = health_final;
4806     }
4807
4808     if (level_editor_test_game || !setup.count_score_after_game)
4809     {
4810       time = time_final;
4811       score = score_final;
4812
4813       game.LevelSolved_CountingTime = time;
4814       game.LevelSolved_CountingScore = score;
4815
4816       game_panel_controls[GAME_PANEL_TIME].value = time;
4817       game_panel_controls[GAME_PANEL_SCORE].value = score;
4818
4819       DisplayGameControlValues();
4820     }
4821
4822     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4823     {
4824       // check if last player has left the level
4825       if (game.exit_x >= 0 &&
4826           game.exit_y >= 0)
4827       {
4828         int x = game.exit_x;
4829         int y = game.exit_y;
4830         int element = Tile[x][y];
4831
4832         // close exit door after last player
4833         if ((game.all_players_gone &&
4834              (element == EL_EXIT_OPEN ||
4835               element == EL_SP_EXIT_OPEN ||
4836               element == EL_STEEL_EXIT_OPEN)) ||
4837             element == EL_EM_EXIT_OPEN ||
4838             element == EL_EM_STEEL_EXIT_OPEN)
4839         {
4840
4841           Tile[x][y] =
4842             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4843              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4844              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4845              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4846              EL_EM_STEEL_EXIT_CLOSING);
4847
4848           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4849         }
4850
4851         // player disappears
4852         DrawLevelField(x, y);
4853       }
4854
4855       for (i = 0; i < MAX_PLAYERS; i++)
4856       {
4857         struct PlayerInfo *player = &stored_player[i];
4858
4859         if (player->present)
4860         {
4861           RemovePlayer(player);
4862
4863           // player disappears
4864           DrawLevelField(player->jx, player->jy);
4865         }
4866       }
4867     }
4868
4869     PlaySound(SND_GAME_WINNING);
4870   }
4871
4872   if (setup.count_score_after_game)
4873   {
4874     if (time != time_final)
4875     {
4876       if (game_over_delay_1 > 0)
4877       {
4878         game_over_delay_1--;
4879
4880         return;
4881       }
4882
4883       int time_to_go = ABS(time_final - time);
4884       int time_count_dir = (time < time_final ? +1 : -1);
4885
4886       if (time_to_go < time_count_steps)
4887         time_count_steps = 1;
4888
4889       time  += time_count_steps * time_count_dir;
4890       score += time_count_steps * time_score;
4891
4892       // set final score to correct rounding differences after counting score
4893       if (time == time_final)
4894         score = score_final;
4895
4896       game.LevelSolved_CountingTime = time;
4897       game.LevelSolved_CountingScore = score;
4898
4899       game_panel_controls[GAME_PANEL_TIME].value = time;
4900       game_panel_controls[GAME_PANEL_SCORE].value = score;
4901
4902       DisplayGameControlValues();
4903
4904       if (time == time_final)
4905         StopSound(SND_GAME_LEVELTIME_BONUS);
4906       else if (setup.sound_loops)
4907         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4908       else
4909         PlaySound(SND_GAME_LEVELTIME_BONUS);
4910
4911       return;
4912     }
4913
4914     if (health != health_final)
4915     {
4916       if (game_over_delay_2 > 0)
4917       {
4918         game_over_delay_2--;
4919
4920         return;
4921       }
4922
4923       int health_count_dir = (health < health_final ? +1 : -1);
4924
4925       health += health_count_dir;
4926       score  += time_score;
4927
4928       game.LevelSolved_CountingHealth = health;
4929       game.LevelSolved_CountingScore = score;
4930
4931       game_panel_controls[GAME_PANEL_HEALTH].value = health;
4932       game_panel_controls[GAME_PANEL_SCORE].value = score;
4933
4934       DisplayGameControlValues();
4935
4936       if (health == health_final)
4937         StopSound(SND_GAME_LEVELTIME_BONUS);
4938       else if (setup.sound_loops)
4939         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4940       else
4941         PlaySound(SND_GAME_LEVELTIME_BONUS);
4942
4943       return;
4944     }
4945   }
4946
4947   game.panel.active = FALSE;
4948
4949   if (game_over_delay_3 > 0)
4950   {
4951     game_over_delay_3--;
4952
4953     return;
4954   }
4955
4956   GameEnd();
4957 }
4958
4959 void GameEnd(void)
4960 {
4961   // used instead of "level_nr" (needed for network games)
4962   int last_level_nr = levelset.level_nr;
4963
4964   game.LevelSolved_GameEnd = TRUE;
4965
4966   if (game.LevelSolved_SaveTape)
4967   {
4968     // make sure that request dialog to save tape does not open door again
4969     if (!global.use_envelope_request)
4970       CloseDoor(DOOR_CLOSE_1);
4971
4972     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4973
4974     // set unique basename for score tape (also saved in high score table)
4975     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
4976   }
4977
4978   // if no tape is to be saved, close both doors simultaneously
4979   CloseDoor(DOOR_CLOSE_ALL);
4980
4981   if (level_editor_test_game)
4982   {
4983     SetGameStatus(GAME_MODE_MAIN);
4984
4985     DrawMainMenu();
4986
4987     return;
4988   }
4989
4990   if (!game.LevelSolved_SaveScore)
4991   {
4992     SetGameStatus(GAME_MODE_MAIN);
4993
4994     DrawMainMenu();
4995
4996     return;
4997   }
4998
4999   if (level_nr == leveldir_current->handicap_level)
5000   {
5001     leveldir_current->handicap_level++;
5002
5003     SaveLevelSetup_SeriesInfo();
5004   }
5005
5006   // save score and score tape before potentially erasing tape below
5007   NewHighScore(last_level_nr);
5008
5009   if (setup.increment_levels &&
5010       level_nr < leveldir_current->last_level &&
5011       !network_playing)
5012   {
5013     level_nr++;         // advance to next level
5014     TapeErase();        // start with empty tape
5015
5016     if (setup.auto_play_next_level)
5017     {
5018       LoadLevel(level_nr);
5019
5020       SaveLevelSetup_SeriesInfo();
5021     }
5022   }
5023
5024   if (scores.last_added >= 0 && setup.show_scores_after_game)
5025   {
5026     SetGameStatus(GAME_MODE_SCORES);
5027
5028     DrawHallOfFame(last_level_nr);
5029   }
5030   else if (setup.auto_play_next_level && setup.increment_levels &&
5031            last_level_nr < leveldir_current->last_level &&
5032            !network_playing)
5033   {
5034     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5035   }
5036   else
5037   {
5038     SetGameStatus(GAME_MODE_MAIN);
5039
5040     DrawMainMenu();
5041   }
5042 }
5043
5044 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5045                          boolean one_score_entry_per_name)
5046 {
5047   int i;
5048
5049   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5050     return -1;
5051
5052   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5053   {
5054     struct ScoreEntry *entry = &list->entry[i];
5055     boolean score_is_better = (new_entry->score >  entry->score);
5056     boolean score_is_equal  = (new_entry->score == entry->score);
5057     boolean time_is_better  = (new_entry->time  <  entry->time);
5058     boolean time_is_equal   = (new_entry->time  == entry->time);
5059     boolean better_by_score = (score_is_better ||
5060                                (score_is_equal && time_is_better));
5061     boolean better_by_time  = (time_is_better ||
5062                                (time_is_equal && score_is_better));
5063     boolean is_better = (level.rate_time_over_score ? better_by_time :
5064                          better_by_score);
5065     boolean entry_is_empty = (entry->score == 0 &&
5066                               entry->time == 0);
5067
5068     // prevent adding server score entries if also existing in local score file
5069     if (strEqual(new_entry->tape_basename, entry->tape_basename))
5070       return -1;
5071
5072     if (is_better || entry_is_empty)
5073     {
5074       // player has made it to the hall of fame
5075
5076       if (i < MAX_SCORE_ENTRIES - 1)
5077       {
5078         int m = MAX_SCORE_ENTRIES - 1;
5079         int l;
5080
5081         if (one_score_entry_per_name)
5082         {
5083           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5084             if (strEqual(list->entry[l].name, new_entry->name))
5085               m = l;
5086
5087           if (m == i)   // player's new highscore overwrites his old one
5088             goto put_into_list;
5089         }
5090
5091         for (l = m; l > i; l--)
5092           list->entry[l] = list->entry[l - 1];
5093       }
5094
5095       put_into_list:
5096
5097       *entry = *new_entry;
5098
5099       return i;
5100     }
5101     else if (one_score_entry_per_name &&
5102              strEqual(entry->name, new_entry->name))
5103     {
5104       // player already in high score list with better score or time
5105
5106       return -1;
5107     }
5108   }
5109
5110   return -1;
5111 }
5112
5113 void NewHighScore(int level_nr)
5114 {
5115   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5116   boolean one_per_name = !program.many_scores_per_name;
5117
5118   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5119   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5120
5121   new_entry.score = game.score_final;
5122   new_entry.time = game.score_time_final;
5123
5124   LoadScore(level_nr);
5125
5126   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5127
5128   if (scores.last_added >= 0)
5129   {
5130     SaveScore(level_nr);
5131
5132     if (game.LevelSolved_SaveTape)
5133     {
5134       SaveScoreTape(level_nr);
5135       SaveServerScore(level_nr);
5136     }
5137
5138     // store last added local score entry (before merging server scores)
5139     scores.last_added_local = scores.last_added;
5140   }
5141 }
5142
5143 void MergeServerScore(void)
5144 {
5145   boolean one_per_name = !program.many_scores_per_name;
5146   int i;
5147
5148   for (i = 0; i < server_scores.num_entries; i++)
5149   {
5150     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5151
5152     if (pos >= 0 && pos <= scores.last_added)
5153       scores.last_added++;
5154   }
5155
5156   if (scores.last_added >= MAX_SCORE_ENTRIES)
5157     scores.last_added = -1;
5158 }
5159
5160 static int getElementMoveStepsizeExt(int x, int y, int direction)
5161 {
5162   int element = Tile[x][y];
5163   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5164   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5165   int horiz_move = (dx != 0);
5166   int sign = (horiz_move ? dx : dy);
5167   int step = sign * element_info[element].move_stepsize;
5168
5169   // special values for move stepsize for spring and things on conveyor belt
5170   if (horiz_move)
5171   {
5172     if (CAN_FALL(element) &&
5173         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5174       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5175     else if (element == EL_SPRING)
5176       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5177   }
5178
5179   return step;
5180 }
5181
5182 static int getElementMoveStepsize(int x, int y)
5183 {
5184   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5185 }
5186
5187 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5188 {
5189   if (player->GfxAction != action || player->GfxDir != dir)
5190   {
5191     player->GfxAction = action;
5192     player->GfxDir = dir;
5193     player->Frame = 0;
5194     player->StepFrame = 0;
5195   }
5196 }
5197
5198 static void ResetGfxFrame(int x, int y)
5199 {
5200   // profiling showed that "autotest" spends 10~20% of its time in this function
5201   if (DrawingDeactivatedField())
5202     return;
5203
5204   int element = Tile[x][y];
5205   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5206
5207   if (graphic_info[graphic].anim_global_sync)
5208     GfxFrame[x][y] = FrameCounter;
5209   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5210     GfxFrame[x][y] = CustomValue[x][y];
5211   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5212     GfxFrame[x][y] = element_info[element].collect_score;
5213   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5214     GfxFrame[x][y] = ChangeDelay[x][y];
5215 }
5216
5217 static void ResetGfxAnimation(int x, int y)
5218 {
5219   GfxAction[x][y] = ACTION_DEFAULT;
5220   GfxDir[x][y] = MovDir[x][y];
5221   GfxFrame[x][y] = 0;
5222
5223   ResetGfxFrame(x, y);
5224 }
5225
5226 static void ResetRandomAnimationValue(int x, int y)
5227 {
5228   GfxRandom[x][y] = INIT_GFX_RANDOM();
5229 }
5230
5231 static void InitMovingField(int x, int y, int direction)
5232 {
5233   int element = Tile[x][y];
5234   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5235   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5236   int newx = x + dx;
5237   int newy = y + dy;
5238   boolean is_moving_before, is_moving_after;
5239
5240   // check if element was/is moving or being moved before/after mode change
5241   is_moving_before = (WasJustMoving[x][y] != 0);
5242   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5243
5244   // reset animation only for moving elements which change direction of moving
5245   // or which just started or stopped moving
5246   // (else CEs with property "can move" / "not moving" are reset each frame)
5247   if (is_moving_before != is_moving_after ||
5248       direction != MovDir[x][y])
5249     ResetGfxAnimation(x, y);
5250
5251   MovDir[x][y] = direction;
5252   GfxDir[x][y] = direction;
5253
5254   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5255                      direction == MV_DOWN && CAN_FALL(element) ?
5256                      ACTION_FALLING : ACTION_MOVING);
5257
5258   // this is needed for CEs with property "can move" / "not moving"
5259
5260   if (is_moving_after)
5261   {
5262     if (Tile[newx][newy] == EL_EMPTY)
5263       Tile[newx][newy] = EL_BLOCKED;
5264
5265     MovDir[newx][newy] = MovDir[x][y];
5266
5267     CustomValue[newx][newy] = CustomValue[x][y];
5268
5269     GfxFrame[newx][newy] = GfxFrame[x][y];
5270     GfxRandom[newx][newy] = GfxRandom[x][y];
5271     GfxAction[newx][newy] = GfxAction[x][y];
5272     GfxDir[newx][newy] = GfxDir[x][y];
5273   }
5274 }
5275
5276 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5277 {
5278   int direction = MovDir[x][y];
5279   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5280   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5281
5282   *goes_to_x = newx;
5283   *goes_to_y = newy;
5284 }
5285
5286 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5287 {
5288   int oldx = x, oldy = y;
5289   int direction = MovDir[x][y];
5290
5291   if (direction == MV_LEFT)
5292     oldx++;
5293   else if (direction == MV_RIGHT)
5294     oldx--;
5295   else if (direction == MV_UP)
5296     oldy++;
5297   else if (direction == MV_DOWN)
5298     oldy--;
5299
5300   *comes_from_x = oldx;
5301   *comes_from_y = oldy;
5302 }
5303
5304 static int MovingOrBlocked2Element(int x, int y)
5305 {
5306   int element = Tile[x][y];
5307
5308   if (element == EL_BLOCKED)
5309   {
5310     int oldx, oldy;
5311
5312     Blocked2Moving(x, y, &oldx, &oldy);
5313     return Tile[oldx][oldy];
5314   }
5315   else
5316     return element;
5317 }
5318
5319 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5320 {
5321   // like MovingOrBlocked2Element(), but if element is moving
5322   // and (x,y) is the field the moving element is just leaving,
5323   // return EL_BLOCKED instead of the element value
5324   int element = Tile[x][y];
5325
5326   if (IS_MOVING(x, y))
5327   {
5328     if (element == EL_BLOCKED)
5329     {
5330       int oldx, oldy;
5331
5332       Blocked2Moving(x, y, &oldx, &oldy);
5333       return Tile[oldx][oldy];
5334     }
5335     else
5336       return EL_BLOCKED;
5337   }
5338   else
5339     return element;
5340 }
5341
5342 static void RemoveField(int x, int y)
5343 {
5344   Tile[x][y] = EL_EMPTY;
5345
5346   MovPos[x][y] = 0;
5347   MovDir[x][y] = 0;
5348   MovDelay[x][y] = 0;
5349
5350   CustomValue[x][y] = 0;
5351
5352   AmoebaNr[x][y] = 0;
5353   ChangeDelay[x][y] = 0;
5354   ChangePage[x][y] = -1;
5355   Pushed[x][y] = FALSE;
5356
5357   GfxElement[x][y] = EL_UNDEFINED;
5358   GfxAction[x][y] = ACTION_DEFAULT;
5359   GfxDir[x][y] = MV_NONE;
5360 }
5361
5362 static void RemoveMovingField(int x, int y)
5363 {
5364   int oldx = x, oldy = y, newx = x, newy = y;
5365   int element = Tile[x][y];
5366   int next_element = EL_UNDEFINED;
5367
5368   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5369     return;
5370
5371   if (IS_MOVING(x, y))
5372   {
5373     Moving2Blocked(x, y, &newx, &newy);
5374
5375     if (Tile[newx][newy] != EL_BLOCKED)
5376     {
5377       // element is moving, but target field is not free (blocked), but
5378       // already occupied by something different (example: acid pool);
5379       // in this case, only remove the moving field, but not the target
5380
5381       RemoveField(oldx, oldy);
5382
5383       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5384
5385       TEST_DrawLevelField(oldx, oldy);
5386
5387       return;
5388     }
5389   }
5390   else if (element == EL_BLOCKED)
5391   {
5392     Blocked2Moving(x, y, &oldx, &oldy);
5393     if (!IS_MOVING(oldx, oldy))
5394       return;
5395   }
5396
5397   if (element == EL_BLOCKED &&
5398       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5399        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5400        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5401        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5402        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5403        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5404     next_element = get_next_element(Tile[oldx][oldy]);
5405
5406   RemoveField(oldx, oldy);
5407   RemoveField(newx, newy);
5408
5409   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5410
5411   if (next_element != EL_UNDEFINED)
5412     Tile[oldx][oldy] = next_element;
5413
5414   TEST_DrawLevelField(oldx, oldy);
5415   TEST_DrawLevelField(newx, newy);
5416 }
5417
5418 void DrawDynamite(int x, int y)
5419 {
5420   int sx = SCREENX(x), sy = SCREENY(y);
5421   int graphic = el2img(Tile[x][y]);
5422   int frame;
5423
5424   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5425     return;
5426
5427   if (IS_WALKABLE_INSIDE(Back[x][y]))
5428     return;
5429
5430   if (Back[x][y])
5431     DrawLevelElement(x, y, Back[x][y]);
5432   else if (Store[x][y])
5433     DrawLevelElement(x, y, Store[x][y]);
5434   else if (game.use_masked_elements)
5435     DrawLevelElement(x, y, EL_EMPTY);
5436
5437   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5438
5439   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5440     DrawGraphicThruMask(sx, sy, graphic, frame);
5441   else
5442     DrawGraphic(sx, sy, graphic, frame);
5443 }
5444
5445 static void CheckDynamite(int x, int y)
5446 {
5447   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5448   {
5449     MovDelay[x][y]--;
5450
5451     if (MovDelay[x][y] != 0)
5452     {
5453       DrawDynamite(x, y);
5454       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5455
5456       return;
5457     }
5458   }
5459
5460   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5461
5462   Bang(x, y);
5463 }
5464
5465 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5466 {
5467   boolean num_checked_players = 0;
5468   int i;
5469
5470   for (i = 0; i < MAX_PLAYERS; i++)
5471   {
5472     if (stored_player[i].active)
5473     {
5474       int sx = stored_player[i].jx;
5475       int sy = stored_player[i].jy;
5476
5477       if (num_checked_players == 0)
5478       {
5479         *sx1 = *sx2 = sx;
5480         *sy1 = *sy2 = sy;
5481       }
5482       else
5483       {
5484         *sx1 = MIN(*sx1, sx);
5485         *sy1 = MIN(*sy1, sy);
5486         *sx2 = MAX(*sx2, sx);
5487         *sy2 = MAX(*sy2, sy);
5488       }
5489
5490       num_checked_players++;
5491     }
5492   }
5493 }
5494
5495 static boolean checkIfAllPlayersFitToScreen_RND(void)
5496 {
5497   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5498
5499   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5500
5501   return (sx2 - sx1 < SCR_FIELDX &&
5502           sy2 - sy1 < SCR_FIELDY);
5503 }
5504
5505 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5506 {
5507   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5508
5509   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5510
5511   *sx = (sx1 + sx2) / 2;
5512   *sy = (sy1 + sy2) / 2;
5513 }
5514
5515 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5516                                boolean center_screen, boolean quick_relocation)
5517 {
5518   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5519   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5520   boolean no_delay = (tape.warp_forward);
5521   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5522   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5523   int new_scroll_x, new_scroll_y;
5524
5525   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5526   {
5527     // case 1: quick relocation inside visible screen (without scrolling)
5528
5529     RedrawPlayfield();
5530
5531     return;
5532   }
5533
5534   if (!level.shifted_relocation || center_screen)
5535   {
5536     // relocation _with_ centering of screen
5537
5538     new_scroll_x = SCROLL_POSITION_X(x);
5539     new_scroll_y = SCROLL_POSITION_Y(y);
5540   }
5541   else
5542   {
5543     // relocation _without_ centering of screen
5544
5545     int center_scroll_x = SCROLL_POSITION_X(old_x);
5546     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5547     int offset_x = x + (scroll_x - center_scroll_x);
5548     int offset_y = y + (scroll_y - center_scroll_y);
5549
5550     // for new screen position, apply previous offset to center position
5551     new_scroll_x = SCROLL_POSITION_X(offset_x);
5552     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5553   }
5554
5555   if (quick_relocation)
5556   {
5557     // case 2: quick relocation (redraw without visible scrolling)
5558
5559     scroll_x = new_scroll_x;
5560     scroll_y = new_scroll_y;
5561
5562     RedrawPlayfield();
5563
5564     return;
5565   }
5566
5567   // case 3: visible relocation (with scrolling to new position)
5568
5569   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5570
5571   SetVideoFrameDelay(wait_delay_value);
5572
5573   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5574   {
5575     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5576     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5577
5578     if (dx == 0 && dy == 0)             // no scrolling needed at all
5579       break;
5580
5581     scroll_x -= dx;
5582     scroll_y -= dy;
5583
5584     // set values for horizontal/vertical screen scrolling (half tile size)
5585     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5586     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5587     int pos_x = dx * TILEX / 2;
5588     int pos_y = dy * TILEY / 2;
5589     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5590     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5591
5592     ScrollLevel(dx, dy);
5593     DrawAllPlayers();
5594
5595     // scroll in two steps of half tile size to make things smoother
5596     BlitScreenToBitmapExt_RND(window, fx, fy);
5597
5598     // scroll second step to align at full tile size
5599     BlitScreenToBitmap(window);
5600   }
5601
5602   DrawAllPlayers();
5603   BackToFront();
5604
5605   SetVideoFrameDelay(frame_delay_value_old);
5606 }
5607
5608 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5609 {
5610   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5611   int player_nr = GET_PLAYER_NR(el_player);
5612   struct PlayerInfo *player = &stored_player[player_nr];
5613   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5614   boolean no_delay = (tape.warp_forward);
5615   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5616   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5617   int old_jx = player->jx;
5618   int old_jy = player->jy;
5619   int old_element = Tile[old_jx][old_jy];
5620   int element = Tile[jx][jy];
5621   boolean player_relocated = (old_jx != jx || old_jy != jy);
5622
5623   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5624   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5625   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5626   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5627   int leave_side_horiz = move_dir_horiz;
5628   int leave_side_vert  = move_dir_vert;
5629   int enter_side = enter_side_horiz | enter_side_vert;
5630   int leave_side = leave_side_horiz | leave_side_vert;
5631
5632   if (player->buried)           // do not reanimate dead player
5633     return;
5634
5635   if (!player_relocated)        // no need to relocate the player
5636     return;
5637
5638   if (IS_PLAYER(jx, jy))        // player already placed at new position
5639   {
5640     RemoveField(jx, jy);        // temporarily remove newly placed player
5641     DrawLevelField(jx, jy);
5642   }
5643
5644   if (player->present)
5645   {
5646     while (player->MovPos)
5647     {
5648       ScrollPlayer(player, SCROLL_GO_ON);
5649       ScrollScreen(NULL, SCROLL_GO_ON);
5650
5651       AdvanceFrameAndPlayerCounters(player->index_nr);
5652
5653       DrawPlayer(player);
5654
5655       BackToFront_WithFrameDelay(wait_delay_value);
5656     }
5657
5658     DrawPlayer(player);         // needed here only to cleanup last field
5659     DrawLevelField(player->jx, player->jy);     // remove player graphic
5660
5661     player->is_moving = FALSE;
5662   }
5663
5664   if (IS_CUSTOM_ELEMENT(old_element))
5665     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5666                                CE_LEFT_BY_PLAYER,
5667                                player->index_bit, leave_side);
5668
5669   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5670                                       CE_PLAYER_LEAVES_X,
5671                                       player->index_bit, leave_side);
5672
5673   Tile[jx][jy] = el_player;
5674   InitPlayerField(jx, jy, el_player, TRUE);
5675
5676   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5677      possible that the relocation target field did not contain a player element,
5678      but a walkable element, to which the new player was relocated -- in this
5679      case, restore that (already initialized!) element on the player field */
5680   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5681   {
5682     Tile[jx][jy] = element;     // restore previously existing element
5683   }
5684
5685   // only visually relocate centered player
5686   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5687                      FALSE, level.instant_relocation);
5688
5689   TestIfPlayerTouchesBadThing(jx, jy);
5690   TestIfPlayerTouchesCustomElement(jx, jy);
5691
5692   if (IS_CUSTOM_ELEMENT(element))
5693     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5694                                player->index_bit, enter_side);
5695
5696   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5697                                       player->index_bit, enter_side);
5698
5699   if (player->is_switching)
5700   {
5701     /* ensure that relocation while still switching an element does not cause
5702        a new element to be treated as also switched directly after relocation
5703        (this is important for teleporter switches that teleport the player to
5704        a place where another teleporter switch is in the same direction, which
5705        would then incorrectly be treated as immediately switched before the
5706        direction key that caused the switch was released) */
5707
5708     player->switch_x += jx - old_jx;
5709     player->switch_y += jy - old_jy;
5710   }
5711 }
5712
5713 static void Explode(int ex, int ey, int phase, int mode)
5714 {
5715   int x, y;
5716   int last_phase;
5717   int border_element;
5718
5719   // !!! eliminate this variable !!!
5720   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5721
5722   if (game.explosions_delayed)
5723   {
5724     ExplodeField[ex][ey] = mode;
5725     return;
5726   }
5727
5728   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5729   {
5730     int center_element = Tile[ex][ey];
5731     int artwork_element, explosion_element;     // set these values later
5732
5733     // remove things displayed in background while burning dynamite
5734     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5735       Back[ex][ey] = 0;
5736
5737     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5738     {
5739       // put moving element to center field (and let it explode there)
5740       center_element = MovingOrBlocked2Element(ex, ey);
5741       RemoveMovingField(ex, ey);
5742       Tile[ex][ey] = center_element;
5743     }
5744
5745     // now "center_element" is finally determined -- set related values now
5746     artwork_element = center_element;           // for custom player artwork
5747     explosion_element = center_element;         // for custom player artwork
5748
5749     if (IS_PLAYER(ex, ey))
5750     {
5751       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5752
5753       artwork_element = stored_player[player_nr].artwork_element;
5754
5755       if (level.use_explosion_element[player_nr])
5756       {
5757         explosion_element = level.explosion_element[player_nr];
5758         artwork_element = explosion_element;
5759       }
5760     }
5761
5762     if (mode == EX_TYPE_NORMAL ||
5763         mode == EX_TYPE_CENTER ||
5764         mode == EX_TYPE_CROSS)
5765       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5766
5767     last_phase = element_info[explosion_element].explosion_delay + 1;
5768
5769     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5770     {
5771       int xx = x - ex + 1;
5772       int yy = y - ey + 1;
5773       int element;
5774
5775       if (!IN_LEV_FIELD(x, y) ||
5776           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5777           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5778         continue;
5779
5780       element = Tile[x][y];
5781
5782       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5783       {
5784         element = MovingOrBlocked2Element(x, y);
5785
5786         if (!IS_EXPLOSION_PROOF(element))
5787           RemoveMovingField(x, y);
5788       }
5789
5790       // indestructible elements can only explode in center (but not flames)
5791       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5792                                            mode == EX_TYPE_BORDER)) ||
5793           element == EL_FLAMES)
5794         continue;
5795
5796       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5797          behaviour, for example when touching a yamyam that explodes to rocks
5798          with active deadly shield, a rock is created under the player !!! */
5799       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5800 #if 0
5801       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5802           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5803            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5804 #else
5805       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5806 #endif
5807       {
5808         if (IS_ACTIVE_BOMB(element))
5809         {
5810           // re-activate things under the bomb like gate or penguin
5811           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5812           Back[x][y] = 0;
5813         }
5814
5815         continue;
5816       }
5817
5818       // save walkable background elements while explosion on same tile
5819       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5820           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5821         Back[x][y] = element;
5822
5823       // ignite explodable elements reached by other explosion
5824       if (element == EL_EXPLOSION)
5825         element = Store2[x][y];
5826
5827       if (AmoebaNr[x][y] &&
5828           (element == EL_AMOEBA_FULL ||
5829            element == EL_BD_AMOEBA ||
5830            element == EL_AMOEBA_GROWING))
5831       {
5832         AmoebaCnt[AmoebaNr[x][y]]--;
5833         AmoebaCnt2[AmoebaNr[x][y]]--;
5834       }
5835
5836       RemoveField(x, y);
5837
5838       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5839       {
5840         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5841
5842         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5843
5844         if (PLAYERINFO(ex, ey)->use_murphy)
5845           Store[x][y] = EL_EMPTY;
5846       }
5847
5848       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5849       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5850       else if (ELEM_IS_PLAYER(center_element))
5851         Store[x][y] = EL_EMPTY;
5852       else if (center_element == EL_YAMYAM)
5853         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5854       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5855         Store[x][y] = element_info[center_element].content.e[xx][yy];
5856 #if 1
5857       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5858       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5859       // otherwise) -- FIX THIS !!!
5860       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5861         Store[x][y] = element_info[element].content.e[1][1];
5862 #else
5863       else if (!CAN_EXPLODE(element))
5864         Store[x][y] = element_info[element].content.e[1][1];
5865 #endif
5866       else
5867         Store[x][y] = EL_EMPTY;
5868
5869       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5870           center_element == EL_AMOEBA_TO_DIAMOND)
5871         Store2[x][y] = element;
5872
5873       Tile[x][y] = EL_EXPLOSION;
5874       GfxElement[x][y] = artwork_element;
5875
5876       ExplodePhase[x][y] = 1;
5877       ExplodeDelay[x][y] = last_phase;
5878
5879       Stop[x][y] = TRUE;
5880     }
5881
5882     if (center_element == EL_YAMYAM)
5883       game.yamyam_content_nr =
5884         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5885
5886     return;
5887   }
5888
5889   if (Stop[ex][ey])
5890     return;
5891
5892   x = ex;
5893   y = ey;
5894
5895   if (phase == 1)
5896     GfxFrame[x][y] = 0;         // restart explosion animation
5897
5898   last_phase = ExplodeDelay[x][y];
5899
5900   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5901
5902   // this can happen if the player leaves an explosion just in time
5903   if (GfxElement[x][y] == EL_UNDEFINED)
5904     GfxElement[x][y] = EL_EMPTY;
5905
5906   border_element = Store2[x][y];
5907   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5908     border_element = StorePlayer[x][y];
5909
5910   if (phase == element_info[border_element].ignition_delay ||
5911       phase == last_phase)
5912   {
5913     boolean border_explosion = FALSE;
5914
5915     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5916         !PLAYER_EXPLOSION_PROTECTED(x, y))
5917     {
5918       KillPlayerUnlessExplosionProtected(x, y);
5919       border_explosion = TRUE;
5920     }
5921     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5922     {
5923       Tile[x][y] = Store2[x][y];
5924       Store2[x][y] = 0;
5925       Bang(x, y);
5926       border_explosion = TRUE;
5927     }
5928     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5929     {
5930       AmoebaToDiamond(x, y);
5931       Store2[x][y] = 0;
5932       border_explosion = TRUE;
5933     }
5934
5935     // if an element just explodes due to another explosion (chain-reaction),
5936     // do not immediately end the new explosion when it was the last frame of
5937     // the explosion (as it would be done in the following "if"-statement!)
5938     if (border_explosion && phase == last_phase)
5939       return;
5940   }
5941
5942   if (phase == last_phase)
5943   {
5944     int element;
5945
5946     element = Tile[x][y] = Store[x][y];
5947     Store[x][y] = Store2[x][y] = 0;
5948     GfxElement[x][y] = EL_UNDEFINED;
5949
5950     // player can escape from explosions and might therefore be still alive
5951     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5952         element <= EL_PLAYER_IS_EXPLODING_4)
5953     {
5954       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5955       int explosion_element = EL_PLAYER_1 + player_nr;
5956       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5957       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5958
5959       if (level.use_explosion_element[player_nr])
5960         explosion_element = level.explosion_element[player_nr];
5961
5962       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5963                     element_info[explosion_element].content.e[xx][yy]);
5964     }
5965
5966     // restore probably existing indestructible background element
5967     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5968       element = Tile[x][y] = Back[x][y];
5969     Back[x][y] = 0;
5970
5971     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5972     GfxDir[x][y] = MV_NONE;
5973     ChangeDelay[x][y] = 0;
5974     ChangePage[x][y] = -1;
5975
5976     CustomValue[x][y] = 0;
5977
5978     InitField_WithBug2(x, y, FALSE);
5979
5980     TEST_DrawLevelField(x, y);
5981
5982     TestIfElementTouchesCustomElement(x, y);
5983
5984     if (GFX_CRUMBLED(element))
5985       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5986
5987     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5988       StorePlayer[x][y] = 0;
5989
5990     if (ELEM_IS_PLAYER(element))
5991       RelocatePlayer(x, y, element);
5992   }
5993   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5994   {
5995     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5996     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5997
5998     if (phase == delay)
5999       TEST_DrawLevelFieldCrumbled(x, y);
6000
6001     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6002     {
6003       DrawLevelElement(x, y, Back[x][y]);
6004       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6005     }
6006     else if (IS_WALKABLE_UNDER(Back[x][y]))
6007     {
6008       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6009       DrawLevelElementThruMask(x, y, Back[x][y]);
6010     }
6011     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6012       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6013   }
6014 }
6015
6016 static void DynaExplode(int ex, int ey)
6017 {
6018   int i, j;
6019   int dynabomb_element = Tile[ex][ey];
6020   int dynabomb_size = 1;
6021   boolean dynabomb_xl = FALSE;
6022   struct PlayerInfo *player;
6023   static int xy[4][2] =
6024   {
6025     { 0, -1 },
6026     { -1, 0 },
6027     { +1, 0 },
6028     { 0, +1 }
6029   };
6030
6031   if (IS_ACTIVE_BOMB(dynabomb_element))
6032   {
6033     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6034     dynabomb_size = player->dynabomb_size;
6035     dynabomb_xl = player->dynabomb_xl;
6036     player->dynabombs_left++;
6037   }
6038
6039   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6040
6041   for (i = 0; i < NUM_DIRECTIONS; i++)
6042   {
6043     for (j = 1; j <= dynabomb_size; j++)
6044     {
6045       int x = ex + j * xy[i][0];
6046       int y = ey + j * xy[i][1];
6047       int element;
6048
6049       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6050         break;
6051
6052       element = Tile[x][y];
6053
6054       // do not restart explosions of fields with active bombs
6055       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6056         continue;
6057
6058       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6059
6060       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6061           !IS_DIGGABLE(element) && !dynabomb_xl)
6062         break;
6063     }
6064   }
6065 }
6066
6067 void Bang(int x, int y)
6068 {
6069   int element = MovingOrBlocked2Element(x, y);
6070   int explosion_type = EX_TYPE_NORMAL;
6071
6072   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6073   {
6074     struct PlayerInfo *player = PLAYERINFO(x, y);
6075
6076     element = Tile[x][y] = player->initial_element;
6077
6078     if (level.use_explosion_element[player->index_nr])
6079     {
6080       int explosion_element = level.explosion_element[player->index_nr];
6081
6082       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6083         explosion_type = EX_TYPE_CROSS;
6084       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6085         explosion_type = EX_TYPE_CENTER;
6086     }
6087   }
6088
6089   switch (element)
6090   {
6091     case EL_BUG:
6092     case EL_SPACESHIP:
6093     case EL_BD_BUTTERFLY:
6094     case EL_BD_FIREFLY:
6095     case EL_YAMYAM:
6096     case EL_DARK_YAMYAM:
6097     case EL_ROBOT:
6098     case EL_PACMAN:
6099     case EL_MOLE:
6100       RaiseScoreElement(element);
6101       break;
6102
6103     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6104     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6105     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6106     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6107     case EL_DYNABOMB_INCREASE_NUMBER:
6108     case EL_DYNABOMB_INCREASE_SIZE:
6109     case EL_DYNABOMB_INCREASE_POWER:
6110       explosion_type = EX_TYPE_DYNA;
6111       break;
6112
6113     case EL_DC_LANDMINE:
6114       explosion_type = EX_TYPE_CENTER;
6115       break;
6116
6117     case EL_PENGUIN:
6118     case EL_LAMP:
6119     case EL_LAMP_ACTIVE:
6120     case EL_AMOEBA_TO_DIAMOND:
6121       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6122         explosion_type = EX_TYPE_CENTER;
6123       break;
6124
6125     default:
6126       if (element_info[element].explosion_type == EXPLODES_CROSS)
6127         explosion_type = EX_TYPE_CROSS;
6128       else if (element_info[element].explosion_type == EXPLODES_1X1)
6129         explosion_type = EX_TYPE_CENTER;
6130       break;
6131   }
6132
6133   if (explosion_type == EX_TYPE_DYNA)
6134     DynaExplode(x, y);
6135   else
6136     Explode(x, y, EX_PHASE_START, explosion_type);
6137
6138   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6139 }
6140
6141 static void SplashAcid(int x, int y)
6142 {
6143   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6144       (!IN_LEV_FIELD(x - 1, y - 2) ||
6145        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6146     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6147
6148   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6149       (!IN_LEV_FIELD(x + 1, y - 2) ||
6150        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6151     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6152
6153   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6154 }
6155
6156 static void InitBeltMovement(void)
6157 {
6158   static int belt_base_element[4] =
6159   {
6160     EL_CONVEYOR_BELT_1_LEFT,
6161     EL_CONVEYOR_BELT_2_LEFT,
6162     EL_CONVEYOR_BELT_3_LEFT,
6163     EL_CONVEYOR_BELT_4_LEFT
6164   };
6165   static int belt_base_active_element[4] =
6166   {
6167     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6168     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6169     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6170     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6171   };
6172
6173   int x, y, i, j;
6174
6175   // set frame order for belt animation graphic according to belt direction
6176   for (i = 0; i < NUM_BELTS; i++)
6177   {
6178     int belt_nr = i;
6179
6180     for (j = 0; j < NUM_BELT_PARTS; j++)
6181     {
6182       int element = belt_base_active_element[belt_nr] + j;
6183       int graphic_1 = el2img(element);
6184       int graphic_2 = el2panelimg(element);
6185
6186       if (game.belt_dir[i] == MV_LEFT)
6187       {
6188         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6189         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6190       }
6191       else
6192       {
6193         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6194         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6195       }
6196     }
6197   }
6198
6199   SCAN_PLAYFIELD(x, y)
6200   {
6201     int element = Tile[x][y];
6202
6203     for (i = 0; i < NUM_BELTS; i++)
6204     {
6205       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6206       {
6207         int e_belt_nr = getBeltNrFromBeltElement(element);
6208         int belt_nr = i;
6209
6210         if (e_belt_nr == belt_nr)
6211         {
6212           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6213
6214           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6215         }
6216       }
6217     }
6218   }
6219 }
6220
6221 static void ToggleBeltSwitch(int x, int y)
6222 {
6223   static int belt_base_element[4] =
6224   {
6225     EL_CONVEYOR_BELT_1_LEFT,
6226     EL_CONVEYOR_BELT_2_LEFT,
6227     EL_CONVEYOR_BELT_3_LEFT,
6228     EL_CONVEYOR_BELT_4_LEFT
6229   };
6230   static int belt_base_active_element[4] =
6231   {
6232     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6233     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6234     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6235     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6236   };
6237   static int belt_base_switch_element[4] =
6238   {
6239     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6240     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6241     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6242     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6243   };
6244   static int belt_move_dir[4] =
6245   {
6246     MV_LEFT,
6247     MV_NONE,
6248     MV_RIGHT,
6249     MV_NONE,
6250   };
6251
6252   int element = Tile[x][y];
6253   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6254   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6255   int belt_dir = belt_move_dir[belt_dir_nr];
6256   int xx, yy, i;
6257
6258   if (!IS_BELT_SWITCH(element))
6259     return;
6260
6261   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6262   game.belt_dir[belt_nr] = belt_dir;
6263
6264   if (belt_dir_nr == 3)
6265     belt_dir_nr = 1;
6266
6267   // set frame order for belt animation graphic according to belt direction
6268   for (i = 0; i < NUM_BELT_PARTS; i++)
6269   {
6270     int element = belt_base_active_element[belt_nr] + i;
6271     int graphic_1 = el2img(element);
6272     int graphic_2 = el2panelimg(element);
6273
6274     if (belt_dir == MV_LEFT)
6275     {
6276       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6277       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6278     }
6279     else
6280     {
6281       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6282       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6283     }
6284   }
6285
6286   SCAN_PLAYFIELD(xx, yy)
6287   {
6288     int element = Tile[xx][yy];
6289
6290     if (IS_BELT_SWITCH(element))
6291     {
6292       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6293
6294       if (e_belt_nr == belt_nr)
6295       {
6296         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6297         TEST_DrawLevelField(xx, yy);
6298       }
6299     }
6300     else if (IS_BELT(element) && belt_dir != MV_NONE)
6301     {
6302       int e_belt_nr = getBeltNrFromBeltElement(element);
6303
6304       if (e_belt_nr == belt_nr)
6305       {
6306         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6307
6308         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6309         TEST_DrawLevelField(xx, yy);
6310       }
6311     }
6312     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6313     {
6314       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6315
6316       if (e_belt_nr == belt_nr)
6317       {
6318         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6319
6320         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6321         TEST_DrawLevelField(xx, yy);
6322       }
6323     }
6324   }
6325 }
6326
6327 static void ToggleSwitchgateSwitch(int x, int y)
6328 {
6329   int xx, yy;
6330
6331   game.switchgate_pos = !game.switchgate_pos;
6332
6333   SCAN_PLAYFIELD(xx, yy)
6334   {
6335     int element = Tile[xx][yy];
6336
6337     if (element == EL_SWITCHGATE_SWITCH_UP)
6338     {
6339       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6340       TEST_DrawLevelField(xx, yy);
6341     }
6342     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6343     {
6344       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6345       TEST_DrawLevelField(xx, yy);
6346     }
6347     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6348     {
6349       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6350       TEST_DrawLevelField(xx, yy);
6351     }
6352     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6353     {
6354       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6355       TEST_DrawLevelField(xx, yy);
6356     }
6357     else if (element == EL_SWITCHGATE_OPEN ||
6358              element == EL_SWITCHGATE_OPENING)
6359     {
6360       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6361
6362       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6363     }
6364     else if (element == EL_SWITCHGATE_CLOSED ||
6365              element == EL_SWITCHGATE_CLOSING)
6366     {
6367       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6368
6369       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6370     }
6371   }
6372 }
6373
6374 static int getInvisibleActiveFromInvisibleElement(int element)
6375 {
6376   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6377           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6378           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6379           element);
6380 }
6381
6382 static int getInvisibleFromInvisibleActiveElement(int element)
6383 {
6384   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6385           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6386           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6387           element);
6388 }
6389
6390 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6391 {
6392   int x, y;
6393
6394   SCAN_PLAYFIELD(x, y)
6395   {
6396     int element = Tile[x][y];
6397
6398     if (element == EL_LIGHT_SWITCH &&
6399         game.light_time_left > 0)
6400     {
6401       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6402       TEST_DrawLevelField(x, y);
6403     }
6404     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6405              game.light_time_left == 0)
6406     {
6407       Tile[x][y] = EL_LIGHT_SWITCH;
6408       TEST_DrawLevelField(x, y);
6409     }
6410     else if (element == EL_EMC_DRIPPER &&
6411              game.light_time_left > 0)
6412     {
6413       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6414       TEST_DrawLevelField(x, y);
6415     }
6416     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6417              game.light_time_left == 0)
6418     {
6419       Tile[x][y] = EL_EMC_DRIPPER;
6420       TEST_DrawLevelField(x, y);
6421     }
6422     else if (element == EL_INVISIBLE_STEELWALL ||
6423              element == EL_INVISIBLE_WALL ||
6424              element == EL_INVISIBLE_SAND)
6425     {
6426       if (game.light_time_left > 0)
6427         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6428
6429       TEST_DrawLevelField(x, y);
6430
6431       // uncrumble neighbour fields, if needed
6432       if (element == EL_INVISIBLE_SAND)
6433         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6434     }
6435     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6436              element == EL_INVISIBLE_WALL_ACTIVE ||
6437              element == EL_INVISIBLE_SAND_ACTIVE)
6438     {
6439       if (game.light_time_left == 0)
6440         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6441
6442       TEST_DrawLevelField(x, y);
6443
6444       // re-crumble neighbour fields, if needed
6445       if (element == EL_INVISIBLE_SAND)
6446         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6447     }
6448   }
6449 }
6450
6451 static void RedrawAllInvisibleElementsForLenses(void)
6452 {
6453   int x, y;
6454
6455   SCAN_PLAYFIELD(x, y)
6456   {
6457     int element = Tile[x][y];
6458
6459     if (element == EL_EMC_DRIPPER &&
6460         game.lenses_time_left > 0)
6461     {
6462       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6463       TEST_DrawLevelField(x, y);
6464     }
6465     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6466              game.lenses_time_left == 0)
6467     {
6468       Tile[x][y] = EL_EMC_DRIPPER;
6469       TEST_DrawLevelField(x, y);
6470     }
6471     else if (element == EL_INVISIBLE_STEELWALL ||
6472              element == EL_INVISIBLE_WALL ||
6473              element == EL_INVISIBLE_SAND)
6474     {
6475       if (game.lenses_time_left > 0)
6476         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6477
6478       TEST_DrawLevelField(x, y);
6479
6480       // uncrumble neighbour fields, if needed
6481       if (element == EL_INVISIBLE_SAND)
6482         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6483     }
6484     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6485              element == EL_INVISIBLE_WALL_ACTIVE ||
6486              element == EL_INVISIBLE_SAND_ACTIVE)
6487     {
6488       if (game.lenses_time_left == 0)
6489         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6490
6491       TEST_DrawLevelField(x, y);
6492
6493       // re-crumble neighbour fields, if needed
6494       if (element == EL_INVISIBLE_SAND)
6495         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6496     }
6497   }
6498 }
6499
6500 static void RedrawAllInvisibleElementsForMagnifier(void)
6501 {
6502   int x, y;
6503
6504   SCAN_PLAYFIELD(x, y)
6505   {
6506     int element = Tile[x][y];
6507
6508     if (element == EL_EMC_FAKE_GRASS &&
6509         game.magnify_time_left > 0)
6510     {
6511       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6512       TEST_DrawLevelField(x, y);
6513     }
6514     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6515              game.magnify_time_left == 0)
6516     {
6517       Tile[x][y] = EL_EMC_FAKE_GRASS;
6518       TEST_DrawLevelField(x, y);
6519     }
6520     else if (IS_GATE_GRAY(element) &&
6521              game.magnify_time_left > 0)
6522     {
6523       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6524                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6525                     IS_EM_GATE_GRAY(element) ?
6526                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6527                     IS_EMC_GATE_GRAY(element) ?
6528                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6529                     IS_DC_GATE_GRAY(element) ?
6530                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6531                     element);
6532       TEST_DrawLevelField(x, y);
6533     }
6534     else if (IS_GATE_GRAY_ACTIVE(element) &&
6535              game.magnify_time_left == 0)
6536     {
6537       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6538                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6539                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6540                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6541                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6542                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6543                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6544                     EL_DC_GATE_WHITE_GRAY :
6545                     element);
6546       TEST_DrawLevelField(x, y);
6547     }
6548   }
6549 }
6550
6551 static void ToggleLightSwitch(int x, int y)
6552 {
6553   int element = Tile[x][y];
6554
6555   game.light_time_left =
6556     (element == EL_LIGHT_SWITCH ?
6557      level.time_light * FRAMES_PER_SECOND : 0);
6558
6559   RedrawAllLightSwitchesAndInvisibleElements();
6560 }
6561
6562 static void ActivateTimegateSwitch(int x, int y)
6563 {
6564   int xx, yy;
6565
6566   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6567
6568   SCAN_PLAYFIELD(xx, yy)
6569   {
6570     int element = Tile[xx][yy];
6571
6572     if (element == EL_TIMEGATE_CLOSED ||
6573         element == EL_TIMEGATE_CLOSING)
6574     {
6575       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6576       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6577     }
6578
6579     /*
6580     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6581     {
6582       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6583       TEST_DrawLevelField(xx, yy);
6584     }
6585     */
6586
6587   }
6588
6589   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6590                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6591 }
6592
6593 static void Impact(int x, int y)
6594 {
6595   boolean last_line = (y == lev_fieldy - 1);
6596   boolean object_hit = FALSE;
6597   boolean impact = (last_line || object_hit);
6598   int element = Tile[x][y];
6599   int smashed = EL_STEELWALL;
6600
6601   if (!last_line)       // check if element below was hit
6602   {
6603     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6604       return;
6605
6606     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6607                                          MovDir[x][y + 1] != MV_DOWN ||
6608                                          MovPos[x][y + 1] <= TILEY / 2));
6609
6610     // do not smash moving elements that left the smashed field in time
6611     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6612         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6613       object_hit = FALSE;
6614
6615 #if USE_QUICKSAND_IMPACT_BUGFIX
6616     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6617     {
6618       RemoveMovingField(x, y + 1);
6619       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6620       Tile[x][y + 2] = EL_ROCK;
6621       TEST_DrawLevelField(x, y + 2);
6622
6623       object_hit = TRUE;
6624     }
6625
6626     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6627     {
6628       RemoveMovingField(x, y + 1);
6629       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6630       Tile[x][y + 2] = EL_ROCK;
6631       TEST_DrawLevelField(x, y + 2);
6632
6633       object_hit = TRUE;
6634     }
6635 #endif
6636
6637     if (object_hit)
6638       smashed = MovingOrBlocked2Element(x, y + 1);
6639
6640     impact = (last_line || object_hit);
6641   }
6642
6643   if (!last_line && smashed == EL_ACID) // element falls into acid
6644   {
6645     SplashAcid(x, y + 1);
6646     return;
6647   }
6648
6649   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6650   // only reset graphic animation if graphic really changes after impact
6651   if (impact &&
6652       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6653   {
6654     ResetGfxAnimation(x, y);
6655     TEST_DrawLevelField(x, y);
6656   }
6657
6658   if (impact && CAN_EXPLODE_IMPACT(element))
6659   {
6660     Bang(x, y);
6661     return;
6662   }
6663   else if (impact && element == EL_PEARL &&
6664            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6665   {
6666     ResetGfxAnimation(x, y);
6667
6668     Tile[x][y] = EL_PEARL_BREAKING;
6669     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6670     return;
6671   }
6672   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6673   {
6674     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6675
6676     return;
6677   }
6678
6679   if (impact && element == EL_AMOEBA_DROP)
6680   {
6681     if (object_hit && IS_PLAYER(x, y + 1))
6682       KillPlayerUnlessEnemyProtected(x, y + 1);
6683     else if (object_hit && smashed == EL_PENGUIN)
6684       Bang(x, y + 1);
6685     else
6686     {
6687       Tile[x][y] = EL_AMOEBA_GROWING;
6688       Store[x][y] = EL_AMOEBA_WET;
6689
6690       ResetRandomAnimationValue(x, y);
6691     }
6692     return;
6693   }
6694
6695   if (object_hit)               // check which object was hit
6696   {
6697     if ((CAN_PASS_MAGIC_WALL(element) && 
6698          (smashed == EL_MAGIC_WALL ||
6699           smashed == EL_BD_MAGIC_WALL)) ||
6700         (CAN_PASS_DC_MAGIC_WALL(element) &&
6701          smashed == EL_DC_MAGIC_WALL))
6702     {
6703       int xx, yy;
6704       int activated_magic_wall =
6705         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6706          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6707          EL_DC_MAGIC_WALL_ACTIVE);
6708
6709       // activate magic wall / mill
6710       SCAN_PLAYFIELD(xx, yy)
6711       {
6712         if (Tile[xx][yy] == smashed)
6713           Tile[xx][yy] = activated_magic_wall;
6714       }
6715
6716       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6717       game.magic_wall_active = TRUE;
6718
6719       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6720                             SND_MAGIC_WALL_ACTIVATING :
6721                             smashed == EL_BD_MAGIC_WALL ?
6722                             SND_BD_MAGIC_WALL_ACTIVATING :
6723                             SND_DC_MAGIC_WALL_ACTIVATING));
6724     }
6725
6726     if (IS_PLAYER(x, y + 1))
6727     {
6728       if (CAN_SMASH_PLAYER(element))
6729       {
6730         KillPlayerUnlessEnemyProtected(x, y + 1);
6731         return;
6732       }
6733     }
6734     else if (smashed == EL_PENGUIN)
6735     {
6736       if (CAN_SMASH_PLAYER(element))
6737       {
6738         Bang(x, y + 1);
6739         return;
6740       }
6741     }
6742     else if (element == EL_BD_DIAMOND)
6743     {
6744       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6745       {
6746         Bang(x, y + 1);
6747         return;
6748       }
6749     }
6750     else if (((element == EL_SP_INFOTRON ||
6751                element == EL_SP_ZONK) &&
6752               (smashed == EL_SP_SNIKSNAK ||
6753                smashed == EL_SP_ELECTRON ||
6754                smashed == EL_SP_DISK_ORANGE)) ||
6755              (element == EL_SP_INFOTRON &&
6756               smashed == EL_SP_DISK_YELLOW))
6757     {
6758       Bang(x, y + 1);
6759       return;
6760     }
6761     else if (CAN_SMASH_EVERYTHING(element))
6762     {
6763       if (IS_CLASSIC_ENEMY(smashed) ||
6764           CAN_EXPLODE_SMASHED(smashed))
6765       {
6766         Bang(x, y + 1);
6767         return;
6768       }
6769       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6770       {
6771         if (smashed == EL_LAMP ||
6772             smashed == EL_LAMP_ACTIVE)
6773         {
6774           Bang(x, y + 1);
6775           return;
6776         }
6777         else if (smashed == EL_NUT)
6778         {
6779           Tile[x][y + 1] = EL_NUT_BREAKING;
6780           PlayLevelSound(x, y, SND_NUT_BREAKING);
6781           RaiseScoreElement(EL_NUT);
6782           return;
6783         }
6784         else if (smashed == EL_PEARL)
6785         {
6786           ResetGfxAnimation(x, y);
6787
6788           Tile[x][y + 1] = EL_PEARL_BREAKING;
6789           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6790           return;
6791         }
6792         else if (smashed == EL_DIAMOND)
6793         {
6794           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6795           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6796           return;
6797         }
6798         else if (IS_BELT_SWITCH(smashed))
6799         {
6800           ToggleBeltSwitch(x, y + 1);
6801         }
6802         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6803                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6804                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6805                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6806         {
6807           ToggleSwitchgateSwitch(x, y + 1);
6808         }
6809         else if (smashed == EL_LIGHT_SWITCH ||
6810                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6811         {
6812           ToggleLightSwitch(x, y + 1);
6813         }
6814         else
6815         {
6816           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6817
6818           CheckElementChangeBySide(x, y + 1, smashed, element,
6819                                    CE_SWITCHED, CH_SIDE_TOP);
6820           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6821                                             CH_SIDE_TOP);
6822         }
6823       }
6824       else
6825       {
6826         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6827       }
6828     }
6829   }
6830
6831   // play sound of magic wall / mill
6832   if (!last_line &&
6833       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6834        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6835        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6836   {
6837     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6838       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6839     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6840       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6841     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6842       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6843
6844     return;
6845   }
6846
6847   // play sound of object that hits the ground
6848   if (last_line || object_hit)
6849     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6850 }
6851
6852 static void TurnRoundExt(int x, int y)
6853 {
6854   static struct
6855   {
6856     int dx, dy;
6857   } move_xy[] =
6858   {
6859     {  0,  0 },
6860     { -1,  0 },
6861     { +1,  0 },
6862     {  0,  0 },
6863     {  0, -1 },
6864     {  0,  0 }, { 0, 0 }, { 0, 0 },
6865     {  0, +1 }
6866   };
6867   static struct
6868   {
6869     int left, right, back;
6870   } turn[] =
6871   {
6872     { 0,        0,              0        },
6873     { MV_DOWN,  MV_UP,          MV_RIGHT },
6874     { MV_UP,    MV_DOWN,        MV_LEFT  },
6875     { 0,        0,              0        },
6876     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6877     { 0,        0,              0        },
6878     { 0,        0,              0        },
6879     { 0,        0,              0        },
6880     { MV_RIGHT, MV_LEFT,        MV_UP    }
6881   };
6882
6883   int element = Tile[x][y];
6884   int move_pattern = element_info[element].move_pattern;
6885
6886   int old_move_dir = MovDir[x][y];
6887   int left_dir  = turn[old_move_dir].left;
6888   int right_dir = turn[old_move_dir].right;
6889   int back_dir  = turn[old_move_dir].back;
6890
6891   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6892   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6893   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6894   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6895
6896   int left_x  = x + left_dx,  left_y  = y + left_dy;
6897   int right_x = x + right_dx, right_y = y + right_dy;
6898   int move_x  = x + move_dx,  move_y  = y + move_dy;
6899
6900   int xx, yy;
6901
6902   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6903   {
6904     TestIfBadThingTouchesOtherBadThing(x, y);
6905
6906     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6907       MovDir[x][y] = right_dir;
6908     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6909       MovDir[x][y] = left_dir;
6910
6911     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6912       MovDelay[x][y] = 9;
6913     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6914       MovDelay[x][y] = 1;
6915   }
6916   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6917   {
6918     TestIfBadThingTouchesOtherBadThing(x, y);
6919
6920     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6921       MovDir[x][y] = left_dir;
6922     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6923       MovDir[x][y] = right_dir;
6924
6925     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6926       MovDelay[x][y] = 9;
6927     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6928       MovDelay[x][y] = 1;
6929   }
6930   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6931   {
6932     TestIfBadThingTouchesOtherBadThing(x, y);
6933
6934     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6935       MovDir[x][y] = left_dir;
6936     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6937       MovDir[x][y] = right_dir;
6938
6939     if (MovDir[x][y] != old_move_dir)
6940       MovDelay[x][y] = 9;
6941   }
6942   else if (element == EL_YAMYAM)
6943   {
6944     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6945     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6946
6947     if (can_turn_left && can_turn_right)
6948       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6949     else if (can_turn_left)
6950       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6951     else if (can_turn_right)
6952       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6953     else
6954       MovDir[x][y] = back_dir;
6955
6956     MovDelay[x][y] = 16 + 16 * RND(3);
6957   }
6958   else if (element == EL_DARK_YAMYAM)
6959   {
6960     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6961                                                          left_x, left_y);
6962     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6963                                                          right_x, right_y);
6964
6965     if (can_turn_left && can_turn_right)
6966       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6967     else if (can_turn_left)
6968       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6969     else if (can_turn_right)
6970       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6971     else
6972       MovDir[x][y] = back_dir;
6973
6974     MovDelay[x][y] = 16 + 16 * RND(3);
6975   }
6976   else if (element == EL_PACMAN)
6977   {
6978     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6979     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6980
6981     if (can_turn_left && can_turn_right)
6982       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6983     else if (can_turn_left)
6984       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6985     else if (can_turn_right)
6986       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6987     else
6988       MovDir[x][y] = back_dir;
6989
6990     MovDelay[x][y] = 6 + RND(40);
6991   }
6992   else if (element == EL_PIG)
6993   {
6994     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6995     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6996     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6997     boolean should_turn_left, should_turn_right, should_move_on;
6998     int rnd_value = 24;
6999     int rnd = RND(rnd_value);
7000
7001     should_turn_left = (can_turn_left &&
7002                         (!can_move_on ||
7003                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7004                                                    y + back_dy + left_dy)));
7005     should_turn_right = (can_turn_right &&
7006                          (!can_move_on ||
7007                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7008                                                     y + back_dy + right_dy)));
7009     should_move_on = (can_move_on &&
7010                       (!can_turn_left ||
7011                        !can_turn_right ||
7012                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7013                                                  y + move_dy + left_dy) ||
7014                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7015                                                  y + move_dy + right_dy)));
7016
7017     if (should_turn_left || should_turn_right || should_move_on)
7018     {
7019       if (should_turn_left && should_turn_right && should_move_on)
7020         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7021                         rnd < 2 * rnd_value / 3 ? right_dir :
7022                         old_move_dir);
7023       else if (should_turn_left && should_turn_right)
7024         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7025       else if (should_turn_left && should_move_on)
7026         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7027       else if (should_turn_right && should_move_on)
7028         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7029       else if (should_turn_left)
7030         MovDir[x][y] = left_dir;
7031       else if (should_turn_right)
7032         MovDir[x][y] = right_dir;
7033       else if (should_move_on)
7034         MovDir[x][y] = old_move_dir;
7035     }
7036     else if (can_move_on && rnd > rnd_value / 8)
7037       MovDir[x][y] = old_move_dir;
7038     else if (can_turn_left && can_turn_right)
7039       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7040     else if (can_turn_left && rnd > rnd_value / 8)
7041       MovDir[x][y] = left_dir;
7042     else if (can_turn_right && rnd > rnd_value/8)
7043       MovDir[x][y] = right_dir;
7044     else
7045       MovDir[x][y] = back_dir;
7046
7047     xx = x + move_xy[MovDir[x][y]].dx;
7048     yy = y + move_xy[MovDir[x][y]].dy;
7049
7050     if (!IN_LEV_FIELD(xx, yy) ||
7051         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7052       MovDir[x][y] = old_move_dir;
7053
7054     MovDelay[x][y] = 0;
7055   }
7056   else if (element == EL_DRAGON)
7057   {
7058     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7059     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7060     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7061     int rnd_value = 24;
7062     int rnd = RND(rnd_value);
7063
7064     if (can_move_on && rnd > rnd_value / 8)
7065       MovDir[x][y] = old_move_dir;
7066     else if (can_turn_left && can_turn_right)
7067       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7068     else if (can_turn_left && rnd > rnd_value / 8)
7069       MovDir[x][y] = left_dir;
7070     else if (can_turn_right && rnd > rnd_value / 8)
7071       MovDir[x][y] = right_dir;
7072     else
7073       MovDir[x][y] = back_dir;
7074
7075     xx = x + move_xy[MovDir[x][y]].dx;
7076     yy = y + move_xy[MovDir[x][y]].dy;
7077
7078     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7079       MovDir[x][y] = old_move_dir;
7080
7081     MovDelay[x][y] = 0;
7082   }
7083   else if (element == EL_MOLE)
7084   {
7085     boolean can_move_on =
7086       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7087                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7088                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7089     if (!can_move_on)
7090     {
7091       boolean can_turn_left =
7092         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7093                               IS_AMOEBOID(Tile[left_x][left_y])));
7094
7095       boolean can_turn_right =
7096         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7097                               IS_AMOEBOID(Tile[right_x][right_y])));
7098
7099       if (can_turn_left && can_turn_right)
7100         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7101       else if (can_turn_left)
7102         MovDir[x][y] = left_dir;
7103       else
7104         MovDir[x][y] = right_dir;
7105     }
7106
7107     if (MovDir[x][y] != old_move_dir)
7108       MovDelay[x][y] = 9;
7109   }
7110   else if (element == EL_BALLOON)
7111   {
7112     MovDir[x][y] = game.wind_direction;
7113     MovDelay[x][y] = 0;
7114   }
7115   else if (element == EL_SPRING)
7116   {
7117     if (MovDir[x][y] & MV_HORIZONTAL)
7118     {
7119       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7120           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7121       {
7122         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7123         ResetGfxAnimation(move_x, move_y);
7124         TEST_DrawLevelField(move_x, move_y);
7125
7126         MovDir[x][y] = back_dir;
7127       }
7128       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7129                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7130         MovDir[x][y] = MV_NONE;
7131     }
7132
7133     MovDelay[x][y] = 0;
7134   }
7135   else if (element == EL_ROBOT ||
7136            element == EL_SATELLITE ||
7137            element == EL_PENGUIN ||
7138            element == EL_EMC_ANDROID)
7139   {
7140     int attr_x = -1, attr_y = -1;
7141
7142     if (game.all_players_gone)
7143     {
7144       attr_x = game.exit_x;
7145       attr_y = game.exit_y;
7146     }
7147     else
7148     {
7149       int i;
7150
7151       for (i = 0; i < MAX_PLAYERS; i++)
7152       {
7153         struct PlayerInfo *player = &stored_player[i];
7154         int jx = player->jx, jy = player->jy;
7155
7156         if (!player->active)
7157           continue;
7158
7159         if (attr_x == -1 ||
7160             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7161         {
7162           attr_x = jx;
7163           attr_y = jy;
7164         }
7165       }
7166     }
7167
7168     if (element == EL_ROBOT &&
7169         game.robot_wheel_x >= 0 &&
7170         game.robot_wheel_y >= 0 &&
7171         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7172          game.engine_version < VERSION_IDENT(3,1,0,0)))
7173     {
7174       attr_x = game.robot_wheel_x;
7175       attr_y = game.robot_wheel_y;
7176     }
7177
7178     if (element == EL_PENGUIN)
7179     {
7180       int i;
7181       static int xy[4][2] =
7182       {
7183         { 0, -1 },
7184         { -1, 0 },
7185         { +1, 0 },
7186         { 0, +1 }
7187       };
7188
7189       for (i = 0; i < NUM_DIRECTIONS; i++)
7190       {
7191         int ex = x + xy[i][0];
7192         int ey = y + xy[i][1];
7193
7194         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7195                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7196                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7197                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7198         {
7199           attr_x = ex;
7200           attr_y = ey;
7201           break;
7202         }
7203       }
7204     }
7205
7206     MovDir[x][y] = MV_NONE;
7207     if (attr_x < x)
7208       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7209     else if (attr_x > x)
7210       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7211     if (attr_y < y)
7212       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7213     else if (attr_y > y)
7214       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7215
7216     if (element == EL_ROBOT)
7217     {
7218       int newx, newy;
7219
7220       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7221         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7222       Moving2Blocked(x, y, &newx, &newy);
7223
7224       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7225         MovDelay[x][y] = 8 + 8 * !RND(3);
7226       else
7227         MovDelay[x][y] = 16;
7228     }
7229     else if (element == EL_PENGUIN)
7230     {
7231       int newx, newy;
7232
7233       MovDelay[x][y] = 1;
7234
7235       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7236       {
7237         boolean first_horiz = RND(2);
7238         int new_move_dir = MovDir[x][y];
7239
7240         MovDir[x][y] =
7241           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7242         Moving2Blocked(x, y, &newx, &newy);
7243
7244         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7245           return;
7246
7247         MovDir[x][y] =
7248           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7249         Moving2Blocked(x, y, &newx, &newy);
7250
7251         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7252           return;
7253
7254         MovDir[x][y] = old_move_dir;
7255         return;
7256       }
7257     }
7258     else if (element == EL_SATELLITE)
7259     {
7260       int newx, newy;
7261
7262       MovDelay[x][y] = 1;
7263
7264       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7265       {
7266         boolean first_horiz = RND(2);
7267         int new_move_dir = MovDir[x][y];
7268
7269         MovDir[x][y] =
7270           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7271         Moving2Blocked(x, y, &newx, &newy);
7272
7273         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7274           return;
7275
7276         MovDir[x][y] =
7277           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7278         Moving2Blocked(x, y, &newx, &newy);
7279
7280         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7281           return;
7282
7283         MovDir[x][y] = old_move_dir;
7284         return;
7285       }
7286     }
7287     else if (element == EL_EMC_ANDROID)
7288     {
7289       static int check_pos[16] =
7290       {
7291         -1,             //  0 => (invalid)
7292         7,              //  1 => MV_LEFT
7293         3,              //  2 => MV_RIGHT
7294         -1,             //  3 => (invalid)
7295         1,              //  4 =>            MV_UP
7296         0,              //  5 => MV_LEFT  | MV_UP
7297         2,              //  6 => MV_RIGHT | MV_UP
7298         -1,             //  7 => (invalid)
7299         5,              //  8 =>            MV_DOWN
7300         6,              //  9 => MV_LEFT  | MV_DOWN
7301         4,              // 10 => MV_RIGHT | MV_DOWN
7302         -1,             // 11 => (invalid)
7303         -1,             // 12 => (invalid)
7304         -1,             // 13 => (invalid)
7305         -1,             // 14 => (invalid)
7306         -1,             // 15 => (invalid)
7307       };
7308       static struct
7309       {
7310         int dx, dy;
7311         int dir;
7312       } check_xy[8] =
7313       {
7314         { -1, -1,       MV_LEFT  | MV_UP   },
7315         {  0, -1,                  MV_UP   },
7316         { +1, -1,       MV_RIGHT | MV_UP   },
7317         { +1,  0,       MV_RIGHT           },
7318         { +1, +1,       MV_RIGHT | MV_DOWN },
7319         {  0, +1,                  MV_DOWN },
7320         { -1, +1,       MV_LEFT  | MV_DOWN },
7321         { -1,  0,       MV_LEFT            },
7322       };
7323       int start_pos, check_order;
7324       boolean can_clone = FALSE;
7325       int i;
7326
7327       // check if there is any free field around current position
7328       for (i = 0; i < 8; i++)
7329       {
7330         int newx = x + check_xy[i].dx;
7331         int newy = y + check_xy[i].dy;
7332
7333         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7334         {
7335           can_clone = TRUE;
7336
7337           break;
7338         }
7339       }
7340
7341       if (can_clone)            // randomly find an element to clone
7342       {
7343         can_clone = FALSE;
7344
7345         start_pos = check_pos[RND(8)];
7346         check_order = (RND(2) ? -1 : +1);
7347
7348         for (i = 0; i < 8; i++)
7349         {
7350           int pos_raw = start_pos + i * check_order;
7351           int pos = (pos_raw + 8) % 8;
7352           int newx = x + check_xy[pos].dx;
7353           int newy = y + check_xy[pos].dy;
7354
7355           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7356           {
7357             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7358             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7359
7360             Store[x][y] = Tile[newx][newy];
7361
7362             can_clone = TRUE;
7363
7364             break;
7365           }
7366         }
7367       }
7368
7369       if (can_clone)            // randomly find a direction to move
7370       {
7371         can_clone = FALSE;
7372
7373         start_pos = check_pos[RND(8)];
7374         check_order = (RND(2) ? -1 : +1);
7375
7376         for (i = 0; i < 8; i++)
7377         {
7378           int pos_raw = start_pos + i * check_order;
7379           int pos = (pos_raw + 8) % 8;
7380           int newx = x + check_xy[pos].dx;
7381           int newy = y + check_xy[pos].dy;
7382           int new_move_dir = check_xy[pos].dir;
7383
7384           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7385           {
7386             MovDir[x][y] = new_move_dir;
7387             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7388
7389             can_clone = TRUE;
7390
7391             break;
7392           }
7393         }
7394       }
7395
7396       if (can_clone)            // cloning and moving successful
7397         return;
7398
7399       // cannot clone -- try to move towards player
7400
7401       start_pos = check_pos[MovDir[x][y] & 0x0f];
7402       check_order = (RND(2) ? -1 : +1);
7403
7404       for (i = 0; i < 3; i++)
7405       {
7406         // first check start_pos, then previous/next or (next/previous) pos
7407         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7408         int pos = (pos_raw + 8) % 8;
7409         int newx = x + check_xy[pos].dx;
7410         int newy = y + check_xy[pos].dy;
7411         int new_move_dir = check_xy[pos].dir;
7412
7413         if (IS_PLAYER(newx, newy))
7414           break;
7415
7416         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7417         {
7418           MovDir[x][y] = new_move_dir;
7419           MovDelay[x][y] = level.android_move_time * 8 + 1;
7420
7421           break;
7422         }
7423       }
7424     }
7425   }
7426   else if (move_pattern == MV_TURNING_LEFT ||
7427            move_pattern == MV_TURNING_RIGHT ||
7428            move_pattern == MV_TURNING_LEFT_RIGHT ||
7429            move_pattern == MV_TURNING_RIGHT_LEFT ||
7430            move_pattern == MV_TURNING_RANDOM ||
7431            move_pattern == MV_ALL_DIRECTIONS)
7432   {
7433     boolean can_turn_left =
7434       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7435     boolean can_turn_right =
7436       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7437
7438     if (element_info[element].move_stepsize == 0)       // "not moving"
7439       return;
7440
7441     if (move_pattern == MV_TURNING_LEFT)
7442       MovDir[x][y] = left_dir;
7443     else if (move_pattern == MV_TURNING_RIGHT)
7444       MovDir[x][y] = right_dir;
7445     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7446       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7447     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7448       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7449     else if (move_pattern == MV_TURNING_RANDOM)
7450       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7451                       can_turn_right && !can_turn_left ? right_dir :
7452                       RND(2) ? left_dir : right_dir);
7453     else if (can_turn_left && can_turn_right)
7454       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7455     else if (can_turn_left)
7456       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7457     else if (can_turn_right)
7458       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7459     else
7460       MovDir[x][y] = back_dir;
7461
7462     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7463   }
7464   else if (move_pattern == MV_HORIZONTAL ||
7465            move_pattern == MV_VERTICAL)
7466   {
7467     if (move_pattern & old_move_dir)
7468       MovDir[x][y] = back_dir;
7469     else if (move_pattern == MV_HORIZONTAL)
7470       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7471     else if (move_pattern == MV_VERTICAL)
7472       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7473
7474     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7475   }
7476   else if (move_pattern & MV_ANY_DIRECTION)
7477   {
7478     MovDir[x][y] = move_pattern;
7479     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7480   }
7481   else if (move_pattern & MV_WIND_DIRECTION)
7482   {
7483     MovDir[x][y] = game.wind_direction;
7484     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7485   }
7486   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7487   {
7488     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7489       MovDir[x][y] = left_dir;
7490     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7491       MovDir[x][y] = right_dir;
7492
7493     if (MovDir[x][y] != old_move_dir)
7494       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7495   }
7496   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7497   {
7498     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7499       MovDir[x][y] = right_dir;
7500     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7501       MovDir[x][y] = left_dir;
7502
7503     if (MovDir[x][y] != old_move_dir)
7504       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7505   }
7506   else if (move_pattern == MV_TOWARDS_PLAYER ||
7507            move_pattern == MV_AWAY_FROM_PLAYER)
7508   {
7509     int attr_x = -1, attr_y = -1;
7510     int newx, newy;
7511     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7512
7513     if (game.all_players_gone)
7514     {
7515       attr_x = game.exit_x;
7516       attr_y = game.exit_y;
7517     }
7518     else
7519     {
7520       int i;
7521
7522       for (i = 0; i < MAX_PLAYERS; i++)
7523       {
7524         struct PlayerInfo *player = &stored_player[i];
7525         int jx = player->jx, jy = player->jy;
7526
7527         if (!player->active)
7528           continue;
7529
7530         if (attr_x == -1 ||
7531             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7532         {
7533           attr_x = jx;
7534           attr_y = jy;
7535         }
7536       }
7537     }
7538
7539     MovDir[x][y] = MV_NONE;
7540     if (attr_x < x)
7541       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7542     else if (attr_x > x)
7543       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7544     if (attr_y < y)
7545       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7546     else if (attr_y > y)
7547       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7548
7549     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7550
7551     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7552     {
7553       boolean first_horiz = RND(2);
7554       int new_move_dir = MovDir[x][y];
7555
7556       if (element_info[element].move_stepsize == 0)     // "not moving"
7557       {
7558         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7559         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7560
7561         return;
7562       }
7563
7564       MovDir[x][y] =
7565         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7566       Moving2Blocked(x, y, &newx, &newy);
7567
7568       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7569         return;
7570
7571       MovDir[x][y] =
7572         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7573       Moving2Blocked(x, y, &newx, &newy);
7574
7575       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7576         return;
7577
7578       MovDir[x][y] = old_move_dir;
7579     }
7580   }
7581   else if (move_pattern == MV_WHEN_PUSHED ||
7582            move_pattern == MV_WHEN_DROPPED)
7583   {
7584     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7585       MovDir[x][y] = MV_NONE;
7586
7587     MovDelay[x][y] = 0;
7588   }
7589   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7590   {
7591     static int test_xy[7][2] =
7592     {
7593       { 0, -1 },
7594       { -1, 0 },
7595       { +1, 0 },
7596       { 0, +1 },
7597       { 0, -1 },
7598       { -1, 0 },
7599       { +1, 0 },
7600     };
7601     static int test_dir[7] =
7602     {
7603       MV_UP,
7604       MV_LEFT,
7605       MV_RIGHT,
7606       MV_DOWN,
7607       MV_UP,
7608       MV_LEFT,
7609       MV_RIGHT,
7610     };
7611     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7612     int move_preference = -1000000;     // start with very low preference
7613     int new_move_dir = MV_NONE;
7614     int start_test = RND(4);
7615     int i;
7616
7617     for (i = 0; i < NUM_DIRECTIONS; i++)
7618     {
7619       int move_dir = test_dir[start_test + i];
7620       int move_dir_preference;
7621
7622       xx = x + test_xy[start_test + i][0];
7623       yy = y + test_xy[start_test + i][1];
7624
7625       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7626           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7627       {
7628         new_move_dir = move_dir;
7629
7630         break;
7631       }
7632
7633       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7634         continue;
7635
7636       move_dir_preference = -1 * RunnerVisit[xx][yy];
7637       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7638         move_dir_preference = PlayerVisit[xx][yy];
7639
7640       if (move_dir_preference > move_preference)
7641       {
7642         // prefer field that has not been visited for the longest time
7643         move_preference = move_dir_preference;
7644         new_move_dir = move_dir;
7645       }
7646       else if (move_dir_preference == move_preference &&
7647                move_dir == old_move_dir)
7648       {
7649         // prefer last direction when all directions are preferred equally
7650         move_preference = move_dir_preference;
7651         new_move_dir = move_dir;
7652       }
7653     }
7654
7655     MovDir[x][y] = new_move_dir;
7656     if (old_move_dir != new_move_dir)
7657       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7658   }
7659 }
7660
7661 static void TurnRound(int x, int y)
7662 {
7663   int direction = MovDir[x][y];
7664
7665   TurnRoundExt(x, y);
7666
7667   GfxDir[x][y] = MovDir[x][y];
7668
7669   if (direction != MovDir[x][y])
7670     GfxFrame[x][y] = 0;
7671
7672   if (MovDelay[x][y])
7673     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7674
7675   ResetGfxFrame(x, y);
7676 }
7677
7678 static boolean JustBeingPushed(int x, int y)
7679 {
7680   int i;
7681
7682   for (i = 0; i < MAX_PLAYERS; i++)
7683   {
7684     struct PlayerInfo *player = &stored_player[i];
7685
7686     if (player->active && player->is_pushing && player->MovPos)
7687     {
7688       int next_jx = player->jx + (player->jx - player->last_jx);
7689       int next_jy = player->jy + (player->jy - player->last_jy);
7690
7691       if (x == next_jx && y == next_jy)
7692         return TRUE;
7693     }
7694   }
7695
7696   return FALSE;
7697 }
7698
7699 static void StartMoving(int x, int y)
7700 {
7701   boolean started_moving = FALSE;       // some elements can fall _and_ move
7702   int element = Tile[x][y];
7703
7704   if (Stop[x][y])
7705     return;
7706
7707   if (MovDelay[x][y] == 0)
7708     GfxAction[x][y] = ACTION_DEFAULT;
7709
7710   if (CAN_FALL(element) && y < lev_fieldy - 1)
7711   {
7712     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7713         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7714       if (JustBeingPushed(x, y))
7715         return;
7716
7717     if (element == EL_QUICKSAND_FULL)
7718     {
7719       if (IS_FREE(x, y + 1))
7720       {
7721         InitMovingField(x, y, MV_DOWN);
7722         started_moving = TRUE;
7723
7724         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7725 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7726         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7727           Store[x][y] = EL_ROCK;
7728 #else
7729         Store[x][y] = EL_ROCK;
7730 #endif
7731
7732         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7733       }
7734       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7735       {
7736         if (!MovDelay[x][y])
7737         {
7738           MovDelay[x][y] = TILEY + 1;
7739
7740           ResetGfxAnimation(x, y);
7741           ResetGfxAnimation(x, y + 1);
7742         }
7743
7744         if (MovDelay[x][y])
7745         {
7746           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7747           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7748
7749           MovDelay[x][y]--;
7750           if (MovDelay[x][y])
7751             return;
7752         }
7753
7754         Tile[x][y] = EL_QUICKSAND_EMPTY;
7755         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7756         Store[x][y + 1] = Store[x][y];
7757         Store[x][y] = 0;
7758
7759         PlayLevelSoundAction(x, y, ACTION_FILLING);
7760       }
7761       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7762       {
7763         if (!MovDelay[x][y])
7764         {
7765           MovDelay[x][y] = TILEY + 1;
7766
7767           ResetGfxAnimation(x, y);
7768           ResetGfxAnimation(x, y + 1);
7769         }
7770
7771         if (MovDelay[x][y])
7772         {
7773           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7774           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7775
7776           MovDelay[x][y]--;
7777           if (MovDelay[x][y])
7778             return;
7779         }
7780
7781         Tile[x][y] = EL_QUICKSAND_EMPTY;
7782         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7783         Store[x][y + 1] = Store[x][y];
7784         Store[x][y] = 0;
7785
7786         PlayLevelSoundAction(x, y, ACTION_FILLING);
7787       }
7788     }
7789     else if (element == EL_QUICKSAND_FAST_FULL)
7790     {
7791       if (IS_FREE(x, y + 1))
7792       {
7793         InitMovingField(x, y, MV_DOWN);
7794         started_moving = TRUE;
7795
7796         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7797 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7798         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7799           Store[x][y] = EL_ROCK;
7800 #else
7801         Store[x][y] = EL_ROCK;
7802 #endif
7803
7804         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7805       }
7806       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7807       {
7808         if (!MovDelay[x][y])
7809         {
7810           MovDelay[x][y] = TILEY + 1;
7811
7812           ResetGfxAnimation(x, y);
7813           ResetGfxAnimation(x, y + 1);
7814         }
7815
7816         if (MovDelay[x][y])
7817         {
7818           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7819           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7820
7821           MovDelay[x][y]--;
7822           if (MovDelay[x][y])
7823             return;
7824         }
7825
7826         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7827         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7828         Store[x][y + 1] = Store[x][y];
7829         Store[x][y] = 0;
7830
7831         PlayLevelSoundAction(x, y, ACTION_FILLING);
7832       }
7833       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7834       {
7835         if (!MovDelay[x][y])
7836         {
7837           MovDelay[x][y] = TILEY + 1;
7838
7839           ResetGfxAnimation(x, y);
7840           ResetGfxAnimation(x, y + 1);
7841         }
7842
7843         if (MovDelay[x][y])
7844         {
7845           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7846           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7847
7848           MovDelay[x][y]--;
7849           if (MovDelay[x][y])
7850             return;
7851         }
7852
7853         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7854         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7855         Store[x][y + 1] = Store[x][y];
7856         Store[x][y] = 0;
7857
7858         PlayLevelSoundAction(x, y, ACTION_FILLING);
7859       }
7860     }
7861     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7862              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7863     {
7864       InitMovingField(x, y, MV_DOWN);
7865       started_moving = TRUE;
7866
7867       Tile[x][y] = EL_QUICKSAND_FILLING;
7868       Store[x][y] = element;
7869
7870       PlayLevelSoundAction(x, y, ACTION_FILLING);
7871     }
7872     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7873              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7874     {
7875       InitMovingField(x, y, MV_DOWN);
7876       started_moving = TRUE;
7877
7878       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7879       Store[x][y] = element;
7880
7881       PlayLevelSoundAction(x, y, ACTION_FILLING);
7882     }
7883     else if (element == EL_MAGIC_WALL_FULL)
7884     {
7885       if (IS_FREE(x, y + 1))
7886       {
7887         InitMovingField(x, y, MV_DOWN);
7888         started_moving = TRUE;
7889
7890         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7891         Store[x][y] = EL_CHANGED(Store[x][y]);
7892       }
7893       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7894       {
7895         if (!MovDelay[x][y])
7896           MovDelay[x][y] = TILEY / 4 + 1;
7897
7898         if (MovDelay[x][y])
7899         {
7900           MovDelay[x][y]--;
7901           if (MovDelay[x][y])
7902             return;
7903         }
7904
7905         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7906         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7907         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7908         Store[x][y] = 0;
7909       }
7910     }
7911     else if (element == EL_BD_MAGIC_WALL_FULL)
7912     {
7913       if (IS_FREE(x, y + 1))
7914       {
7915         InitMovingField(x, y, MV_DOWN);
7916         started_moving = TRUE;
7917
7918         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7919         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7920       }
7921       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7922       {
7923         if (!MovDelay[x][y])
7924           MovDelay[x][y] = TILEY / 4 + 1;
7925
7926         if (MovDelay[x][y])
7927         {
7928           MovDelay[x][y]--;
7929           if (MovDelay[x][y])
7930             return;
7931         }
7932
7933         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7934         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7935         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7936         Store[x][y] = 0;
7937       }
7938     }
7939     else if (element == EL_DC_MAGIC_WALL_FULL)
7940     {
7941       if (IS_FREE(x, y + 1))
7942       {
7943         InitMovingField(x, y, MV_DOWN);
7944         started_moving = TRUE;
7945
7946         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7947         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7948       }
7949       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7950       {
7951         if (!MovDelay[x][y])
7952           MovDelay[x][y] = TILEY / 4 + 1;
7953
7954         if (MovDelay[x][y])
7955         {
7956           MovDelay[x][y]--;
7957           if (MovDelay[x][y])
7958             return;
7959         }
7960
7961         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7962         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7963         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7964         Store[x][y] = 0;
7965       }
7966     }
7967     else if ((CAN_PASS_MAGIC_WALL(element) &&
7968               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7969                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7970              (CAN_PASS_DC_MAGIC_WALL(element) &&
7971               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7972
7973     {
7974       InitMovingField(x, y, MV_DOWN);
7975       started_moving = TRUE;
7976
7977       Tile[x][y] =
7978         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7979          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7980          EL_DC_MAGIC_WALL_FILLING);
7981       Store[x][y] = element;
7982     }
7983     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7984     {
7985       SplashAcid(x, y + 1);
7986
7987       InitMovingField(x, y, MV_DOWN);
7988       started_moving = TRUE;
7989
7990       Store[x][y] = EL_ACID;
7991     }
7992     else if (
7993              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7994               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7995              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7996               CAN_FALL(element) && WasJustFalling[x][y] &&
7997               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7998
7999              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8000               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8001               (Tile[x][y + 1] == EL_BLOCKED)))
8002     {
8003       /* this is needed for a special case not covered by calling "Impact()"
8004          from "ContinueMoving()": if an element moves to a tile directly below
8005          another element which was just falling on that tile (which was empty
8006          in the previous frame), the falling element above would just stop
8007          instead of smashing the element below (in previous version, the above
8008          element was just checked for "moving" instead of "falling", resulting
8009          in incorrect smashes caused by horizontal movement of the above
8010          element; also, the case of the player being the element to smash was
8011          simply not covered here... :-/ ) */
8012
8013       CheckCollision[x][y] = 0;
8014       CheckImpact[x][y] = 0;
8015
8016       Impact(x, y);
8017     }
8018     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8019     {
8020       if (MovDir[x][y] == MV_NONE)
8021       {
8022         InitMovingField(x, y, MV_DOWN);
8023         started_moving = TRUE;
8024       }
8025     }
8026     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8027     {
8028       if (WasJustFalling[x][y]) // prevent animation from being restarted
8029         MovDir[x][y] = MV_DOWN;
8030
8031       InitMovingField(x, y, MV_DOWN);
8032       started_moving = TRUE;
8033     }
8034     else if (element == EL_AMOEBA_DROP)
8035     {
8036       Tile[x][y] = EL_AMOEBA_GROWING;
8037       Store[x][y] = EL_AMOEBA_WET;
8038     }
8039     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8040               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8041              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8042              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8043     {
8044       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8045                                 (IS_FREE(x - 1, y + 1) ||
8046                                  Tile[x - 1][y + 1] == EL_ACID));
8047       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8048                                 (IS_FREE(x + 1, y + 1) ||
8049                                  Tile[x + 1][y + 1] == EL_ACID));
8050       boolean can_fall_any  = (can_fall_left || can_fall_right);
8051       boolean can_fall_both = (can_fall_left && can_fall_right);
8052       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8053
8054       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8055       {
8056         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8057           can_fall_right = FALSE;
8058         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8059           can_fall_left = FALSE;
8060         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8061           can_fall_right = FALSE;
8062         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8063           can_fall_left = FALSE;
8064
8065         can_fall_any  = (can_fall_left || can_fall_right);
8066         can_fall_both = FALSE;
8067       }
8068
8069       if (can_fall_both)
8070       {
8071         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8072           can_fall_right = FALSE;       // slip down on left side
8073         else
8074           can_fall_left = !(can_fall_right = RND(2));
8075
8076         can_fall_both = FALSE;
8077       }
8078
8079       if (can_fall_any)
8080       {
8081         // if not determined otherwise, prefer left side for slipping down
8082         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8083         started_moving = TRUE;
8084       }
8085     }
8086     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8087     {
8088       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8089       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8090       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8091       int belt_dir = game.belt_dir[belt_nr];
8092
8093       if ((belt_dir == MV_LEFT  && left_is_free) ||
8094           (belt_dir == MV_RIGHT && right_is_free))
8095       {
8096         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8097
8098         InitMovingField(x, y, belt_dir);
8099         started_moving = TRUE;
8100
8101         Pushed[x][y] = TRUE;
8102         Pushed[nextx][y] = TRUE;
8103
8104         GfxAction[x][y] = ACTION_DEFAULT;
8105       }
8106       else
8107       {
8108         MovDir[x][y] = 0;       // if element was moving, stop it
8109       }
8110     }
8111   }
8112
8113   // not "else if" because of elements that can fall and move (EL_SPRING)
8114   if (CAN_MOVE(element) && !started_moving)
8115   {
8116     int move_pattern = element_info[element].move_pattern;
8117     int newx, newy;
8118
8119     Moving2Blocked(x, y, &newx, &newy);
8120
8121     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8122       return;
8123
8124     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8125         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8126     {
8127       WasJustMoving[x][y] = 0;
8128       CheckCollision[x][y] = 0;
8129
8130       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8131
8132       if (Tile[x][y] != element)        // element has changed
8133         return;
8134     }
8135
8136     if (!MovDelay[x][y])        // start new movement phase
8137     {
8138       // all objects that can change their move direction after each step
8139       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8140
8141       if (element != EL_YAMYAM &&
8142           element != EL_DARK_YAMYAM &&
8143           element != EL_PACMAN &&
8144           !(move_pattern & MV_ANY_DIRECTION) &&
8145           move_pattern != MV_TURNING_LEFT &&
8146           move_pattern != MV_TURNING_RIGHT &&
8147           move_pattern != MV_TURNING_LEFT_RIGHT &&
8148           move_pattern != MV_TURNING_RIGHT_LEFT &&
8149           move_pattern != MV_TURNING_RANDOM)
8150       {
8151         TurnRound(x, y);
8152
8153         if (MovDelay[x][y] && (element == EL_BUG ||
8154                                element == EL_SPACESHIP ||
8155                                element == EL_SP_SNIKSNAK ||
8156                                element == EL_SP_ELECTRON ||
8157                                element == EL_MOLE))
8158           TEST_DrawLevelField(x, y);
8159       }
8160     }
8161
8162     if (MovDelay[x][y])         // wait some time before next movement
8163     {
8164       MovDelay[x][y]--;
8165
8166       if (element == EL_ROBOT ||
8167           element == EL_YAMYAM ||
8168           element == EL_DARK_YAMYAM)
8169       {
8170         DrawLevelElementAnimationIfNeeded(x, y, element);
8171         PlayLevelSoundAction(x, y, ACTION_WAITING);
8172       }
8173       else if (element == EL_SP_ELECTRON)
8174         DrawLevelElementAnimationIfNeeded(x, y, element);
8175       else if (element == EL_DRAGON)
8176       {
8177         int i;
8178         int dir = MovDir[x][y];
8179         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8180         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8181         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8182                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8183                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8184                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8185         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8186
8187         GfxAction[x][y] = ACTION_ATTACKING;
8188
8189         if (IS_PLAYER(x, y))
8190           DrawPlayerField(x, y);
8191         else
8192           TEST_DrawLevelField(x, y);
8193
8194         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8195
8196         for (i = 1; i <= 3; i++)
8197         {
8198           int xx = x + i * dx;
8199           int yy = y + i * dy;
8200           int sx = SCREENX(xx);
8201           int sy = SCREENY(yy);
8202           int flame_graphic = graphic + (i - 1);
8203
8204           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8205             break;
8206
8207           if (MovDelay[x][y])
8208           {
8209             int flamed = MovingOrBlocked2Element(xx, yy);
8210
8211             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8212               Bang(xx, yy);
8213             else
8214               RemoveMovingField(xx, yy);
8215
8216             ChangeDelay[xx][yy] = 0;
8217
8218             Tile[xx][yy] = EL_FLAMES;
8219
8220             if (IN_SCR_FIELD(sx, sy))
8221             {
8222               TEST_DrawLevelFieldCrumbled(xx, yy);
8223               DrawGraphic(sx, sy, flame_graphic, frame);
8224             }
8225           }
8226           else
8227           {
8228             if (Tile[xx][yy] == EL_FLAMES)
8229               Tile[xx][yy] = EL_EMPTY;
8230             TEST_DrawLevelField(xx, yy);
8231           }
8232         }
8233       }
8234
8235       if (MovDelay[x][y])       // element still has to wait some time
8236       {
8237         PlayLevelSoundAction(x, y, ACTION_WAITING);
8238
8239         return;
8240       }
8241     }
8242
8243     // now make next step
8244
8245     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8246
8247     if (DONT_COLLIDE_WITH(element) &&
8248         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8249         !PLAYER_ENEMY_PROTECTED(newx, newy))
8250     {
8251       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8252
8253       return;
8254     }
8255
8256     else if (CAN_MOVE_INTO_ACID(element) &&
8257              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8258              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8259              (MovDir[x][y] == MV_DOWN ||
8260               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8261     {
8262       SplashAcid(newx, newy);
8263       Store[x][y] = EL_ACID;
8264     }
8265     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8266     {
8267       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8268           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8269           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8270           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8271       {
8272         RemoveField(x, y);
8273         TEST_DrawLevelField(x, y);
8274
8275         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8276         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8277           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8278
8279         game.friends_still_needed--;
8280         if (!game.friends_still_needed &&
8281             !game.GameOver &&
8282             game.all_players_gone)
8283           LevelSolved();
8284
8285         return;
8286       }
8287       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8288       {
8289         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8290           TEST_DrawLevelField(newx, newy);
8291         else
8292           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8293       }
8294       else if (!IS_FREE(newx, newy))
8295       {
8296         GfxAction[x][y] = ACTION_WAITING;
8297
8298         if (IS_PLAYER(x, y))
8299           DrawPlayerField(x, y);
8300         else
8301           TEST_DrawLevelField(x, y);
8302
8303         return;
8304       }
8305     }
8306     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8307     {
8308       if (IS_FOOD_PIG(Tile[newx][newy]))
8309       {
8310         if (IS_MOVING(newx, newy))
8311           RemoveMovingField(newx, newy);
8312         else
8313         {
8314           Tile[newx][newy] = EL_EMPTY;
8315           TEST_DrawLevelField(newx, newy);
8316         }
8317
8318         PlayLevelSound(x, y, SND_PIG_DIGGING);
8319       }
8320       else if (!IS_FREE(newx, newy))
8321       {
8322         if (IS_PLAYER(x, y))
8323           DrawPlayerField(x, y);
8324         else
8325           TEST_DrawLevelField(x, y);
8326
8327         return;
8328       }
8329     }
8330     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8331     {
8332       if (Store[x][y] != EL_EMPTY)
8333       {
8334         boolean can_clone = FALSE;
8335         int xx, yy;
8336
8337         // check if element to clone is still there
8338         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8339         {
8340           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8341           {
8342             can_clone = TRUE;
8343
8344             break;
8345           }
8346         }
8347
8348         // cannot clone or target field not free anymore -- do not clone
8349         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8350           Store[x][y] = EL_EMPTY;
8351       }
8352
8353       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8354       {
8355         if (IS_MV_DIAGONAL(MovDir[x][y]))
8356         {
8357           int diagonal_move_dir = MovDir[x][y];
8358           int stored = Store[x][y];
8359           int change_delay = 8;
8360           int graphic;
8361
8362           // android is moving diagonally
8363
8364           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8365
8366           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8367           GfxElement[x][y] = EL_EMC_ANDROID;
8368           GfxAction[x][y] = ACTION_SHRINKING;
8369           GfxDir[x][y] = diagonal_move_dir;
8370           ChangeDelay[x][y] = change_delay;
8371
8372           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8373                                    GfxDir[x][y]);
8374
8375           DrawLevelGraphicAnimation(x, y, graphic);
8376           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8377
8378           if (Tile[newx][newy] == EL_ACID)
8379           {
8380             SplashAcid(newx, newy);
8381
8382             return;
8383           }
8384
8385           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8386
8387           Store[newx][newy] = EL_EMC_ANDROID;
8388           GfxElement[newx][newy] = EL_EMC_ANDROID;
8389           GfxAction[newx][newy] = ACTION_GROWING;
8390           GfxDir[newx][newy] = diagonal_move_dir;
8391           ChangeDelay[newx][newy] = change_delay;
8392
8393           graphic = el_act_dir2img(GfxElement[newx][newy],
8394                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8395
8396           DrawLevelGraphicAnimation(newx, newy, graphic);
8397           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8398
8399           return;
8400         }
8401         else
8402         {
8403           Tile[newx][newy] = EL_EMPTY;
8404           TEST_DrawLevelField(newx, newy);
8405
8406           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8407         }
8408       }
8409       else if (!IS_FREE(newx, newy))
8410       {
8411         return;
8412       }
8413     }
8414     else if (IS_CUSTOM_ELEMENT(element) &&
8415              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8416     {
8417       if (!DigFieldByCE(newx, newy, element))
8418         return;
8419
8420       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8421       {
8422         RunnerVisit[x][y] = FrameCounter;
8423         PlayerVisit[x][y] /= 8;         // expire player visit path
8424       }
8425     }
8426     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8427     {
8428       if (!IS_FREE(newx, newy))
8429       {
8430         if (IS_PLAYER(x, y))
8431           DrawPlayerField(x, y);
8432         else
8433           TEST_DrawLevelField(x, y);
8434
8435         return;
8436       }
8437       else
8438       {
8439         boolean wanna_flame = !RND(10);
8440         int dx = newx - x, dy = newy - y;
8441         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8442         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8443         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8444                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8445         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8446                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8447
8448         if ((wanna_flame ||
8449              IS_CLASSIC_ENEMY(element1) ||
8450              IS_CLASSIC_ENEMY(element2)) &&
8451             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8452             element1 != EL_FLAMES && element2 != EL_FLAMES)
8453         {
8454           ResetGfxAnimation(x, y);
8455           GfxAction[x][y] = ACTION_ATTACKING;
8456
8457           if (IS_PLAYER(x, y))
8458             DrawPlayerField(x, y);
8459           else
8460             TEST_DrawLevelField(x, y);
8461
8462           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8463
8464           MovDelay[x][y] = 50;
8465
8466           Tile[newx][newy] = EL_FLAMES;
8467           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8468             Tile[newx1][newy1] = EL_FLAMES;
8469           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8470             Tile[newx2][newy2] = EL_FLAMES;
8471
8472           return;
8473         }
8474       }
8475     }
8476     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8477              Tile[newx][newy] == EL_DIAMOND)
8478     {
8479       if (IS_MOVING(newx, newy))
8480         RemoveMovingField(newx, newy);
8481       else
8482       {
8483         Tile[newx][newy] = EL_EMPTY;
8484         TEST_DrawLevelField(newx, newy);
8485       }
8486
8487       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8488     }
8489     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8490              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8491     {
8492       if (AmoebaNr[newx][newy])
8493       {
8494         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8495         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8496             Tile[newx][newy] == EL_BD_AMOEBA)
8497           AmoebaCnt[AmoebaNr[newx][newy]]--;
8498       }
8499
8500       if (IS_MOVING(newx, newy))
8501       {
8502         RemoveMovingField(newx, newy);
8503       }
8504       else
8505       {
8506         Tile[newx][newy] = EL_EMPTY;
8507         TEST_DrawLevelField(newx, newy);
8508       }
8509
8510       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8511     }
8512     else if ((element == EL_PACMAN || element == EL_MOLE)
8513              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8514     {
8515       if (AmoebaNr[newx][newy])
8516       {
8517         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8518         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8519             Tile[newx][newy] == EL_BD_AMOEBA)
8520           AmoebaCnt[AmoebaNr[newx][newy]]--;
8521       }
8522
8523       if (element == EL_MOLE)
8524       {
8525         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8526         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8527
8528         ResetGfxAnimation(x, y);
8529         GfxAction[x][y] = ACTION_DIGGING;
8530         TEST_DrawLevelField(x, y);
8531
8532         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8533
8534         return;                         // wait for shrinking amoeba
8535       }
8536       else      // element == EL_PACMAN
8537       {
8538         Tile[newx][newy] = EL_EMPTY;
8539         TEST_DrawLevelField(newx, newy);
8540         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8541       }
8542     }
8543     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8544              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8545               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8546     {
8547       // wait for shrinking amoeba to completely disappear
8548       return;
8549     }
8550     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8551     {
8552       // object was running against a wall
8553
8554       TurnRound(x, y);
8555
8556       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8557         DrawLevelElementAnimation(x, y, element);
8558
8559       if (DONT_TOUCH(element))
8560         TestIfBadThingTouchesPlayer(x, y);
8561
8562       return;
8563     }
8564
8565     InitMovingField(x, y, MovDir[x][y]);
8566
8567     PlayLevelSoundAction(x, y, ACTION_MOVING);
8568   }
8569
8570   if (MovDir[x][y])
8571     ContinueMoving(x, y);
8572 }
8573
8574 void ContinueMoving(int x, int y)
8575 {
8576   int element = Tile[x][y];
8577   struct ElementInfo *ei = &element_info[element];
8578   int direction = MovDir[x][y];
8579   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8580   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8581   int newx = x + dx, newy = y + dy;
8582   int stored = Store[x][y];
8583   int stored_new = Store[newx][newy];
8584   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8585   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8586   boolean last_line = (newy == lev_fieldy - 1);
8587   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8588
8589   if (pushed_by_player)         // special case: moving object pushed by player
8590   {
8591     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8592   }
8593   else if (use_step_delay)      // special case: moving object has step delay
8594   {
8595     if (!MovDelay[x][y])
8596       MovPos[x][y] += getElementMoveStepsize(x, y);
8597
8598     if (MovDelay[x][y])
8599       MovDelay[x][y]--;
8600     else
8601       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8602
8603     if (MovDelay[x][y])
8604     {
8605       TEST_DrawLevelField(x, y);
8606
8607       return;   // element is still waiting
8608     }
8609   }
8610   else                          // normal case: generically moving object
8611   {
8612     MovPos[x][y] += getElementMoveStepsize(x, y);
8613   }
8614
8615   if (ABS(MovPos[x][y]) < TILEX)
8616   {
8617     TEST_DrawLevelField(x, y);
8618
8619     return;     // element is still moving
8620   }
8621
8622   // element reached destination field
8623
8624   Tile[x][y] = EL_EMPTY;
8625   Tile[newx][newy] = element;
8626   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8627
8628   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8629   {
8630     element = Tile[newx][newy] = EL_ACID;
8631   }
8632   else if (element == EL_MOLE)
8633   {
8634     Tile[x][y] = EL_SAND;
8635
8636     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8637   }
8638   else if (element == EL_QUICKSAND_FILLING)
8639   {
8640     element = Tile[newx][newy] = get_next_element(element);
8641     Store[newx][newy] = Store[x][y];
8642   }
8643   else if (element == EL_QUICKSAND_EMPTYING)
8644   {
8645     Tile[x][y] = get_next_element(element);
8646     element = Tile[newx][newy] = Store[x][y];
8647   }
8648   else if (element == EL_QUICKSAND_FAST_FILLING)
8649   {
8650     element = Tile[newx][newy] = get_next_element(element);
8651     Store[newx][newy] = Store[x][y];
8652   }
8653   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8654   {
8655     Tile[x][y] = get_next_element(element);
8656     element = Tile[newx][newy] = Store[x][y];
8657   }
8658   else if (element == EL_MAGIC_WALL_FILLING)
8659   {
8660     element = Tile[newx][newy] = get_next_element(element);
8661     if (!game.magic_wall_active)
8662       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8663     Store[newx][newy] = Store[x][y];
8664   }
8665   else if (element == EL_MAGIC_WALL_EMPTYING)
8666   {
8667     Tile[x][y] = get_next_element(element);
8668     if (!game.magic_wall_active)
8669       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8670     element = Tile[newx][newy] = Store[x][y];
8671
8672     InitField(newx, newy, FALSE);
8673   }
8674   else if (element == EL_BD_MAGIC_WALL_FILLING)
8675   {
8676     element = Tile[newx][newy] = get_next_element(element);
8677     if (!game.magic_wall_active)
8678       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8679     Store[newx][newy] = Store[x][y];
8680   }
8681   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8682   {
8683     Tile[x][y] = get_next_element(element);
8684     if (!game.magic_wall_active)
8685       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8686     element = Tile[newx][newy] = Store[x][y];
8687
8688     InitField(newx, newy, FALSE);
8689   }
8690   else if (element == EL_DC_MAGIC_WALL_FILLING)
8691   {
8692     element = Tile[newx][newy] = get_next_element(element);
8693     if (!game.magic_wall_active)
8694       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8695     Store[newx][newy] = Store[x][y];
8696   }
8697   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8698   {
8699     Tile[x][y] = get_next_element(element);
8700     if (!game.magic_wall_active)
8701       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8702     element = Tile[newx][newy] = Store[x][y];
8703
8704     InitField(newx, newy, FALSE);
8705   }
8706   else if (element == EL_AMOEBA_DROPPING)
8707   {
8708     Tile[x][y] = get_next_element(element);
8709     element = Tile[newx][newy] = Store[x][y];
8710   }
8711   else if (element == EL_SOKOBAN_OBJECT)
8712   {
8713     if (Back[x][y])
8714       Tile[x][y] = Back[x][y];
8715
8716     if (Back[newx][newy])
8717       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8718
8719     Back[x][y] = Back[newx][newy] = 0;
8720   }
8721
8722   Store[x][y] = EL_EMPTY;
8723   MovPos[x][y] = 0;
8724   MovDir[x][y] = 0;
8725   MovDelay[x][y] = 0;
8726
8727   MovDelay[newx][newy] = 0;
8728
8729   if (CAN_CHANGE_OR_HAS_ACTION(element))
8730   {
8731     // copy element change control values to new field
8732     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8733     ChangePage[newx][newy]  = ChangePage[x][y];
8734     ChangeCount[newx][newy] = ChangeCount[x][y];
8735     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8736   }
8737
8738   CustomValue[newx][newy] = CustomValue[x][y];
8739
8740   ChangeDelay[x][y] = 0;
8741   ChangePage[x][y] = -1;
8742   ChangeCount[x][y] = 0;
8743   ChangeEvent[x][y] = -1;
8744
8745   CustomValue[x][y] = 0;
8746
8747   // copy animation control values to new field
8748   GfxFrame[newx][newy]  = GfxFrame[x][y];
8749   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8750   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8751   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8752
8753   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8754
8755   // some elements can leave other elements behind after moving
8756   if (ei->move_leave_element != EL_EMPTY &&
8757       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8758       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8759   {
8760     int move_leave_element = ei->move_leave_element;
8761
8762     // this makes it possible to leave the removed element again
8763     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8764       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8765
8766     Tile[x][y] = move_leave_element;
8767
8768     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8769       MovDir[x][y] = direction;
8770
8771     InitField(x, y, FALSE);
8772
8773     if (GFX_CRUMBLED(Tile[x][y]))
8774       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8775
8776     if (ELEM_IS_PLAYER(move_leave_element))
8777       RelocatePlayer(x, y, move_leave_element);
8778   }
8779
8780   // do this after checking for left-behind element
8781   ResetGfxAnimation(x, y);      // reset animation values for old field
8782
8783   if (!CAN_MOVE(element) ||
8784       (CAN_FALL(element) && direction == MV_DOWN &&
8785        (element == EL_SPRING ||
8786         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8787         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8788     GfxDir[x][y] = MovDir[newx][newy] = 0;
8789
8790   TEST_DrawLevelField(x, y);
8791   TEST_DrawLevelField(newx, newy);
8792
8793   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8794
8795   // prevent pushed element from moving on in pushed direction
8796   if (pushed_by_player && CAN_MOVE(element) &&
8797       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8798       !(element_info[element].move_pattern & direction))
8799     TurnRound(newx, newy);
8800
8801   // prevent elements on conveyor belt from moving on in last direction
8802   if (pushed_by_conveyor && CAN_FALL(element) &&
8803       direction & MV_HORIZONTAL)
8804     MovDir[newx][newy] = 0;
8805
8806   if (!pushed_by_player)
8807   {
8808     int nextx = newx + dx, nexty = newy + dy;
8809     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8810
8811     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8812
8813     if (CAN_FALL(element) && direction == MV_DOWN)
8814       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8815
8816     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8817       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8818
8819     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8820       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8821   }
8822
8823   if (DONT_TOUCH(element))      // object may be nasty to player or others
8824   {
8825     TestIfBadThingTouchesPlayer(newx, newy);
8826     TestIfBadThingTouchesFriend(newx, newy);
8827
8828     if (!IS_CUSTOM_ELEMENT(element))
8829       TestIfBadThingTouchesOtherBadThing(newx, newy);
8830   }
8831   else if (element == EL_PENGUIN)
8832     TestIfFriendTouchesBadThing(newx, newy);
8833
8834   if (DONT_GET_HIT_BY(element))
8835   {
8836     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8837   }
8838
8839   // give the player one last chance (one more frame) to move away
8840   if (CAN_FALL(element) && direction == MV_DOWN &&
8841       (last_line || (!IS_FREE(x, newy + 1) &&
8842                      (!IS_PLAYER(x, newy + 1) ||
8843                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8844     Impact(x, newy);
8845
8846   if (pushed_by_player && !game.use_change_when_pushing_bug)
8847   {
8848     int push_side = MV_DIR_OPPOSITE(direction);
8849     struct PlayerInfo *player = PLAYERINFO(x, y);
8850
8851     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8852                                player->index_bit, push_side);
8853     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8854                                         player->index_bit, push_side);
8855   }
8856
8857   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8858     MovDelay[newx][newy] = 1;
8859
8860   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8861
8862   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8863   TestIfElementHitsCustomElement(newx, newy, direction);
8864   TestIfPlayerTouchesCustomElement(newx, newy);
8865   TestIfElementTouchesCustomElement(newx, newy);
8866
8867   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8868       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8869     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8870                              MV_DIR_OPPOSITE(direction));
8871 }
8872
8873 int AmoebaNeighbourNr(int ax, int ay)
8874 {
8875   int i;
8876   int element = Tile[ax][ay];
8877   int group_nr = 0;
8878   static int xy[4][2] =
8879   {
8880     { 0, -1 },
8881     { -1, 0 },
8882     { +1, 0 },
8883     { 0, +1 }
8884   };
8885
8886   for (i = 0; i < NUM_DIRECTIONS; i++)
8887   {
8888     int x = ax + xy[i][0];
8889     int y = ay + xy[i][1];
8890
8891     if (!IN_LEV_FIELD(x, y))
8892       continue;
8893
8894     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8895       group_nr = AmoebaNr[x][y];
8896   }
8897
8898   return group_nr;
8899 }
8900
8901 static void AmoebaMerge(int ax, int ay)
8902 {
8903   int i, x, y, xx, yy;
8904   int new_group_nr = AmoebaNr[ax][ay];
8905   static int xy[4][2] =
8906   {
8907     { 0, -1 },
8908     { -1, 0 },
8909     { +1, 0 },
8910     { 0, +1 }
8911   };
8912
8913   if (new_group_nr == 0)
8914     return;
8915
8916   for (i = 0; i < NUM_DIRECTIONS; i++)
8917   {
8918     x = ax + xy[i][0];
8919     y = ay + xy[i][1];
8920
8921     if (!IN_LEV_FIELD(x, y))
8922       continue;
8923
8924     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8925          Tile[x][y] == EL_BD_AMOEBA ||
8926          Tile[x][y] == EL_AMOEBA_DEAD) &&
8927         AmoebaNr[x][y] != new_group_nr)
8928     {
8929       int old_group_nr = AmoebaNr[x][y];
8930
8931       if (old_group_nr == 0)
8932         return;
8933
8934       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8935       AmoebaCnt[old_group_nr] = 0;
8936       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8937       AmoebaCnt2[old_group_nr] = 0;
8938
8939       SCAN_PLAYFIELD(xx, yy)
8940       {
8941         if (AmoebaNr[xx][yy] == old_group_nr)
8942           AmoebaNr[xx][yy] = new_group_nr;
8943       }
8944     }
8945   }
8946 }
8947
8948 void AmoebaToDiamond(int ax, int ay)
8949 {
8950   int i, x, y;
8951
8952   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8953   {
8954     int group_nr = AmoebaNr[ax][ay];
8955
8956 #ifdef DEBUG
8957     if (group_nr == 0)
8958     {
8959       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8960       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8961
8962       return;
8963     }
8964 #endif
8965
8966     SCAN_PLAYFIELD(x, y)
8967     {
8968       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8969       {
8970         AmoebaNr[x][y] = 0;
8971         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8972       }
8973     }
8974
8975     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8976                             SND_AMOEBA_TURNING_TO_GEM :
8977                             SND_AMOEBA_TURNING_TO_ROCK));
8978     Bang(ax, ay);
8979   }
8980   else
8981   {
8982     static int xy[4][2] =
8983     {
8984       { 0, -1 },
8985       { -1, 0 },
8986       { +1, 0 },
8987       { 0, +1 }
8988     };
8989
8990     for (i = 0; i < NUM_DIRECTIONS; i++)
8991     {
8992       x = ax + xy[i][0];
8993       y = ay + xy[i][1];
8994
8995       if (!IN_LEV_FIELD(x, y))
8996         continue;
8997
8998       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8999       {
9000         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9001                               SND_AMOEBA_TURNING_TO_GEM :
9002                               SND_AMOEBA_TURNING_TO_ROCK));
9003         Bang(x, y);
9004       }
9005     }
9006   }
9007 }
9008
9009 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9010 {
9011   int x, y;
9012   int group_nr = AmoebaNr[ax][ay];
9013   boolean done = FALSE;
9014
9015 #ifdef DEBUG
9016   if (group_nr == 0)
9017   {
9018     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9019     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9020
9021     return;
9022   }
9023 #endif
9024
9025   SCAN_PLAYFIELD(x, y)
9026   {
9027     if (AmoebaNr[x][y] == group_nr &&
9028         (Tile[x][y] == EL_AMOEBA_DEAD ||
9029          Tile[x][y] == EL_BD_AMOEBA ||
9030          Tile[x][y] == EL_AMOEBA_GROWING))
9031     {
9032       AmoebaNr[x][y] = 0;
9033       Tile[x][y] = new_element;
9034       InitField(x, y, FALSE);
9035       TEST_DrawLevelField(x, y);
9036       done = TRUE;
9037     }
9038   }
9039
9040   if (done)
9041     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9042                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9043                             SND_BD_AMOEBA_TURNING_TO_GEM));
9044 }
9045
9046 static void AmoebaGrowing(int x, int y)
9047 {
9048   static unsigned int sound_delay = 0;
9049   static unsigned int sound_delay_value = 0;
9050
9051   if (!MovDelay[x][y])          // start new growing cycle
9052   {
9053     MovDelay[x][y] = 7;
9054
9055     if (DelayReached(&sound_delay, sound_delay_value))
9056     {
9057       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9058       sound_delay_value = 30;
9059     }
9060   }
9061
9062   if (MovDelay[x][y])           // wait some time before growing bigger
9063   {
9064     MovDelay[x][y]--;
9065     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9066     {
9067       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9068                                            6 - MovDelay[x][y]);
9069
9070       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9071     }
9072
9073     if (!MovDelay[x][y])
9074     {
9075       Tile[x][y] = Store[x][y];
9076       Store[x][y] = 0;
9077       TEST_DrawLevelField(x, y);
9078     }
9079   }
9080 }
9081
9082 static void AmoebaShrinking(int x, int y)
9083 {
9084   static unsigned int sound_delay = 0;
9085   static unsigned int sound_delay_value = 0;
9086
9087   if (!MovDelay[x][y])          // start new shrinking cycle
9088   {
9089     MovDelay[x][y] = 7;
9090
9091     if (DelayReached(&sound_delay, sound_delay_value))
9092       sound_delay_value = 30;
9093   }
9094
9095   if (MovDelay[x][y])           // wait some time before shrinking
9096   {
9097     MovDelay[x][y]--;
9098     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9099     {
9100       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9101                                            6 - MovDelay[x][y]);
9102
9103       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9104     }
9105
9106     if (!MovDelay[x][y])
9107     {
9108       Tile[x][y] = EL_EMPTY;
9109       TEST_DrawLevelField(x, y);
9110
9111       // don't let mole enter this field in this cycle;
9112       // (give priority to objects falling to this field from above)
9113       Stop[x][y] = TRUE;
9114     }
9115   }
9116 }
9117
9118 static void AmoebaReproduce(int ax, int ay)
9119 {
9120   int i;
9121   int element = Tile[ax][ay];
9122   int graphic = el2img(element);
9123   int newax = ax, neway = ay;
9124   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9125   static int xy[4][2] =
9126   {
9127     { 0, -1 },
9128     { -1, 0 },
9129     { +1, 0 },
9130     { 0, +1 }
9131   };
9132
9133   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9134   {
9135     Tile[ax][ay] = EL_AMOEBA_DEAD;
9136     TEST_DrawLevelField(ax, ay);
9137     return;
9138   }
9139
9140   if (IS_ANIMATED(graphic))
9141     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9142
9143   if (!MovDelay[ax][ay])        // start making new amoeba field
9144     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9145
9146   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9147   {
9148     MovDelay[ax][ay]--;
9149     if (MovDelay[ax][ay])
9150       return;
9151   }
9152
9153   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9154   {
9155     int start = RND(4);
9156     int x = ax + xy[start][0];
9157     int y = ay + xy[start][1];
9158
9159     if (!IN_LEV_FIELD(x, y))
9160       return;
9161
9162     if (IS_FREE(x, y) ||
9163         CAN_GROW_INTO(Tile[x][y]) ||
9164         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9165         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9166     {
9167       newax = x;
9168       neway = y;
9169     }
9170
9171     if (newax == ax && neway == ay)
9172       return;
9173   }
9174   else                          // normal or "filled" (BD style) amoeba
9175   {
9176     int start = RND(4);
9177     boolean waiting_for_player = FALSE;
9178
9179     for (i = 0; i < NUM_DIRECTIONS; i++)
9180     {
9181       int j = (start + i) % 4;
9182       int x = ax + xy[j][0];
9183       int y = ay + xy[j][1];
9184
9185       if (!IN_LEV_FIELD(x, y))
9186         continue;
9187
9188       if (IS_FREE(x, y) ||
9189           CAN_GROW_INTO(Tile[x][y]) ||
9190           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9191           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9192       {
9193         newax = x;
9194         neway = y;
9195         break;
9196       }
9197       else if (IS_PLAYER(x, y))
9198         waiting_for_player = TRUE;
9199     }
9200
9201     if (newax == ax && neway == ay)             // amoeba cannot grow
9202     {
9203       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9204       {
9205         Tile[ax][ay] = EL_AMOEBA_DEAD;
9206         TEST_DrawLevelField(ax, ay);
9207         AmoebaCnt[AmoebaNr[ax][ay]]--;
9208
9209         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9210         {
9211           if (element == EL_AMOEBA_FULL)
9212             AmoebaToDiamond(ax, ay);
9213           else if (element == EL_BD_AMOEBA)
9214             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9215         }
9216       }
9217       return;
9218     }
9219     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9220     {
9221       // amoeba gets larger by growing in some direction
9222
9223       int new_group_nr = AmoebaNr[ax][ay];
9224
9225 #ifdef DEBUG
9226   if (new_group_nr == 0)
9227   {
9228     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9229           newax, neway);
9230     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9231
9232     return;
9233   }
9234 #endif
9235
9236       AmoebaNr[newax][neway] = new_group_nr;
9237       AmoebaCnt[new_group_nr]++;
9238       AmoebaCnt2[new_group_nr]++;
9239
9240       // if amoeba touches other amoeba(s) after growing, unify them
9241       AmoebaMerge(newax, neway);
9242
9243       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9244       {
9245         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9246         return;
9247       }
9248     }
9249   }
9250
9251   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9252       (neway == lev_fieldy - 1 && newax != ax))
9253   {
9254     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9255     Store[newax][neway] = element;
9256   }
9257   else if (neway == ay || element == EL_EMC_DRIPPER)
9258   {
9259     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9260
9261     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9262   }
9263   else
9264   {
9265     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9266     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9267     Store[ax][ay] = EL_AMOEBA_DROP;
9268     ContinueMoving(ax, ay);
9269     return;
9270   }
9271
9272   TEST_DrawLevelField(newax, neway);
9273 }
9274
9275 static void Life(int ax, int ay)
9276 {
9277   int x1, y1, x2, y2;
9278   int life_time = 40;
9279   int element = Tile[ax][ay];
9280   int graphic = el2img(element);
9281   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9282                          level.biomaze);
9283   boolean changed = FALSE;
9284
9285   if (IS_ANIMATED(graphic))
9286     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9287
9288   if (Stop[ax][ay])
9289     return;
9290
9291   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9292     MovDelay[ax][ay] = life_time;
9293
9294   if (MovDelay[ax][ay])         // wait some time before next cycle
9295   {
9296     MovDelay[ax][ay]--;
9297     if (MovDelay[ax][ay])
9298       return;
9299   }
9300
9301   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9302   {
9303     int xx = ax+x1, yy = ay+y1;
9304     int old_element = Tile[xx][yy];
9305     int num_neighbours = 0;
9306
9307     if (!IN_LEV_FIELD(xx, yy))
9308       continue;
9309
9310     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9311     {
9312       int x = xx+x2, y = yy+y2;
9313
9314       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9315         continue;
9316
9317       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9318       boolean is_neighbour = FALSE;
9319
9320       if (level.use_life_bugs)
9321         is_neighbour =
9322           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9323            (IS_FREE(x, y)                             &&  Stop[x][y]));
9324       else
9325         is_neighbour =
9326           (Last[x][y] == element || is_player_cell);
9327
9328       if (is_neighbour)
9329         num_neighbours++;
9330     }
9331
9332     boolean is_free = FALSE;
9333
9334     if (level.use_life_bugs)
9335       is_free = (IS_FREE(xx, yy));
9336     else
9337       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9338
9339     if (xx == ax && yy == ay)           // field in the middle
9340     {
9341       if (num_neighbours < life_parameter[0] ||
9342           num_neighbours > life_parameter[1])
9343       {
9344         Tile[xx][yy] = EL_EMPTY;
9345         if (Tile[xx][yy] != old_element)
9346           TEST_DrawLevelField(xx, yy);
9347         Stop[xx][yy] = TRUE;
9348         changed = TRUE;
9349       }
9350     }
9351     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9352     {                                   // free border field
9353       if (num_neighbours >= life_parameter[2] &&
9354           num_neighbours <= life_parameter[3])
9355       {
9356         Tile[xx][yy] = element;
9357         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9358         if (Tile[xx][yy] != old_element)
9359           TEST_DrawLevelField(xx, yy);
9360         Stop[xx][yy] = TRUE;
9361         changed = TRUE;
9362       }
9363     }
9364   }
9365
9366   if (changed)
9367     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9368                    SND_GAME_OF_LIFE_GROWING);
9369 }
9370
9371 static void InitRobotWheel(int x, int y)
9372 {
9373   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9374 }
9375
9376 static void RunRobotWheel(int x, int y)
9377 {
9378   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9379 }
9380
9381 static void StopRobotWheel(int x, int y)
9382 {
9383   if (game.robot_wheel_x == x &&
9384       game.robot_wheel_y == y)
9385   {
9386     game.robot_wheel_x = -1;
9387     game.robot_wheel_y = -1;
9388     game.robot_wheel_active = FALSE;
9389   }
9390 }
9391
9392 static void InitTimegateWheel(int x, int y)
9393 {
9394   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9395 }
9396
9397 static void RunTimegateWheel(int x, int y)
9398 {
9399   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9400 }
9401
9402 static void InitMagicBallDelay(int x, int y)
9403 {
9404   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9405 }
9406
9407 static void ActivateMagicBall(int bx, int by)
9408 {
9409   int x, y;
9410
9411   if (level.ball_random)
9412   {
9413     int pos_border = RND(8);    // select one of the eight border elements
9414     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9415     int xx = pos_content % 3;
9416     int yy = pos_content / 3;
9417
9418     x = bx - 1 + xx;
9419     y = by - 1 + yy;
9420
9421     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9422       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9423   }
9424   else
9425   {
9426     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9427     {
9428       int xx = x - bx + 1;
9429       int yy = y - by + 1;
9430
9431       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9432         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9433     }
9434   }
9435
9436   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9437 }
9438
9439 static void CheckExit(int x, int y)
9440 {
9441   if (game.gems_still_needed > 0 ||
9442       game.sokoban_fields_still_needed > 0 ||
9443       game.sokoban_objects_still_needed > 0 ||
9444       game.lights_still_needed > 0)
9445   {
9446     int element = Tile[x][y];
9447     int graphic = el2img(element);
9448
9449     if (IS_ANIMATED(graphic))
9450       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9451
9452     return;
9453   }
9454
9455   // do not re-open exit door closed after last player
9456   if (game.all_players_gone)
9457     return;
9458
9459   Tile[x][y] = EL_EXIT_OPENING;
9460
9461   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9462 }
9463
9464 static void CheckExitEM(int x, int y)
9465 {
9466   if (game.gems_still_needed > 0 ||
9467       game.sokoban_fields_still_needed > 0 ||
9468       game.sokoban_objects_still_needed > 0 ||
9469       game.lights_still_needed > 0)
9470   {
9471     int element = Tile[x][y];
9472     int graphic = el2img(element);
9473
9474     if (IS_ANIMATED(graphic))
9475       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9476
9477     return;
9478   }
9479
9480   // do not re-open exit door closed after last player
9481   if (game.all_players_gone)
9482     return;
9483
9484   Tile[x][y] = EL_EM_EXIT_OPENING;
9485
9486   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9487 }
9488
9489 static void CheckExitSteel(int x, int y)
9490 {
9491   if (game.gems_still_needed > 0 ||
9492       game.sokoban_fields_still_needed > 0 ||
9493       game.sokoban_objects_still_needed > 0 ||
9494       game.lights_still_needed > 0)
9495   {
9496     int element = Tile[x][y];
9497     int graphic = el2img(element);
9498
9499     if (IS_ANIMATED(graphic))
9500       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9501
9502     return;
9503   }
9504
9505   // do not re-open exit door closed after last player
9506   if (game.all_players_gone)
9507     return;
9508
9509   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9510
9511   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9512 }
9513
9514 static void CheckExitSteelEM(int x, int y)
9515 {
9516   if (game.gems_still_needed > 0 ||
9517       game.sokoban_fields_still_needed > 0 ||
9518       game.sokoban_objects_still_needed > 0 ||
9519       game.lights_still_needed > 0)
9520   {
9521     int element = Tile[x][y];
9522     int graphic = el2img(element);
9523
9524     if (IS_ANIMATED(graphic))
9525       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9526
9527     return;
9528   }
9529
9530   // do not re-open exit door closed after last player
9531   if (game.all_players_gone)
9532     return;
9533
9534   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9535
9536   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9537 }
9538
9539 static void CheckExitSP(int x, int y)
9540 {
9541   if (game.gems_still_needed > 0)
9542   {
9543     int element = Tile[x][y];
9544     int graphic = el2img(element);
9545
9546     if (IS_ANIMATED(graphic))
9547       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9548
9549     return;
9550   }
9551
9552   // do not re-open exit door closed after last player
9553   if (game.all_players_gone)
9554     return;
9555
9556   Tile[x][y] = EL_SP_EXIT_OPENING;
9557
9558   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9559 }
9560
9561 static void CloseAllOpenTimegates(void)
9562 {
9563   int x, y;
9564
9565   SCAN_PLAYFIELD(x, y)
9566   {
9567     int element = Tile[x][y];
9568
9569     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9570     {
9571       Tile[x][y] = EL_TIMEGATE_CLOSING;
9572
9573       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9574     }
9575   }
9576 }
9577
9578 static void DrawTwinkleOnField(int x, int y)
9579 {
9580   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9581     return;
9582
9583   if (Tile[x][y] == EL_BD_DIAMOND)
9584     return;
9585
9586   if (MovDelay[x][y] == 0)      // next animation frame
9587     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9588
9589   if (MovDelay[x][y] != 0)      // wait some time before next frame
9590   {
9591     MovDelay[x][y]--;
9592
9593     DrawLevelElementAnimation(x, y, Tile[x][y]);
9594
9595     if (MovDelay[x][y] != 0)
9596     {
9597       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9598                                            10 - MovDelay[x][y]);
9599
9600       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9601     }
9602   }
9603 }
9604
9605 static void MauerWaechst(int x, int y)
9606 {
9607   int delay = 6;
9608
9609   if (!MovDelay[x][y])          // next animation frame
9610     MovDelay[x][y] = 3 * delay;
9611
9612   if (MovDelay[x][y])           // wait some time before next frame
9613   {
9614     MovDelay[x][y]--;
9615
9616     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9617     {
9618       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9619       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9620
9621       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9622     }
9623
9624     if (!MovDelay[x][y])
9625     {
9626       if (MovDir[x][y] == MV_LEFT)
9627       {
9628         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9629           TEST_DrawLevelField(x - 1, y);
9630       }
9631       else if (MovDir[x][y] == MV_RIGHT)
9632       {
9633         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9634           TEST_DrawLevelField(x + 1, y);
9635       }
9636       else if (MovDir[x][y] == MV_UP)
9637       {
9638         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9639           TEST_DrawLevelField(x, y - 1);
9640       }
9641       else
9642       {
9643         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9644           TEST_DrawLevelField(x, y + 1);
9645       }
9646
9647       Tile[x][y] = Store[x][y];
9648       Store[x][y] = 0;
9649       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9650       TEST_DrawLevelField(x, y);
9651     }
9652   }
9653 }
9654
9655 static void MauerAbleger(int ax, int ay)
9656 {
9657   int element = Tile[ax][ay];
9658   int graphic = el2img(element);
9659   boolean oben_frei = FALSE, unten_frei = FALSE;
9660   boolean links_frei = FALSE, rechts_frei = FALSE;
9661   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9662   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9663   boolean new_wall = FALSE;
9664
9665   if (IS_ANIMATED(graphic))
9666     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9667
9668   if (!MovDelay[ax][ay])        // start building new wall
9669     MovDelay[ax][ay] = 6;
9670
9671   if (MovDelay[ax][ay])         // wait some time before building new wall
9672   {
9673     MovDelay[ax][ay]--;
9674     if (MovDelay[ax][ay])
9675       return;
9676   }
9677
9678   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9679     oben_frei = TRUE;
9680   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9681     unten_frei = TRUE;
9682   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9683     links_frei = TRUE;
9684   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9685     rechts_frei = TRUE;
9686
9687   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9688       element == EL_EXPANDABLE_WALL_ANY)
9689   {
9690     if (oben_frei)
9691     {
9692       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9693       Store[ax][ay-1] = element;
9694       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9695       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9696         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9697                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9698       new_wall = TRUE;
9699     }
9700     if (unten_frei)
9701     {
9702       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9703       Store[ax][ay+1] = element;
9704       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9705       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9706         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9707                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9708       new_wall = TRUE;
9709     }
9710   }
9711
9712   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9713       element == EL_EXPANDABLE_WALL_ANY ||
9714       element == EL_EXPANDABLE_WALL ||
9715       element == EL_BD_EXPANDABLE_WALL)
9716   {
9717     if (links_frei)
9718     {
9719       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9720       Store[ax-1][ay] = element;
9721       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9722       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9723         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9724                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9725       new_wall = TRUE;
9726     }
9727
9728     if (rechts_frei)
9729     {
9730       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9731       Store[ax+1][ay] = element;
9732       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9733       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9734         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9735                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9736       new_wall = TRUE;
9737     }
9738   }
9739
9740   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9741     TEST_DrawLevelField(ax, ay);
9742
9743   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9744     oben_massiv = TRUE;
9745   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9746     unten_massiv = TRUE;
9747   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9748     links_massiv = TRUE;
9749   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9750     rechts_massiv = TRUE;
9751
9752   if (((oben_massiv && unten_massiv) ||
9753        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9754        element == EL_EXPANDABLE_WALL) &&
9755       ((links_massiv && rechts_massiv) ||
9756        element == EL_EXPANDABLE_WALL_VERTICAL))
9757     Tile[ax][ay] = EL_WALL;
9758
9759   if (new_wall)
9760     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9761 }
9762
9763 static void MauerAblegerStahl(int ax, int ay)
9764 {
9765   int element = Tile[ax][ay];
9766   int graphic = el2img(element);
9767   boolean oben_frei = FALSE, unten_frei = FALSE;
9768   boolean links_frei = FALSE, rechts_frei = FALSE;
9769   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9770   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9771   boolean new_wall = FALSE;
9772
9773   if (IS_ANIMATED(graphic))
9774     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9775
9776   if (!MovDelay[ax][ay])        // start building new wall
9777     MovDelay[ax][ay] = 6;
9778
9779   if (MovDelay[ax][ay])         // wait some time before building new wall
9780   {
9781     MovDelay[ax][ay]--;
9782     if (MovDelay[ax][ay])
9783       return;
9784   }
9785
9786   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9787     oben_frei = TRUE;
9788   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9789     unten_frei = TRUE;
9790   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9791     links_frei = TRUE;
9792   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9793     rechts_frei = TRUE;
9794
9795   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9796       element == EL_EXPANDABLE_STEELWALL_ANY)
9797   {
9798     if (oben_frei)
9799     {
9800       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9801       Store[ax][ay-1] = element;
9802       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9803       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9804         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9805                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9806       new_wall = TRUE;
9807     }
9808     if (unten_frei)
9809     {
9810       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9811       Store[ax][ay+1] = element;
9812       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9813       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9814         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9815                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9816       new_wall = TRUE;
9817     }
9818   }
9819
9820   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9821       element == EL_EXPANDABLE_STEELWALL_ANY)
9822   {
9823     if (links_frei)
9824     {
9825       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9826       Store[ax-1][ay] = element;
9827       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9828       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9829         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9830                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9831       new_wall = TRUE;
9832     }
9833
9834     if (rechts_frei)
9835     {
9836       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9837       Store[ax+1][ay] = element;
9838       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9839       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9840         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9841                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9842       new_wall = TRUE;
9843     }
9844   }
9845
9846   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9847     oben_massiv = TRUE;
9848   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9849     unten_massiv = TRUE;
9850   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9851     links_massiv = TRUE;
9852   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9853     rechts_massiv = TRUE;
9854
9855   if (((oben_massiv && unten_massiv) ||
9856        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9857       ((links_massiv && rechts_massiv) ||
9858        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9859     Tile[ax][ay] = EL_STEELWALL;
9860
9861   if (new_wall)
9862     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9863 }
9864
9865 static void CheckForDragon(int x, int y)
9866 {
9867   int i, j;
9868   boolean dragon_found = FALSE;
9869   static int xy[4][2] =
9870   {
9871     { 0, -1 },
9872     { -1, 0 },
9873     { +1, 0 },
9874     { 0, +1 }
9875   };
9876
9877   for (i = 0; i < NUM_DIRECTIONS; i++)
9878   {
9879     for (j = 0; j < 4; j++)
9880     {
9881       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9882
9883       if (IN_LEV_FIELD(xx, yy) &&
9884           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9885       {
9886         if (Tile[xx][yy] == EL_DRAGON)
9887           dragon_found = TRUE;
9888       }
9889       else
9890         break;
9891     }
9892   }
9893
9894   if (!dragon_found)
9895   {
9896     for (i = 0; i < NUM_DIRECTIONS; i++)
9897     {
9898       for (j = 0; j < 3; j++)
9899       {
9900         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9901   
9902         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9903         {
9904           Tile[xx][yy] = EL_EMPTY;
9905           TEST_DrawLevelField(xx, yy);
9906         }
9907         else
9908           break;
9909       }
9910     }
9911   }
9912 }
9913
9914 static void InitBuggyBase(int x, int y)
9915 {
9916   int element = Tile[x][y];
9917   int activating_delay = FRAMES_PER_SECOND / 4;
9918
9919   ChangeDelay[x][y] =
9920     (element == EL_SP_BUGGY_BASE ?
9921      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9922      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9923      activating_delay :
9924      element == EL_SP_BUGGY_BASE_ACTIVE ?
9925      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9926 }
9927
9928 static void WarnBuggyBase(int x, int y)
9929 {
9930   int i;
9931   static int xy[4][2] =
9932   {
9933     { 0, -1 },
9934     { -1, 0 },
9935     { +1, 0 },
9936     { 0, +1 }
9937   };
9938
9939   for (i = 0; i < NUM_DIRECTIONS; i++)
9940   {
9941     int xx = x + xy[i][0];
9942     int yy = y + xy[i][1];
9943
9944     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9945     {
9946       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9947
9948       break;
9949     }
9950   }
9951 }
9952
9953 static void InitTrap(int x, int y)
9954 {
9955   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9956 }
9957
9958 static void ActivateTrap(int x, int y)
9959 {
9960   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9961 }
9962
9963 static void ChangeActiveTrap(int x, int y)
9964 {
9965   int graphic = IMG_TRAP_ACTIVE;
9966
9967   // if new animation frame was drawn, correct crumbled sand border
9968   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9969     TEST_DrawLevelFieldCrumbled(x, y);
9970 }
9971
9972 static int getSpecialActionElement(int element, int number, int base_element)
9973 {
9974   return (element != EL_EMPTY ? element :
9975           number != -1 ? base_element + number - 1 :
9976           EL_EMPTY);
9977 }
9978
9979 static int getModifiedActionNumber(int value_old, int operator, int operand,
9980                                    int value_min, int value_max)
9981 {
9982   int value_new = (operator == CA_MODE_SET      ? operand :
9983                    operator == CA_MODE_ADD      ? value_old + operand :
9984                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9985                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9986                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9987                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9988                    value_old);
9989
9990   return (value_new < value_min ? value_min :
9991           value_new > value_max ? value_max :
9992           value_new);
9993 }
9994
9995 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9996 {
9997   struct ElementInfo *ei = &element_info[element];
9998   struct ElementChangeInfo *change = &ei->change_page[page];
9999   int target_element = change->target_element;
10000   int action_type = change->action_type;
10001   int action_mode = change->action_mode;
10002   int action_arg = change->action_arg;
10003   int action_element = change->action_element;
10004   int i;
10005
10006   if (!change->has_action)
10007     return;
10008
10009   // ---------- determine action paramater values -----------------------------
10010
10011   int level_time_value =
10012     (level.time > 0 ? TimeLeft :
10013      TimePlayed);
10014
10015   int action_arg_element_raw =
10016     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10017      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10018      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10019      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10020      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10021      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10022      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10023      EL_EMPTY);
10024   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10025
10026   int action_arg_direction =
10027     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10028      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10029      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10030      change->actual_trigger_side :
10031      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10032      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10033      MV_NONE);
10034
10035   int action_arg_number_min =
10036     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10037      CA_ARG_MIN);
10038
10039   int action_arg_number_max =
10040     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10041      action_type == CA_SET_LEVEL_GEMS ? 999 :
10042      action_type == CA_SET_LEVEL_TIME ? 9999 :
10043      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10044      action_type == CA_SET_CE_VALUE ? 9999 :
10045      action_type == CA_SET_CE_SCORE ? 9999 :
10046      CA_ARG_MAX);
10047
10048   int action_arg_number_reset =
10049     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10050      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10051      action_type == CA_SET_LEVEL_TIME ? level.time :
10052      action_type == CA_SET_LEVEL_SCORE ? 0 :
10053      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10054      action_type == CA_SET_CE_SCORE ? 0 :
10055      0);
10056
10057   int action_arg_number =
10058     (action_arg <= CA_ARG_MAX ? action_arg :
10059      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10060      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10061      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10062      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10063      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10064      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10065      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10066      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10067      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10068      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10069      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10070      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10071      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10072      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10073      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10074      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10075      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10076      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10077      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10078      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10079      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10080      -1);
10081
10082   int action_arg_number_old =
10083     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10084      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10085      action_type == CA_SET_LEVEL_SCORE ? game.score :
10086      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10087      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10088      0);
10089
10090   int action_arg_number_new =
10091     getModifiedActionNumber(action_arg_number_old,
10092                             action_mode, action_arg_number,
10093                             action_arg_number_min, action_arg_number_max);
10094
10095   int trigger_player_bits =
10096     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10097      change->actual_trigger_player_bits : change->trigger_player);
10098
10099   int action_arg_player_bits =
10100     (action_arg >= CA_ARG_PLAYER_1 &&
10101      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10102      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10103      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10104      PLAYER_BITS_ANY);
10105
10106   // ---------- execute action  -----------------------------------------------
10107
10108   switch (action_type)
10109   {
10110     case CA_NO_ACTION:
10111     {
10112       return;
10113     }
10114
10115     // ---------- level actions  ----------------------------------------------
10116
10117     case CA_RESTART_LEVEL:
10118     {
10119       game.restart_level = TRUE;
10120
10121       break;
10122     }
10123
10124     case CA_SHOW_ENVELOPE:
10125     {
10126       int element = getSpecialActionElement(action_arg_element,
10127                                             action_arg_number, EL_ENVELOPE_1);
10128
10129       if (IS_ENVELOPE(element))
10130         local_player->show_envelope = element;
10131
10132       break;
10133     }
10134
10135     case CA_SET_LEVEL_TIME:
10136     {
10137       if (level.time > 0)       // only modify limited time value
10138       {
10139         TimeLeft = action_arg_number_new;
10140
10141         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10142
10143         DisplayGameControlValues();
10144
10145         if (!TimeLeft && setup.time_limit)
10146           for (i = 0; i < MAX_PLAYERS; i++)
10147             KillPlayer(&stored_player[i]);
10148       }
10149
10150       break;
10151     }
10152
10153     case CA_SET_LEVEL_SCORE:
10154     {
10155       game.score = action_arg_number_new;
10156
10157       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10158
10159       DisplayGameControlValues();
10160
10161       break;
10162     }
10163
10164     case CA_SET_LEVEL_GEMS:
10165     {
10166       game.gems_still_needed = action_arg_number_new;
10167
10168       game.snapshot.collected_item = TRUE;
10169
10170       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10171
10172       DisplayGameControlValues();
10173
10174       break;
10175     }
10176
10177     case CA_SET_LEVEL_WIND:
10178     {
10179       game.wind_direction = action_arg_direction;
10180
10181       break;
10182     }
10183
10184     case CA_SET_LEVEL_RANDOM_SEED:
10185     {
10186       // ensure that setting a new random seed while playing is predictable
10187       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10188
10189       break;
10190     }
10191
10192     // ---------- player actions  ---------------------------------------------
10193
10194     case CA_MOVE_PLAYER:
10195     case CA_MOVE_PLAYER_NEW:
10196     {
10197       // automatically move to the next field in specified direction
10198       for (i = 0; i < MAX_PLAYERS; i++)
10199         if (trigger_player_bits & (1 << i))
10200           if (action_type == CA_MOVE_PLAYER ||
10201               stored_player[i].MovPos == 0)
10202             stored_player[i].programmed_action = action_arg_direction;
10203
10204       break;
10205     }
10206
10207     case CA_EXIT_PLAYER:
10208     {
10209       for (i = 0; i < MAX_PLAYERS; i++)
10210         if (action_arg_player_bits & (1 << i))
10211           ExitPlayer(&stored_player[i]);
10212
10213       if (game.players_still_needed == 0)
10214         LevelSolved();
10215
10216       break;
10217     }
10218
10219     case CA_KILL_PLAYER:
10220     {
10221       for (i = 0; i < MAX_PLAYERS; i++)
10222         if (action_arg_player_bits & (1 << i))
10223           KillPlayer(&stored_player[i]);
10224
10225       break;
10226     }
10227
10228     case CA_SET_PLAYER_KEYS:
10229     {
10230       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10231       int element = getSpecialActionElement(action_arg_element,
10232                                             action_arg_number, EL_KEY_1);
10233
10234       if (IS_KEY(element))
10235       {
10236         for (i = 0; i < MAX_PLAYERS; i++)
10237         {
10238           if (trigger_player_bits & (1 << i))
10239           {
10240             stored_player[i].key[KEY_NR(element)] = key_state;
10241
10242             DrawGameDoorValues();
10243           }
10244         }
10245       }
10246
10247       break;
10248     }
10249
10250     case CA_SET_PLAYER_SPEED:
10251     {
10252       for (i = 0; i < MAX_PLAYERS; i++)
10253       {
10254         if (trigger_player_bits & (1 << i))
10255         {
10256           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10257
10258           if (action_arg == CA_ARG_SPEED_FASTER &&
10259               stored_player[i].cannot_move)
10260           {
10261             action_arg_number = STEPSIZE_VERY_SLOW;
10262           }
10263           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10264                    action_arg == CA_ARG_SPEED_FASTER)
10265           {
10266             action_arg_number = 2;
10267             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10268                            CA_MODE_MULTIPLY);
10269           }
10270           else if (action_arg == CA_ARG_NUMBER_RESET)
10271           {
10272             action_arg_number = level.initial_player_stepsize[i];
10273           }
10274
10275           move_stepsize =
10276             getModifiedActionNumber(move_stepsize,
10277                                     action_mode,
10278                                     action_arg_number,
10279                                     action_arg_number_min,
10280                                     action_arg_number_max);
10281
10282           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10283         }
10284       }
10285
10286       break;
10287     }
10288
10289     case CA_SET_PLAYER_SHIELD:
10290     {
10291       for (i = 0; i < MAX_PLAYERS; i++)
10292       {
10293         if (trigger_player_bits & (1 << i))
10294         {
10295           if (action_arg == CA_ARG_SHIELD_OFF)
10296           {
10297             stored_player[i].shield_normal_time_left = 0;
10298             stored_player[i].shield_deadly_time_left = 0;
10299           }
10300           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10301           {
10302             stored_player[i].shield_normal_time_left = 999999;
10303           }
10304           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10305           {
10306             stored_player[i].shield_normal_time_left = 999999;
10307             stored_player[i].shield_deadly_time_left = 999999;
10308           }
10309         }
10310       }
10311
10312       break;
10313     }
10314
10315     case CA_SET_PLAYER_GRAVITY:
10316     {
10317       for (i = 0; i < MAX_PLAYERS; i++)
10318       {
10319         if (trigger_player_bits & (1 << i))
10320         {
10321           stored_player[i].gravity =
10322             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10323              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10324              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10325              stored_player[i].gravity);
10326         }
10327       }
10328
10329       break;
10330     }
10331
10332     case CA_SET_PLAYER_ARTWORK:
10333     {
10334       for (i = 0; i < MAX_PLAYERS; i++)
10335       {
10336         if (trigger_player_bits & (1 << i))
10337         {
10338           int artwork_element = action_arg_element;
10339
10340           if (action_arg == CA_ARG_ELEMENT_RESET)
10341             artwork_element =
10342               (level.use_artwork_element[i] ? level.artwork_element[i] :
10343                stored_player[i].element_nr);
10344
10345           if (stored_player[i].artwork_element != artwork_element)
10346             stored_player[i].Frame = 0;
10347
10348           stored_player[i].artwork_element = artwork_element;
10349
10350           SetPlayerWaiting(&stored_player[i], FALSE);
10351
10352           // set number of special actions for bored and sleeping animation
10353           stored_player[i].num_special_action_bored =
10354             get_num_special_action(artwork_element,
10355                                    ACTION_BORING_1, ACTION_BORING_LAST);
10356           stored_player[i].num_special_action_sleeping =
10357             get_num_special_action(artwork_element,
10358                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10359         }
10360       }
10361
10362       break;
10363     }
10364
10365     case CA_SET_PLAYER_INVENTORY:
10366     {
10367       for (i = 0; i < MAX_PLAYERS; i++)
10368       {
10369         struct PlayerInfo *player = &stored_player[i];
10370         int j, k;
10371
10372         if (trigger_player_bits & (1 << i))
10373         {
10374           int inventory_element = action_arg_element;
10375
10376           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10377               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10378               action_arg == CA_ARG_ELEMENT_ACTION)
10379           {
10380             int element = inventory_element;
10381             int collect_count = element_info[element].collect_count_initial;
10382
10383             if (!IS_CUSTOM_ELEMENT(element))
10384               collect_count = 1;
10385
10386             if (collect_count == 0)
10387               player->inventory_infinite_element = element;
10388             else
10389               for (k = 0; k < collect_count; k++)
10390                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10391                   player->inventory_element[player->inventory_size++] =
10392                     element;
10393           }
10394           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10395                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10396                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10397           {
10398             if (player->inventory_infinite_element != EL_UNDEFINED &&
10399                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10400                                      action_arg_element_raw))
10401               player->inventory_infinite_element = EL_UNDEFINED;
10402
10403             for (k = 0, j = 0; j < player->inventory_size; j++)
10404             {
10405               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10406                                         action_arg_element_raw))
10407                 player->inventory_element[k++] = player->inventory_element[j];
10408             }
10409
10410             player->inventory_size = k;
10411           }
10412           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10413           {
10414             if (player->inventory_size > 0)
10415             {
10416               for (j = 0; j < player->inventory_size - 1; j++)
10417                 player->inventory_element[j] = player->inventory_element[j + 1];
10418
10419               player->inventory_size--;
10420             }
10421           }
10422           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10423           {
10424             if (player->inventory_size > 0)
10425               player->inventory_size--;
10426           }
10427           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10428           {
10429             player->inventory_infinite_element = EL_UNDEFINED;
10430             player->inventory_size = 0;
10431           }
10432           else if (action_arg == CA_ARG_INVENTORY_RESET)
10433           {
10434             player->inventory_infinite_element = EL_UNDEFINED;
10435             player->inventory_size = 0;
10436
10437             if (level.use_initial_inventory[i])
10438             {
10439               for (j = 0; j < level.initial_inventory_size[i]; j++)
10440               {
10441                 int element = level.initial_inventory_content[i][j];
10442                 int collect_count = element_info[element].collect_count_initial;
10443
10444                 if (!IS_CUSTOM_ELEMENT(element))
10445                   collect_count = 1;
10446
10447                 if (collect_count == 0)
10448                   player->inventory_infinite_element = element;
10449                 else
10450                   for (k = 0; k < collect_count; k++)
10451                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10452                       player->inventory_element[player->inventory_size++] =
10453                         element;
10454               }
10455             }
10456           }
10457         }
10458       }
10459
10460       break;
10461     }
10462
10463     // ---------- CE actions  -------------------------------------------------
10464
10465     case CA_SET_CE_VALUE:
10466     {
10467       int last_ce_value = CustomValue[x][y];
10468
10469       CustomValue[x][y] = action_arg_number_new;
10470
10471       if (CustomValue[x][y] != last_ce_value)
10472       {
10473         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10474         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10475
10476         if (CustomValue[x][y] == 0)
10477         {
10478           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10479           ChangeCount[x][y] = 0;        // allow at least one more change
10480
10481           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10482           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10483         }
10484       }
10485
10486       break;
10487     }
10488
10489     case CA_SET_CE_SCORE:
10490     {
10491       int last_ce_score = ei->collect_score;
10492
10493       ei->collect_score = action_arg_number_new;
10494
10495       if (ei->collect_score != last_ce_score)
10496       {
10497         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10498         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10499
10500         if (ei->collect_score == 0)
10501         {
10502           int xx, yy;
10503
10504           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10505           ChangeCount[x][y] = 0;        // allow at least one more change
10506
10507           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10508           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10509
10510           /*
10511             This is a very special case that seems to be a mixture between
10512             CheckElementChange() and CheckTriggeredElementChange(): while
10513             the first one only affects single elements that are triggered
10514             directly, the second one affects multiple elements in the playfield
10515             that are triggered indirectly by another element. This is a third
10516             case: Changing the CE score always affects multiple identical CEs,
10517             so every affected CE must be checked, not only the single CE for
10518             which the CE score was changed in the first place (as every instance
10519             of that CE shares the same CE score, and therefore also can change)!
10520           */
10521           SCAN_PLAYFIELD(xx, yy)
10522           {
10523             if (Tile[xx][yy] == element)
10524               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10525                                  CE_SCORE_GETS_ZERO);
10526           }
10527         }
10528       }
10529
10530       break;
10531     }
10532
10533     case CA_SET_CE_ARTWORK:
10534     {
10535       int artwork_element = action_arg_element;
10536       boolean reset_frame = FALSE;
10537       int xx, yy;
10538
10539       if (action_arg == CA_ARG_ELEMENT_RESET)
10540         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10541                            element);
10542
10543       if (ei->gfx_element != artwork_element)
10544         reset_frame = TRUE;
10545
10546       ei->gfx_element = artwork_element;
10547
10548       SCAN_PLAYFIELD(xx, yy)
10549       {
10550         if (Tile[xx][yy] == element)
10551         {
10552           if (reset_frame)
10553           {
10554             ResetGfxAnimation(xx, yy);
10555             ResetRandomAnimationValue(xx, yy);
10556           }
10557
10558           TEST_DrawLevelField(xx, yy);
10559         }
10560       }
10561
10562       break;
10563     }
10564
10565     // ---------- engine actions  ---------------------------------------------
10566
10567     case CA_SET_ENGINE_SCAN_MODE:
10568     {
10569       InitPlayfieldScanMode(action_arg);
10570
10571       break;
10572     }
10573
10574     default:
10575       break;
10576   }
10577 }
10578
10579 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10580 {
10581   int old_element = Tile[x][y];
10582   int new_element = GetElementFromGroupElement(element);
10583   int previous_move_direction = MovDir[x][y];
10584   int last_ce_value = CustomValue[x][y];
10585   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10586   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10587   boolean add_player_onto_element = (new_element_is_player &&
10588                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10589                                      IS_WALKABLE(old_element));
10590
10591   if (!add_player_onto_element)
10592   {
10593     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10594       RemoveMovingField(x, y);
10595     else
10596       RemoveField(x, y);
10597
10598     Tile[x][y] = new_element;
10599
10600     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10601       MovDir[x][y] = previous_move_direction;
10602
10603     if (element_info[new_element].use_last_ce_value)
10604       CustomValue[x][y] = last_ce_value;
10605
10606     InitField_WithBug1(x, y, FALSE);
10607
10608     new_element = Tile[x][y];   // element may have changed
10609
10610     ResetGfxAnimation(x, y);
10611     ResetRandomAnimationValue(x, y);
10612
10613     TEST_DrawLevelField(x, y);
10614
10615     if (GFX_CRUMBLED(new_element))
10616       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10617   }
10618
10619   // check if element under the player changes from accessible to unaccessible
10620   // (needed for special case of dropping element which then changes)
10621   // (must be checked after creating new element for walkable group elements)
10622   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10623       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10624   {
10625     Bang(x, y);
10626
10627     return;
10628   }
10629
10630   // "ChangeCount" not set yet to allow "entered by player" change one time
10631   if (new_element_is_player)
10632     RelocatePlayer(x, y, new_element);
10633
10634   if (is_change)
10635     ChangeCount[x][y]++;        // count number of changes in the same frame
10636
10637   TestIfBadThingTouchesPlayer(x, y);
10638   TestIfPlayerTouchesCustomElement(x, y);
10639   TestIfElementTouchesCustomElement(x, y);
10640 }
10641
10642 static void CreateField(int x, int y, int element)
10643 {
10644   CreateFieldExt(x, y, element, FALSE);
10645 }
10646
10647 static void CreateElementFromChange(int x, int y, int element)
10648 {
10649   element = GET_VALID_RUNTIME_ELEMENT(element);
10650
10651   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10652   {
10653     int old_element = Tile[x][y];
10654
10655     // prevent changed element from moving in same engine frame
10656     // unless both old and new element can either fall or move
10657     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10658         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10659       Stop[x][y] = TRUE;
10660   }
10661
10662   CreateFieldExt(x, y, element, TRUE);
10663 }
10664
10665 static boolean ChangeElement(int x, int y, int element, int page)
10666 {
10667   struct ElementInfo *ei = &element_info[element];
10668   struct ElementChangeInfo *change = &ei->change_page[page];
10669   int ce_value = CustomValue[x][y];
10670   int ce_score = ei->collect_score;
10671   int target_element;
10672   int old_element = Tile[x][y];
10673
10674   // always use default change event to prevent running into a loop
10675   if (ChangeEvent[x][y] == -1)
10676     ChangeEvent[x][y] = CE_DELAY;
10677
10678   if (ChangeEvent[x][y] == CE_DELAY)
10679   {
10680     // reset actual trigger element, trigger player and action element
10681     change->actual_trigger_element = EL_EMPTY;
10682     change->actual_trigger_player = EL_EMPTY;
10683     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10684     change->actual_trigger_side = CH_SIDE_NONE;
10685     change->actual_trigger_ce_value = 0;
10686     change->actual_trigger_ce_score = 0;
10687   }
10688
10689   // do not change elements more than a specified maximum number of changes
10690   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10691     return FALSE;
10692
10693   ChangeCount[x][y]++;          // count number of changes in the same frame
10694
10695   if (change->explode)
10696   {
10697     Bang(x, y);
10698
10699     return TRUE;
10700   }
10701
10702   if (change->use_target_content)
10703   {
10704     boolean complete_replace = TRUE;
10705     boolean can_replace[3][3];
10706     int xx, yy;
10707
10708     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10709     {
10710       boolean is_empty;
10711       boolean is_walkable;
10712       boolean is_diggable;
10713       boolean is_collectible;
10714       boolean is_removable;
10715       boolean is_destructible;
10716       int ex = x + xx - 1;
10717       int ey = y + yy - 1;
10718       int content_element = change->target_content.e[xx][yy];
10719       int e;
10720
10721       can_replace[xx][yy] = TRUE;
10722
10723       if (ex == x && ey == y)   // do not check changing element itself
10724         continue;
10725
10726       if (content_element == EL_EMPTY_SPACE)
10727       {
10728         can_replace[xx][yy] = FALSE;    // do not replace border with space
10729
10730         continue;
10731       }
10732
10733       if (!IN_LEV_FIELD(ex, ey))
10734       {
10735         can_replace[xx][yy] = FALSE;
10736         complete_replace = FALSE;
10737
10738         continue;
10739       }
10740
10741       e = Tile[ex][ey];
10742
10743       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10744         e = MovingOrBlocked2Element(ex, ey);
10745
10746       is_empty = (IS_FREE(ex, ey) ||
10747                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10748
10749       is_walkable     = (is_empty || IS_WALKABLE(e));
10750       is_diggable     = (is_empty || IS_DIGGABLE(e));
10751       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10752       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10753       is_removable    = (is_diggable || is_collectible);
10754
10755       can_replace[xx][yy] =
10756         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10757           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10758           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10759           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10760           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10761           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10762          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10763
10764       if (!can_replace[xx][yy])
10765         complete_replace = FALSE;
10766     }
10767
10768     if (!change->only_if_complete || complete_replace)
10769     {
10770       boolean something_has_changed = FALSE;
10771
10772       if (change->only_if_complete && change->use_random_replace &&
10773           RND(100) < change->random_percentage)
10774         return FALSE;
10775
10776       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10777       {
10778         int ex = x + xx - 1;
10779         int ey = y + yy - 1;
10780         int content_element;
10781
10782         if (can_replace[xx][yy] && (!change->use_random_replace ||
10783                                     RND(100) < change->random_percentage))
10784         {
10785           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10786             RemoveMovingField(ex, ey);
10787
10788           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10789
10790           content_element = change->target_content.e[xx][yy];
10791           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10792                                               ce_value, ce_score);
10793
10794           CreateElementFromChange(ex, ey, target_element);
10795
10796           something_has_changed = TRUE;
10797
10798           // for symmetry reasons, freeze newly created border elements
10799           if (ex != x || ey != y)
10800             Stop[ex][ey] = TRUE;        // no more moving in this frame
10801         }
10802       }
10803
10804       if (something_has_changed)
10805       {
10806         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10807         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10808       }
10809     }
10810   }
10811   else
10812   {
10813     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10814                                         ce_value, ce_score);
10815
10816     if (element == EL_DIAGONAL_GROWING ||
10817         element == EL_DIAGONAL_SHRINKING)
10818     {
10819       target_element = Store[x][y];
10820
10821       Store[x][y] = EL_EMPTY;
10822     }
10823
10824     CreateElementFromChange(x, y, target_element);
10825
10826     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10827     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10828   }
10829
10830   // this uses direct change before indirect change
10831   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10832
10833   return TRUE;
10834 }
10835
10836 static void HandleElementChange(int x, int y, int page)
10837 {
10838   int element = MovingOrBlocked2Element(x, y);
10839   struct ElementInfo *ei = &element_info[element];
10840   struct ElementChangeInfo *change = &ei->change_page[page];
10841   boolean handle_action_before_change = FALSE;
10842
10843 #ifdef DEBUG
10844   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10845       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10846   {
10847     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10848           x, y, element, element_info[element].token_name);
10849     Debug("game:playing:HandleElementChange", "This should never happen!");
10850   }
10851 #endif
10852
10853   // this can happen with classic bombs on walkable, changing elements
10854   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10855   {
10856     return;
10857   }
10858
10859   if (ChangeDelay[x][y] == 0)           // initialize element change
10860   {
10861     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10862
10863     if (change->can_change)
10864     {
10865       // !!! not clear why graphic animation should be reset at all here !!!
10866       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10867       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10868
10869       /*
10870         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10871
10872         When using an animation frame delay of 1 (this only happens with
10873         "sp_zonk.moving.left/right" in the classic graphics), the default
10874         (non-moving) animation shows wrong animation frames (while the
10875         moving animation, like "sp_zonk.moving.left/right", is correct,
10876         so this graphical bug never shows up with the classic graphics).
10877         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10878         be drawn instead of the correct frames 0,1,2,3. This is caused by
10879         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10880         an element change: First when the change delay ("ChangeDelay[][]")
10881         counter has reached zero after decrementing, then a second time in
10882         the next frame (after "GfxFrame[][]" was already incremented) when
10883         "ChangeDelay[][]" is reset to the initial delay value again.
10884
10885         This causes frame 0 to be drawn twice, while the last frame won't
10886         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10887
10888         As some animations may already be cleverly designed around this bug
10889         (at least the "Snake Bite" snake tail animation does this), it cannot
10890         simply be fixed here without breaking such existing animations.
10891         Unfortunately, it cannot easily be detected if a graphics set was
10892         designed "before" or "after" the bug was fixed. As a workaround,
10893         a new graphics set option "game.graphics_engine_version" was added
10894         to be able to specify the game's major release version for which the
10895         graphics set was designed, which can then be used to decide if the
10896         bugfix should be used (version 4 and above) or not (version 3 or
10897         below, or if no version was specified at all, as with old sets).
10898
10899         (The wrong/fixed animation frames can be tested with the test level set
10900         "test_gfxframe" and level "000", which contains a specially prepared
10901         custom element at level position (x/y) == (11/9) which uses the zonk
10902         animation mentioned above. Using "game.graphics_engine_version: 4"
10903         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10904         This can also be seen from the debug output for this test element.)
10905       */
10906
10907       // when a custom element is about to change (for example by change delay),
10908       // do not reset graphic animation when the custom element is moving
10909       if (game.graphics_engine_version < 4 &&
10910           !IS_MOVING(x, y))
10911       {
10912         ResetGfxAnimation(x, y);
10913         ResetRandomAnimationValue(x, y);
10914       }
10915
10916       if (change->pre_change_function)
10917         change->pre_change_function(x, y);
10918     }
10919   }
10920
10921   ChangeDelay[x][y]--;
10922
10923   if (ChangeDelay[x][y] != 0)           // continue element change
10924   {
10925     if (change->can_change)
10926     {
10927       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10928
10929       if (IS_ANIMATED(graphic))
10930         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10931
10932       if (change->change_function)
10933         change->change_function(x, y);
10934     }
10935   }
10936   else                                  // finish element change
10937   {
10938     if (ChangePage[x][y] != -1)         // remember page from delayed change
10939     {
10940       page = ChangePage[x][y];
10941       ChangePage[x][y] = -1;
10942
10943       change = &ei->change_page[page];
10944     }
10945
10946     if (IS_MOVING(x, y))                // never change a running system ;-)
10947     {
10948       ChangeDelay[x][y] = 1;            // try change after next move step
10949       ChangePage[x][y] = page;          // remember page to use for change
10950
10951       return;
10952     }
10953
10954     // special case: set new level random seed before changing element
10955     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10956       handle_action_before_change = TRUE;
10957
10958     if (change->has_action && handle_action_before_change)
10959       ExecuteCustomElementAction(x, y, element, page);
10960
10961     if (change->can_change)
10962     {
10963       if (ChangeElement(x, y, element, page))
10964       {
10965         if (change->post_change_function)
10966           change->post_change_function(x, y);
10967       }
10968     }
10969
10970     if (change->has_action && !handle_action_before_change)
10971       ExecuteCustomElementAction(x, y, element, page);
10972   }
10973 }
10974
10975 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10976                                               int trigger_element,
10977                                               int trigger_event,
10978                                               int trigger_player,
10979                                               int trigger_side,
10980                                               int trigger_page)
10981 {
10982   boolean change_done_any = FALSE;
10983   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10984   int i;
10985
10986   if (!(trigger_events[trigger_element][trigger_event]))
10987     return FALSE;
10988
10989   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10990
10991   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10992   {
10993     int element = EL_CUSTOM_START + i;
10994     boolean change_done = FALSE;
10995     int p;
10996
10997     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10998         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10999       continue;
11000
11001     for (p = 0; p < element_info[element].num_change_pages; p++)
11002     {
11003       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11004
11005       if (change->can_change_or_has_action &&
11006           change->has_event[trigger_event] &&
11007           change->trigger_side & trigger_side &&
11008           change->trigger_player & trigger_player &&
11009           change->trigger_page & trigger_page_bits &&
11010           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11011       {
11012         change->actual_trigger_element = trigger_element;
11013         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11014         change->actual_trigger_player_bits = trigger_player;
11015         change->actual_trigger_side = trigger_side;
11016         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11017         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11018
11019         if ((change->can_change && !change_done) || change->has_action)
11020         {
11021           int x, y;
11022
11023           SCAN_PLAYFIELD(x, y)
11024           {
11025             if (Tile[x][y] == element)
11026             {
11027               if (change->can_change && !change_done)
11028               {
11029                 // if element already changed in this frame, not only prevent
11030                 // another element change (checked in ChangeElement()), but
11031                 // also prevent additional element actions for this element
11032
11033                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11034                     !level.use_action_after_change_bug)
11035                   continue;
11036
11037                 ChangeDelay[x][y] = 1;
11038                 ChangeEvent[x][y] = trigger_event;
11039
11040                 HandleElementChange(x, y, p);
11041               }
11042               else if (change->has_action)
11043               {
11044                 // if element already changed in this frame, not only prevent
11045                 // another element change (checked in ChangeElement()), but
11046                 // also prevent additional element actions for this element
11047
11048                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11049                     !level.use_action_after_change_bug)
11050                   continue;
11051
11052                 ExecuteCustomElementAction(x, y, element, p);
11053                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11054               }
11055             }
11056           }
11057
11058           if (change->can_change)
11059           {
11060             change_done = TRUE;
11061             change_done_any = TRUE;
11062           }
11063         }
11064       }
11065     }
11066   }
11067
11068   RECURSION_LOOP_DETECTION_END();
11069
11070   return change_done_any;
11071 }
11072
11073 static boolean CheckElementChangeExt(int x, int y,
11074                                      int element,
11075                                      int trigger_element,
11076                                      int trigger_event,
11077                                      int trigger_player,
11078                                      int trigger_side)
11079 {
11080   boolean change_done = FALSE;
11081   int p;
11082
11083   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11084       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11085     return FALSE;
11086
11087   if (Tile[x][y] == EL_BLOCKED)
11088   {
11089     Blocked2Moving(x, y, &x, &y);
11090     element = Tile[x][y];
11091   }
11092
11093   // check if element has already changed or is about to change after moving
11094   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11095        Tile[x][y] != element) ||
11096
11097       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11098        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11099         ChangePage[x][y] != -1)))
11100     return FALSE;
11101
11102   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11103
11104   for (p = 0; p < element_info[element].num_change_pages; p++)
11105   {
11106     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11107
11108     /* check trigger element for all events where the element that is checked
11109        for changing interacts with a directly adjacent element -- this is
11110        different to element changes that affect other elements to change on the
11111        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11112     boolean check_trigger_element =
11113       (trigger_event == CE_TOUCHING_X ||
11114        trigger_event == CE_HITTING_X ||
11115        trigger_event == CE_HIT_BY_X ||
11116        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11117
11118     if (change->can_change_or_has_action &&
11119         change->has_event[trigger_event] &&
11120         change->trigger_side & trigger_side &&
11121         change->trigger_player & trigger_player &&
11122         (!check_trigger_element ||
11123          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11124     {
11125       change->actual_trigger_element = trigger_element;
11126       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11127       change->actual_trigger_player_bits = trigger_player;
11128       change->actual_trigger_side = trigger_side;
11129       change->actual_trigger_ce_value = CustomValue[x][y];
11130       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11131
11132       // special case: trigger element not at (x,y) position for some events
11133       if (check_trigger_element)
11134       {
11135         static struct
11136         {
11137           int dx, dy;
11138         } move_xy[] =
11139           {
11140             {  0,  0 },
11141             { -1,  0 },
11142             { +1,  0 },
11143             {  0,  0 },
11144             {  0, -1 },
11145             {  0,  0 }, { 0, 0 }, { 0, 0 },
11146             {  0, +1 }
11147           };
11148
11149         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11150         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11151
11152         change->actual_trigger_ce_value = CustomValue[xx][yy];
11153         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11154       }
11155
11156       if (change->can_change && !change_done)
11157       {
11158         ChangeDelay[x][y] = 1;
11159         ChangeEvent[x][y] = trigger_event;
11160
11161         HandleElementChange(x, y, p);
11162
11163         change_done = TRUE;
11164       }
11165       else if (change->has_action)
11166       {
11167         ExecuteCustomElementAction(x, y, element, p);
11168         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11169       }
11170     }
11171   }
11172
11173   RECURSION_LOOP_DETECTION_END();
11174
11175   return change_done;
11176 }
11177
11178 static void PlayPlayerSound(struct PlayerInfo *player)
11179 {
11180   int jx = player->jx, jy = player->jy;
11181   int sound_element = player->artwork_element;
11182   int last_action = player->last_action_waiting;
11183   int action = player->action_waiting;
11184
11185   if (player->is_waiting)
11186   {
11187     if (action != last_action)
11188       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11189     else
11190       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11191   }
11192   else
11193   {
11194     if (action != last_action)
11195       StopSound(element_info[sound_element].sound[last_action]);
11196
11197     if (last_action == ACTION_SLEEPING)
11198       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11199   }
11200 }
11201
11202 static void PlayAllPlayersSound(void)
11203 {
11204   int i;
11205
11206   for (i = 0; i < MAX_PLAYERS; i++)
11207     if (stored_player[i].active)
11208       PlayPlayerSound(&stored_player[i]);
11209 }
11210
11211 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11212 {
11213   boolean last_waiting = player->is_waiting;
11214   int move_dir = player->MovDir;
11215
11216   player->dir_waiting = move_dir;
11217   player->last_action_waiting = player->action_waiting;
11218
11219   if (is_waiting)
11220   {
11221     if (!last_waiting)          // not waiting -> waiting
11222     {
11223       player->is_waiting = TRUE;
11224
11225       player->frame_counter_bored =
11226         FrameCounter +
11227         game.player_boring_delay_fixed +
11228         GetSimpleRandom(game.player_boring_delay_random);
11229       player->frame_counter_sleeping =
11230         FrameCounter +
11231         game.player_sleeping_delay_fixed +
11232         GetSimpleRandom(game.player_sleeping_delay_random);
11233
11234       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11235     }
11236
11237     if (game.player_sleeping_delay_fixed +
11238         game.player_sleeping_delay_random > 0 &&
11239         player->anim_delay_counter == 0 &&
11240         player->post_delay_counter == 0 &&
11241         FrameCounter >= player->frame_counter_sleeping)
11242       player->is_sleeping = TRUE;
11243     else if (game.player_boring_delay_fixed +
11244              game.player_boring_delay_random > 0 &&
11245              FrameCounter >= player->frame_counter_bored)
11246       player->is_bored = TRUE;
11247
11248     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11249                               player->is_bored ? ACTION_BORING :
11250                               ACTION_WAITING);
11251
11252     if (player->is_sleeping && player->use_murphy)
11253     {
11254       // special case for sleeping Murphy when leaning against non-free tile
11255
11256       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11257           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11258            !IS_MOVING(player->jx - 1, player->jy)))
11259         move_dir = MV_LEFT;
11260       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11261                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11262                 !IS_MOVING(player->jx + 1, player->jy)))
11263         move_dir = MV_RIGHT;
11264       else
11265         player->is_sleeping = FALSE;
11266
11267       player->dir_waiting = move_dir;
11268     }
11269
11270     if (player->is_sleeping)
11271     {
11272       if (player->num_special_action_sleeping > 0)
11273       {
11274         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11275         {
11276           int last_special_action = player->special_action_sleeping;
11277           int num_special_action = player->num_special_action_sleeping;
11278           int special_action =
11279             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11280              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11281              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11282              last_special_action + 1 : ACTION_SLEEPING);
11283           int special_graphic =
11284             el_act_dir2img(player->artwork_element, special_action, move_dir);
11285
11286           player->anim_delay_counter =
11287             graphic_info[special_graphic].anim_delay_fixed +
11288             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11289           player->post_delay_counter =
11290             graphic_info[special_graphic].post_delay_fixed +
11291             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11292
11293           player->special_action_sleeping = special_action;
11294         }
11295
11296         if (player->anim_delay_counter > 0)
11297         {
11298           player->action_waiting = player->special_action_sleeping;
11299           player->anim_delay_counter--;
11300         }
11301         else if (player->post_delay_counter > 0)
11302         {
11303           player->post_delay_counter--;
11304         }
11305       }
11306     }
11307     else if (player->is_bored)
11308     {
11309       if (player->num_special_action_bored > 0)
11310       {
11311         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11312         {
11313           int special_action =
11314             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11315           int special_graphic =
11316             el_act_dir2img(player->artwork_element, special_action, move_dir);
11317
11318           player->anim_delay_counter =
11319             graphic_info[special_graphic].anim_delay_fixed +
11320             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11321           player->post_delay_counter =
11322             graphic_info[special_graphic].post_delay_fixed +
11323             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11324
11325           player->special_action_bored = special_action;
11326         }
11327
11328         if (player->anim_delay_counter > 0)
11329         {
11330           player->action_waiting = player->special_action_bored;
11331           player->anim_delay_counter--;
11332         }
11333         else if (player->post_delay_counter > 0)
11334         {
11335           player->post_delay_counter--;
11336         }
11337       }
11338     }
11339   }
11340   else if (last_waiting)        // waiting -> not waiting
11341   {
11342     player->is_waiting = FALSE;
11343     player->is_bored = FALSE;
11344     player->is_sleeping = FALSE;
11345
11346     player->frame_counter_bored = -1;
11347     player->frame_counter_sleeping = -1;
11348
11349     player->anim_delay_counter = 0;
11350     player->post_delay_counter = 0;
11351
11352     player->dir_waiting = player->MovDir;
11353     player->action_waiting = ACTION_DEFAULT;
11354
11355     player->special_action_bored = ACTION_DEFAULT;
11356     player->special_action_sleeping = ACTION_DEFAULT;
11357   }
11358 }
11359
11360 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11361 {
11362   if ((!player->is_moving  && player->was_moving) ||
11363       (player->MovPos == 0 && player->was_moving) ||
11364       (player->is_snapping && !player->was_snapping) ||
11365       (player->is_dropping && !player->was_dropping))
11366   {
11367     if (!CheckSaveEngineSnapshotToList())
11368       return;
11369
11370     player->was_moving = FALSE;
11371     player->was_snapping = TRUE;
11372     player->was_dropping = TRUE;
11373   }
11374   else
11375   {
11376     if (player->is_moving)
11377       player->was_moving = TRUE;
11378
11379     if (!player->is_snapping)
11380       player->was_snapping = FALSE;
11381
11382     if (!player->is_dropping)
11383       player->was_dropping = FALSE;
11384   }
11385
11386   static struct MouseActionInfo mouse_action_last = { 0 };
11387   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11388   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11389
11390   if (new_released)
11391     CheckSaveEngineSnapshotToList();
11392
11393   mouse_action_last = mouse_action;
11394 }
11395
11396 static void CheckSingleStepMode(struct PlayerInfo *player)
11397 {
11398   if (tape.single_step && tape.recording && !tape.pausing)
11399   {
11400     // as it is called "single step mode", just return to pause mode when the
11401     // player stopped moving after one tile (or never starts moving at all)
11402     // (reverse logic needed here in case single step mode used in team mode)
11403     if (player->is_moving ||
11404         player->is_pushing ||
11405         player->is_dropping_pressed ||
11406         player->effective_mouse_action.button)
11407       game.enter_single_step_mode = FALSE;
11408   }
11409
11410   CheckSaveEngineSnapshot(player);
11411 }
11412
11413 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11414 {
11415   int left      = player_action & JOY_LEFT;
11416   int right     = player_action & JOY_RIGHT;
11417   int up        = player_action & JOY_UP;
11418   int down      = player_action & JOY_DOWN;
11419   int button1   = player_action & JOY_BUTTON_1;
11420   int button2   = player_action & JOY_BUTTON_2;
11421   int dx        = (left ? -1 : right ? 1 : 0);
11422   int dy        = (up   ? -1 : down  ? 1 : 0);
11423
11424   if (!player->active || tape.pausing)
11425     return 0;
11426
11427   if (player_action)
11428   {
11429     if (button1)
11430       SnapField(player, dx, dy);
11431     else
11432     {
11433       if (button2)
11434         DropElement(player);
11435
11436       MovePlayer(player, dx, dy);
11437     }
11438
11439     CheckSingleStepMode(player);
11440
11441     SetPlayerWaiting(player, FALSE);
11442
11443     return player_action;
11444   }
11445   else
11446   {
11447     // no actions for this player (no input at player's configured device)
11448
11449     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11450     SnapField(player, 0, 0);
11451     CheckGravityMovementWhenNotMoving(player);
11452
11453     if (player->MovPos == 0)
11454       SetPlayerWaiting(player, TRUE);
11455
11456     if (player->MovPos == 0)    // needed for tape.playing
11457       player->is_moving = FALSE;
11458
11459     player->is_dropping = FALSE;
11460     player->is_dropping_pressed = FALSE;
11461     player->drop_pressed_delay = 0;
11462
11463     CheckSingleStepMode(player);
11464
11465     return 0;
11466   }
11467 }
11468
11469 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11470                                          byte *tape_action)
11471 {
11472   if (!tape.use_mouse_actions)
11473     return;
11474
11475   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11476   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11477   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11478 }
11479
11480 static void SetTapeActionFromMouseAction(byte *tape_action,
11481                                          struct MouseActionInfo *mouse_action)
11482 {
11483   if (!tape.use_mouse_actions)
11484     return;
11485
11486   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11487   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11488   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11489 }
11490
11491 static void CheckLevelSolved(void)
11492 {
11493   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11494   {
11495     if (game_em.level_solved &&
11496         !game_em.game_over)                             // game won
11497     {
11498       LevelSolved();
11499
11500       game_em.game_over = TRUE;
11501
11502       game.all_players_gone = TRUE;
11503     }
11504
11505     if (game_em.game_over)                              // game lost
11506       game.all_players_gone = TRUE;
11507   }
11508   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11509   {
11510     if (game_sp.level_solved &&
11511         !game_sp.game_over)                             // game won
11512     {
11513       LevelSolved();
11514
11515       game_sp.game_over = TRUE;
11516
11517       game.all_players_gone = TRUE;
11518     }
11519
11520     if (game_sp.game_over)                              // game lost
11521       game.all_players_gone = TRUE;
11522   }
11523   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11524   {
11525     if (game_mm.level_solved &&
11526         !game_mm.game_over)                             // game won
11527     {
11528       LevelSolved();
11529
11530       game_mm.game_over = TRUE;
11531
11532       game.all_players_gone = TRUE;
11533     }
11534
11535     if (game_mm.game_over)                              // game lost
11536       game.all_players_gone = TRUE;
11537   }
11538 }
11539
11540 static void CheckLevelTime(void)
11541 {
11542   int i;
11543
11544   if (TimeFrames >= FRAMES_PER_SECOND)
11545   {
11546     TimeFrames = 0;
11547     TapeTime++;
11548
11549     for (i = 0; i < MAX_PLAYERS; i++)
11550     {
11551       struct PlayerInfo *player = &stored_player[i];
11552
11553       if (SHIELD_ON(player))
11554       {
11555         player->shield_normal_time_left--;
11556
11557         if (player->shield_deadly_time_left > 0)
11558           player->shield_deadly_time_left--;
11559       }
11560     }
11561
11562     if (!game.LevelSolved && !level.use_step_counter)
11563     {
11564       TimePlayed++;
11565
11566       if (TimeLeft > 0)
11567       {
11568         TimeLeft--;
11569
11570         if (TimeLeft <= 10 && setup.time_limit)
11571           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11572
11573         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11574            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11575
11576         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11577
11578         if (!TimeLeft && setup.time_limit)
11579         {
11580           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11581             game_em.lev->killed_out_of_time = TRUE;
11582           else
11583             for (i = 0; i < MAX_PLAYERS; i++)
11584               KillPlayer(&stored_player[i]);
11585         }
11586       }
11587       else if (game.no_time_limit && !game.all_players_gone)
11588       {
11589         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11590       }
11591
11592       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11593     }
11594
11595     if (tape.recording || tape.playing)
11596       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11597   }
11598
11599   if (tape.recording || tape.playing)
11600     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11601
11602   UpdateAndDisplayGameControlValues();
11603 }
11604
11605 void AdvanceFrameAndPlayerCounters(int player_nr)
11606 {
11607   int i;
11608
11609   // advance frame counters (global frame counter and time frame counter)
11610   FrameCounter++;
11611   TimeFrames++;
11612
11613   // advance player counters (counters for move delay, move animation etc.)
11614   for (i = 0; i < MAX_PLAYERS; i++)
11615   {
11616     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11617     int move_delay_value = stored_player[i].move_delay_value;
11618     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11619
11620     if (!advance_player_counters)       // not all players may be affected
11621       continue;
11622
11623     if (move_frames == 0)       // less than one move per game frame
11624     {
11625       int stepsize = TILEX / move_delay_value;
11626       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11627       int count = (stored_player[i].is_moving ?
11628                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11629
11630       if (count % delay == 0)
11631         move_frames = 1;
11632     }
11633
11634     stored_player[i].Frame += move_frames;
11635
11636     if (stored_player[i].MovPos != 0)
11637       stored_player[i].StepFrame += move_frames;
11638
11639     if (stored_player[i].move_delay > 0)
11640       stored_player[i].move_delay--;
11641
11642     // due to bugs in previous versions, counter must count up, not down
11643     if (stored_player[i].push_delay != -1)
11644       stored_player[i].push_delay++;
11645
11646     if (stored_player[i].drop_delay > 0)
11647       stored_player[i].drop_delay--;
11648
11649     if (stored_player[i].is_dropping_pressed)
11650       stored_player[i].drop_pressed_delay++;
11651   }
11652 }
11653
11654 void StartGameActions(boolean init_network_game, boolean record_tape,
11655                       int random_seed)
11656 {
11657   unsigned int new_random_seed = InitRND(random_seed);
11658
11659   if (record_tape)
11660     TapeStartRecording(new_random_seed);
11661
11662   if (init_network_game)
11663   {
11664     SendToServer_LevelFile();
11665     SendToServer_StartPlaying();
11666
11667     return;
11668   }
11669
11670   InitGame();
11671 }
11672
11673 static void GameActionsExt(void)
11674 {
11675 #if 0
11676   static unsigned int game_frame_delay = 0;
11677 #endif
11678   unsigned int game_frame_delay_value;
11679   byte *recorded_player_action;
11680   byte summarized_player_action = 0;
11681   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11682   int i;
11683
11684   // detect endless loops, caused by custom element programming
11685   if (recursion_loop_detected && recursion_loop_depth == 0)
11686   {
11687     char *message = getStringCat3("Internal Error! Element ",
11688                                   EL_NAME(recursion_loop_element),
11689                                   " caused endless loop! Quit the game?");
11690
11691     Warn("element '%s' caused endless loop in game engine",
11692          EL_NAME(recursion_loop_element));
11693
11694     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11695
11696     recursion_loop_detected = FALSE;    // if game should be continued
11697
11698     free(message);
11699
11700     return;
11701   }
11702
11703   if (game.restart_level)
11704     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11705
11706   CheckLevelSolved();
11707
11708   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11709     GameWon();
11710
11711   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11712     TapeStop();
11713
11714   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11715     return;
11716
11717   game_frame_delay_value =
11718     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11719
11720   if (tape.playing && tape.warp_forward && !tape.pausing)
11721     game_frame_delay_value = 0;
11722
11723   SetVideoFrameDelay(game_frame_delay_value);
11724
11725   // (de)activate virtual buttons depending on current game status
11726   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11727   {
11728     if (game.all_players_gone)  // if no players there to be controlled anymore
11729       SetOverlayActive(FALSE);
11730     else if (!tape.playing)     // if game continues after tape stopped playing
11731       SetOverlayActive(TRUE);
11732   }
11733
11734 #if 0
11735 #if 0
11736   // ---------- main game synchronization point ----------
11737
11738   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11739
11740   Debug("game:playing:skip", "skip == %d", skip);
11741
11742 #else
11743   // ---------- main game synchronization point ----------
11744
11745   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11746 #endif
11747 #endif
11748
11749   if (network_playing && !network_player_action_received)
11750   {
11751     // try to get network player actions in time
11752
11753     // last chance to get network player actions without main loop delay
11754     HandleNetworking();
11755
11756     // game was quit by network peer
11757     if (game_status != GAME_MODE_PLAYING)
11758       return;
11759
11760     // check if network player actions still missing and game still running
11761     if (!network_player_action_received && !checkGameEnded())
11762       return;           // failed to get network player actions in time
11763
11764     // do not yet reset "network_player_action_received" (for tape.pausing)
11765   }
11766
11767   if (tape.pausing)
11768     return;
11769
11770   // at this point we know that we really continue executing the game
11771
11772   network_player_action_received = FALSE;
11773
11774   // when playing tape, read previously recorded player input from tape data
11775   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11776
11777   local_player->effective_mouse_action = local_player->mouse_action;
11778
11779   if (recorded_player_action != NULL)
11780     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11781                                  recorded_player_action);
11782
11783   // TapePlayAction() may return NULL when toggling to "pause before death"
11784   if (tape.pausing)
11785     return;
11786
11787   if (tape.set_centered_player)
11788   {
11789     game.centered_player_nr_next = tape.centered_player_nr_next;
11790     game.set_centered_player = TRUE;
11791   }
11792
11793   for (i = 0; i < MAX_PLAYERS; i++)
11794   {
11795     summarized_player_action |= stored_player[i].action;
11796
11797     if (!network_playing && (game.team_mode || tape.playing))
11798       stored_player[i].effective_action = stored_player[i].action;
11799   }
11800
11801   if (network_playing && !checkGameEnded())
11802     SendToServer_MovePlayer(summarized_player_action);
11803
11804   // summarize all actions at local players mapped input device position
11805   // (this allows using different input devices in single player mode)
11806   if (!network.enabled && !game.team_mode)
11807     stored_player[map_player_action[local_player->index_nr]].effective_action =
11808       summarized_player_action;
11809
11810   // summarize all actions at centered player in local team mode
11811   if (tape.recording &&
11812       setup.team_mode && !network.enabled &&
11813       setup.input_on_focus &&
11814       game.centered_player_nr != -1)
11815   {
11816     for (i = 0; i < MAX_PLAYERS; i++)
11817       stored_player[map_player_action[i]].effective_action =
11818         (i == game.centered_player_nr ? summarized_player_action : 0);
11819   }
11820
11821   if (recorded_player_action != NULL)
11822     for (i = 0; i < MAX_PLAYERS; i++)
11823       stored_player[i].effective_action = recorded_player_action[i];
11824
11825   for (i = 0; i < MAX_PLAYERS; i++)
11826   {
11827     tape_action[i] = stored_player[i].effective_action;
11828
11829     /* (this may happen in the RND game engine if a player was not present on
11830        the playfield on level start, but appeared later from a custom element */
11831     if (setup.team_mode &&
11832         tape.recording &&
11833         tape_action[i] &&
11834         !tape.player_participates[i])
11835       tape.player_participates[i] = TRUE;
11836   }
11837
11838   SetTapeActionFromMouseAction(tape_action,
11839                                &local_player->effective_mouse_action);
11840
11841   // only record actions from input devices, but not programmed actions
11842   if (tape.recording)
11843     TapeRecordAction(tape_action);
11844
11845   // remember if game was played (especially after tape stopped playing)
11846   if (!tape.playing && summarized_player_action)
11847     game.GamePlayed = TRUE;
11848
11849 #if USE_NEW_PLAYER_ASSIGNMENTS
11850   // !!! also map player actions in single player mode !!!
11851   // if (game.team_mode)
11852   if (1)
11853   {
11854     byte mapped_action[MAX_PLAYERS];
11855
11856 #if DEBUG_PLAYER_ACTIONS
11857     for (i = 0; i < MAX_PLAYERS; i++)
11858       DebugContinued("", "%d, ", stored_player[i].effective_action);
11859 #endif
11860
11861     for (i = 0; i < MAX_PLAYERS; i++)
11862       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11863
11864     for (i = 0; i < MAX_PLAYERS; i++)
11865       stored_player[i].effective_action = mapped_action[i];
11866
11867 #if DEBUG_PLAYER_ACTIONS
11868     DebugContinued("", "=> ");
11869     for (i = 0; i < MAX_PLAYERS; i++)
11870       DebugContinued("", "%d, ", stored_player[i].effective_action);
11871     DebugContinued("game:playing:player", "\n");
11872 #endif
11873   }
11874 #if DEBUG_PLAYER_ACTIONS
11875   else
11876   {
11877     for (i = 0; i < MAX_PLAYERS; i++)
11878       DebugContinued("", "%d, ", stored_player[i].effective_action);
11879     DebugContinued("game:playing:player", "\n");
11880   }
11881 #endif
11882 #endif
11883
11884   for (i = 0; i < MAX_PLAYERS; i++)
11885   {
11886     // allow engine snapshot in case of changed movement attempt
11887     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11888         (stored_player[i].effective_action & KEY_MOTION))
11889       game.snapshot.changed_action = TRUE;
11890
11891     // allow engine snapshot in case of snapping/dropping attempt
11892     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11893         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11894       game.snapshot.changed_action = TRUE;
11895
11896     game.snapshot.last_action[i] = stored_player[i].effective_action;
11897   }
11898
11899   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11900   {
11901     GameActions_EM_Main();
11902   }
11903   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11904   {
11905     GameActions_SP_Main();
11906   }
11907   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11908   {
11909     GameActions_MM_Main();
11910   }
11911   else
11912   {
11913     GameActions_RND_Main();
11914   }
11915
11916   BlitScreenToBitmap(backbuffer);
11917
11918   CheckLevelSolved();
11919   CheckLevelTime();
11920
11921   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11922
11923   if (global.show_frames_per_second)
11924   {
11925     static unsigned int fps_counter = 0;
11926     static int fps_frames = 0;
11927     unsigned int fps_delay_ms = Counter() - fps_counter;
11928
11929     fps_frames++;
11930
11931     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11932     {
11933       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11934
11935       fps_frames = 0;
11936       fps_counter = Counter();
11937
11938       // always draw FPS to screen after FPS value was updated
11939       redraw_mask |= REDRAW_FPS;
11940     }
11941
11942     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11943     if (GetDrawDeactivationMask() == REDRAW_NONE)
11944       redraw_mask |= REDRAW_FPS;
11945   }
11946 }
11947
11948 static void GameActions_CheckSaveEngineSnapshot(void)
11949 {
11950   if (!game.snapshot.save_snapshot)
11951     return;
11952
11953   // clear flag for saving snapshot _before_ saving snapshot
11954   game.snapshot.save_snapshot = FALSE;
11955
11956   SaveEngineSnapshotToList();
11957 }
11958
11959 void GameActions(void)
11960 {
11961   GameActionsExt();
11962
11963   GameActions_CheckSaveEngineSnapshot();
11964 }
11965
11966 void GameActions_EM_Main(void)
11967 {
11968   byte effective_action[MAX_PLAYERS];
11969   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11970   int i;
11971
11972   for (i = 0; i < MAX_PLAYERS; i++)
11973     effective_action[i] = stored_player[i].effective_action;
11974
11975   GameActions_EM(effective_action, warp_mode);
11976 }
11977
11978 void GameActions_SP_Main(void)
11979 {
11980   byte effective_action[MAX_PLAYERS];
11981   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11982   int i;
11983
11984   for (i = 0; i < MAX_PLAYERS; i++)
11985     effective_action[i] = stored_player[i].effective_action;
11986
11987   GameActions_SP(effective_action, warp_mode);
11988
11989   for (i = 0; i < MAX_PLAYERS; i++)
11990   {
11991     if (stored_player[i].force_dropping)
11992       stored_player[i].action |= KEY_BUTTON_DROP;
11993
11994     stored_player[i].force_dropping = FALSE;
11995   }
11996 }
11997
11998 void GameActions_MM_Main(void)
11999 {
12000   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12001
12002   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12003 }
12004
12005 void GameActions_RND_Main(void)
12006 {
12007   GameActions_RND();
12008 }
12009
12010 void GameActions_RND(void)
12011 {
12012   static struct MouseActionInfo mouse_action_last = { 0 };
12013   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12014   int magic_wall_x = 0, magic_wall_y = 0;
12015   int i, x, y, element, graphic, last_gfx_frame;
12016
12017   InitPlayfieldScanModeVars();
12018
12019   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12020   {
12021     SCAN_PLAYFIELD(x, y)
12022     {
12023       ChangeCount[x][y] = 0;
12024       ChangeEvent[x][y] = -1;
12025     }
12026   }
12027
12028   if (game.set_centered_player)
12029   {
12030     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12031
12032     // switching to "all players" only possible if all players fit to screen
12033     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12034     {
12035       game.centered_player_nr_next = game.centered_player_nr;
12036       game.set_centered_player = FALSE;
12037     }
12038
12039     // do not switch focus to non-existing (or non-active) player
12040     if (game.centered_player_nr_next >= 0 &&
12041         !stored_player[game.centered_player_nr_next].active)
12042     {
12043       game.centered_player_nr_next = game.centered_player_nr;
12044       game.set_centered_player = FALSE;
12045     }
12046   }
12047
12048   if (game.set_centered_player &&
12049       ScreenMovPos == 0)        // screen currently aligned at tile position
12050   {
12051     int sx, sy;
12052
12053     if (game.centered_player_nr_next == -1)
12054     {
12055       setScreenCenteredToAllPlayers(&sx, &sy);
12056     }
12057     else
12058     {
12059       sx = stored_player[game.centered_player_nr_next].jx;
12060       sy = stored_player[game.centered_player_nr_next].jy;
12061     }
12062
12063     game.centered_player_nr = game.centered_player_nr_next;
12064     game.set_centered_player = FALSE;
12065
12066     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12067     DrawGameDoorValues();
12068   }
12069
12070   // check single step mode (set flag and clear again if any player is active)
12071   game.enter_single_step_mode =
12072     (tape.single_step && tape.recording && !tape.pausing);
12073
12074   for (i = 0; i < MAX_PLAYERS; i++)
12075   {
12076     int actual_player_action = stored_player[i].effective_action;
12077
12078 #if 1
12079     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12080        - rnd_equinox_tetrachloride 048
12081        - rnd_equinox_tetrachloride_ii 096
12082        - rnd_emanuel_schmieg 002
12083        - doctor_sloan_ww 001, 020
12084     */
12085     if (stored_player[i].MovPos == 0)
12086       CheckGravityMovement(&stored_player[i]);
12087 #endif
12088
12089     // overwrite programmed action with tape action
12090     if (stored_player[i].programmed_action)
12091       actual_player_action = stored_player[i].programmed_action;
12092
12093     PlayerActions(&stored_player[i], actual_player_action);
12094
12095     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12096   }
12097
12098   // single step pause mode may already have been toggled by "ScrollPlayer()"
12099   if (game.enter_single_step_mode && !tape.pausing)
12100     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12101
12102   ScrollScreen(NULL, SCROLL_GO_ON);
12103
12104   /* for backwards compatibility, the following code emulates a fixed bug that
12105      occured when pushing elements (causing elements that just made their last
12106      pushing step to already (if possible) make their first falling step in the
12107      same game frame, which is bad); this code is also needed to use the famous
12108      "spring push bug" which is used in older levels and might be wanted to be
12109      used also in newer levels, but in this case the buggy pushing code is only
12110      affecting the "spring" element and no other elements */
12111
12112   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12113   {
12114     for (i = 0; i < MAX_PLAYERS; i++)
12115     {
12116       struct PlayerInfo *player = &stored_player[i];
12117       int x = player->jx;
12118       int y = player->jy;
12119
12120       if (player->active && player->is_pushing && player->is_moving &&
12121           IS_MOVING(x, y) &&
12122           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12123            Tile[x][y] == EL_SPRING))
12124       {
12125         ContinueMoving(x, y);
12126
12127         // continue moving after pushing (this is actually a bug)
12128         if (!IS_MOVING(x, y))
12129           Stop[x][y] = FALSE;
12130       }
12131     }
12132   }
12133
12134   SCAN_PLAYFIELD(x, y)
12135   {
12136     Last[x][y] = Tile[x][y];
12137
12138     ChangeCount[x][y] = 0;
12139     ChangeEvent[x][y] = -1;
12140
12141     // this must be handled before main playfield loop
12142     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12143     {
12144       MovDelay[x][y]--;
12145       if (MovDelay[x][y] <= 0)
12146         RemoveField(x, y);
12147     }
12148
12149     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12150     {
12151       MovDelay[x][y]--;
12152       if (MovDelay[x][y] <= 0)
12153       {
12154         int element = Store[x][y];
12155         int move_direction = MovDir[x][y];
12156         int player_index_bit = Store2[x][y];
12157
12158         Store[x][y] = 0;
12159         Store2[x][y] = 0;
12160
12161         RemoveField(x, y);
12162         TEST_DrawLevelField(x, y);
12163
12164         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12165       }
12166     }
12167
12168 #if DEBUG
12169     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12170     {
12171       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12172             x, y);
12173       Debug("game:playing:GameActions_RND", "This should never happen!");
12174
12175       ChangePage[x][y] = -1;
12176     }
12177 #endif
12178
12179     Stop[x][y] = FALSE;
12180     if (WasJustMoving[x][y] > 0)
12181       WasJustMoving[x][y]--;
12182     if (WasJustFalling[x][y] > 0)
12183       WasJustFalling[x][y]--;
12184     if (CheckCollision[x][y] > 0)
12185       CheckCollision[x][y]--;
12186     if (CheckImpact[x][y] > 0)
12187       CheckImpact[x][y]--;
12188
12189     GfxFrame[x][y]++;
12190
12191     /* reset finished pushing action (not done in ContinueMoving() to allow
12192        continuous pushing animation for elements with zero push delay) */
12193     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12194     {
12195       ResetGfxAnimation(x, y);
12196       TEST_DrawLevelField(x, y);
12197     }
12198
12199 #if DEBUG
12200     if (IS_BLOCKED(x, y))
12201     {
12202       int oldx, oldy;
12203
12204       Blocked2Moving(x, y, &oldx, &oldy);
12205       if (!IS_MOVING(oldx, oldy))
12206       {
12207         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12208         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12209         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12210         Debug("game:playing:GameActions_RND", "This should never happen!");
12211       }
12212     }
12213 #endif
12214   }
12215
12216   if (mouse_action.button)
12217   {
12218     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12219     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12220
12221     x = mouse_action.lx;
12222     y = mouse_action.ly;
12223     element = Tile[x][y];
12224
12225     if (new_button)
12226     {
12227       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12228       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12229                                          ch_button);
12230     }
12231
12232     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12233     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12234                                        ch_button);
12235   }
12236
12237   SCAN_PLAYFIELD(x, y)
12238   {
12239     element = Tile[x][y];
12240     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12241     last_gfx_frame = GfxFrame[x][y];
12242
12243     ResetGfxFrame(x, y);
12244
12245     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12246       DrawLevelGraphicAnimation(x, y, graphic);
12247
12248     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12249         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12250       ResetRandomAnimationValue(x, y);
12251
12252     SetRandomAnimationValue(x, y);
12253
12254     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12255
12256     if (IS_INACTIVE(element))
12257     {
12258       if (IS_ANIMATED(graphic))
12259         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12260
12261       continue;
12262     }
12263
12264     // this may take place after moving, so 'element' may have changed
12265     if (IS_CHANGING(x, y) &&
12266         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12267     {
12268       int page = element_info[element].event_page_nr[CE_DELAY];
12269
12270       HandleElementChange(x, y, page);
12271
12272       element = Tile[x][y];
12273       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12274     }
12275
12276     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12277     {
12278       StartMoving(x, y);
12279
12280       element = Tile[x][y];
12281       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12282
12283       if (IS_ANIMATED(graphic) &&
12284           !IS_MOVING(x, y) &&
12285           !Stop[x][y])
12286         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12287
12288       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12289         TEST_DrawTwinkleOnField(x, y);
12290     }
12291     else if (element == EL_ACID)
12292     {
12293       if (!Stop[x][y])
12294         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12295     }
12296     else if ((element == EL_EXIT_OPEN ||
12297               element == EL_EM_EXIT_OPEN ||
12298               element == EL_SP_EXIT_OPEN ||
12299               element == EL_STEEL_EXIT_OPEN ||
12300               element == EL_EM_STEEL_EXIT_OPEN ||
12301               element == EL_SP_TERMINAL ||
12302               element == EL_SP_TERMINAL_ACTIVE ||
12303               element == EL_EXTRA_TIME ||
12304               element == EL_SHIELD_NORMAL ||
12305               element == EL_SHIELD_DEADLY) &&
12306              IS_ANIMATED(graphic))
12307       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12308     else if (IS_MOVING(x, y))
12309       ContinueMoving(x, y);
12310     else if (IS_ACTIVE_BOMB(element))
12311       CheckDynamite(x, y);
12312     else if (element == EL_AMOEBA_GROWING)
12313       AmoebaGrowing(x, y);
12314     else if (element == EL_AMOEBA_SHRINKING)
12315       AmoebaShrinking(x, y);
12316
12317 #if !USE_NEW_AMOEBA_CODE
12318     else if (IS_AMOEBALIVE(element))
12319       AmoebaReproduce(x, y);
12320 #endif
12321
12322     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12323       Life(x, y);
12324     else if (element == EL_EXIT_CLOSED)
12325       CheckExit(x, y);
12326     else if (element == EL_EM_EXIT_CLOSED)
12327       CheckExitEM(x, y);
12328     else if (element == EL_STEEL_EXIT_CLOSED)
12329       CheckExitSteel(x, y);
12330     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12331       CheckExitSteelEM(x, y);
12332     else if (element == EL_SP_EXIT_CLOSED)
12333       CheckExitSP(x, y);
12334     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12335              element == EL_EXPANDABLE_STEELWALL_GROWING)
12336       MauerWaechst(x, y);
12337     else if (element == EL_EXPANDABLE_WALL ||
12338              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12339              element == EL_EXPANDABLE_WALL_VERTICAL ||
12340              element == EL_EXPANDABLE_WALL_ANY ||
12341              element == EL_BD_EXPANDABLE_WALL)
12342       MauerAbleger(x, y);
12343     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12344              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12345              element == EL_EXPANDABLE_STEELWALL_ANY)
12346       MauerAblegerStahl(x, y);
12347     else if (element == EL_FLAMES)
12348       CheckForDragon(x, y);
12349     else if (element == EL_EXPLOSION)
12350       ; // drawing of correct explosion animation is handled separately
12351     else if (element == EL_ELEMENT_SNAPPING ||
12352              element == EL_DIAGONAL_SHRINKING ||
12353              element == EL_DIAGONAL_GROWING)
12354     {
12355       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12356
12357       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12358     }
12359     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12360       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12361
12362     if (IS_BELT_ACTIVE(element))
12363       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12364
12365     if (game.magic_wall_active)
12366     {
12367       int jx = local_player->jx, jy = local_player->jy;
12368
12369       // play the element sound at the position nearest to the player
12370       if ((element == EL_MAGIC_WALL_FULL ||
12371            element == EL_MAGIC_WALL_ACTIVE ||
12372            element == EL_MAGIC_WALL_EMPTYING ||
12373            element == EL_BD_MAGIC_WALL_FULL ||
12374            element == EL_BD_MAGIC_WALL_ACTIVE ||
12375            element == EL_BD_MAGIC_WALL_EMPTYING ||
12376            element == EL_DC_MAGIC_WALL_FULL ||
12377            element == EL_DC_MAGIC_WALL_ACTIVE ||
12378            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12379           ABS(x - jx) + ABS(y - jy) <
12380           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12381       {
12382         magic_wall_x = x;
12383         magic_wall_y = y;
12384       }
12385     }
12386   }
12387
12388 #if USE_NEW_AMOEBA_CODE
12389   // new experimental amoeba growth stuff
12390   if (!(FrameCounter % 8))
12391   {
12392     static unsigned int random = 1684108901;
12393
12394     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12395     {
12396       x = RND(lev_fieldx);
12397       y = RND(lev_fieldy);
12398       element = Tile[x][y];
12399
12400       if (!IS_PLAYER(x,y) &&
12401           (element == EL_EMPTY ||
12402            CAN_GROW_INTO(element) ||
12403            element == EL_QUICKSAND_EMPTY ||
12404            element == EL_QUICKSAND_FAST_EMPTY ||
12405            element == EL_ACID_SPLASH_LEFT ||
12406            element == EL_ACID_SPLASH_RIGHT))
12407       {
12408         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12409             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12410             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12411             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12412           Tile[x][y] = EL_AMOEBA_DROP;
12413       }
12414
12415       random = random * 129 + 1;
12416     }
12417   }
12418 #endif
12419
12420   game.explosions_delayed = FALSE;
12421
12422   SCAN_PLAYFIELD(x, y)
12423   {
12424     element = Tile[x][y];
12425
12426     if (ExplodeField[x][y])
12427       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12428     else if (element == EL_EXPLOSION)
12429       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12430
12431     ExplodeField[x][y] = EX_TYPE_NONE;
12432   }
12433
12434   game.explosions_delayed = TRUE;
12435
12436   if (game.magic_wall_active)
12437   {
12438     if (!(game.magic_wall_time_left % 4))
12439     {
12440       int element = Tile[magic_wall_x][magic_wall_y];
12441
12442       if (element == EL_BD_MAGIC_WALL_FULL ||
12443           element == EL_BD_MAGIC_WALL_ACTIVE ||
12444           element == EL_BD_MAGIC_WALL_EMPTYING)
12445         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12446       else if (element == EL_DC_MAGIC_WALL_FULL ||
12447                element == EL_DC_MAGIC_WALL_ACTIVE ||
12448                element == EL_DC_MAGIC_WALL_EMPTYING)
12449         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12450       else
12451         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12452     }
12453
12454     if (game.magic_wall_time_left > 0)
12455     {
12456       game.magic_wall_time_left--;
12457
12458       if (!game.magic_wall_time_left)
12459       {
12460         SCAN_PLAYFIELD(x, y)
12461         {
12462           element = Tile[x][y];
12463
12464           if (element == EL_MAGIC_WALL_ACTIVE ||
12465               element == EL_MAGIC_WALL_FULL)
12466           {
12467             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12468             TEST_DrawLevelField(x, y);
12469           }
12470           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12471                    element == EL_BD_MAGIC_WALL_FULL)
12472           {
12473             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12474             TEST_DrawLevelField(x, y);
12475           }
12476           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12477                    element == EL_DC_MAGIC_WALL_FULL)
12478           {
12479             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12480             TEST_DrawLevelField(x, y);
12481           }
12482         }
12483
12484         game.magic_wall_active = FALSE;
12485       }
12486     }
12487   }
12488
12489   if (game.light_time_left > 0)
12490   {
12491     game.light_time_left--;
12492
12493     if (game.light_time_left == 0)
12494       RedrawAllLightSwitchesAndInvisibleElements();
12495   }
12496
12497   if (game.timegate_time_left > 0)
12498   {
12499     game.timegate_time_left--;
12500
12501     if (game.timegate_time_left == 0)
12502       CloseAllOpenTimegates();
12503   }
12504
12505   if (game.lenses_time_left > 0)
12506   {
12507     game.lenses_time_left--;
12508
12509     if (game.lenses_time_left == 0)
12510       RedrawAllInvisibleElementsForLenses();
12511   }
12512
12513   if (game.magnify_time_left > 0)
12514   {
12515     game.magnify_time_left--;
12516
12517     if (game.magnify_time_left == 0)
12518       RedrawAllInvisibleElementsForMagnifier();
12519   }
12520
12521   for (i = 0; i < MAX_PLAYERS; i++)
12522   {
12523     struct PlayerInfo *player = &stored_player[i];
12524
12525     if (SHIELD_ON(player))
12526     {
12527       if (player->shield_deadly_time_left)
12528         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12529       else if (player->shield_normal_time_left)
12530         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12531     }
12532   }
12533
12534 #if USE_DELAYED_GFX_REDRAW
12535   SCAN_PLAYFIELD(x, y)
12536   {
12537     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12538     {
12539       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12540          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12541
12542       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12543         DrawLevelField(x, y);
12544
12545       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12546         DrawLevelFieldCrumbled(x, y);
12547
12548       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12549         DrawLevelFieldCrumbledNeighbours(x, y);
12550
12551       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12552         DrawTwinkleOnField(x, y);
12553     }
12554
12555     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12556   }
12557 #endif
12558
12559   DrawAllPlayers();
12560   PlayAllPlayersSound();
12561
12562   for (i = 0; i < MAX_PLAYERS; i++)
12563   {
12564     struct PlayerInfo *player = &stored_player[i];
12565
12566     if (player->show_envelope != 0 && (!player->active ||
12567                                        player->MovPos == 0))
12568     {
12569       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12570
12571       player->show_envelope = 0;
12572     }
12573   }
12574
12575   // use random number generator in every frame to make it less predictable
12576   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12577     RND(1);
12578
12579   mouse_action_last = mouse_action;
12580 }
12581
12582 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12583 {
12584   int min_x = x, min_y = y, max_x = x, max_y = y;
12585   int scr_fieldx = getScreenFieldSizeX();
12586   int scr_fieldy = getScreenFieldSizeY();
12587   int i;
12588
12589   for (i = 0; i < MAX_PLAYERS; i++)
12590   {
12591     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12592
12593     if (!stored_player[i].active || &stored_player[i] == player)
12594       continue;
12595
12596     min_x = MIN(min_x, jx);
12597     min_y = MIN(min_y, jy);
12598     max_x = MAX(max_x, jx);
12599     max_y = MAX(max_y, jy);
12600   }
12601
12602   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12603 }
12604
12605 static boolean AllPlayersInVisibleScreen(void)
12606 {
12607   int i;
12608
12609   for (i = 0; i < MAX_PLAYERS; i++)
12610   {
12611     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12612
12613     if (!stored_player[i].active)
12614       continue;
12615
12616     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12617       return FALSE;
12618   }
12619
12620   return TRUE;
12621 }
12622
12623 void ScrollLevel(int dx, int dy)
12624 {
12625   int scroll_offset = 2 * TILEX_VAR;
12626   int x, y;
12627
12628   BlitBitmap(drawto_field, drawto_field,
12629              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12630              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12631              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12632              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12633              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12634              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12635
12636   if (dx != 0)
12637   {
12638     x = (dx == 1 ? BX1 : BX2);
12639     for (y = BY1; y <= BY2; y++)
12640       DrawScreenField(x, y);
12641   }
12642
12643   if (dy != 0)
12644   {
12645     y = (dy == 1 ? BY1 : BY2);
12646     for (x = BX1; x <= BX2; x++)
12647       DrawScreenField(x, y);
12648   }
12649
12650   redraw_mask |= REDRAW_FIELD;
12651 }
12652
12653 static boolean canFallDown(struct PlayerInfo *player)
12654 {
12655   int jx = player->jx, jy = player->jy;
12656
12657   return (IN_LEV_FIELD(jx, jy + 1) &&
12658           (IS_FREE(jx, jy + 1) ||
12659            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12660           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12661           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12662 }
12663
12664 static boolean canPassField(int x, int y, int move_dir)
12665 {
12666   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12667   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12668   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12669   int nextx = x + dx;
12670   int nexty = y + dy;
12671   int element = Tile[x][y];
12672
12673   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12674           !CAN_MOVE(element) &&
12675           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12676           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12677           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12678 }
12679
12680 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12681 {
12682   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12683   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12684   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12685   int newx = x + dx;
12686   int newy = y + dy;
12687
12688   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12689           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12690           (IS_DIGGABLE(Tile[newx][newy]) ||
12691            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12692            canPassField(newx, newy, move_dir)));
12693 }
12694
12695 static void CheckGravityMovement(struct PlayerInfo *player)
12696 {
12697   if (player->gravity && !player->programmed_action)
12698   {
12699     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12700     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12701     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12702     int jx = player->jx, jy = player->jy;
12703     boolean player_is_moving_to_valid_field =
12704       (!player_is_snapping &&
12705        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12706         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12707     boolean player_can_fall_down = canFallDown(player);
12708
12709     if (player_can_fall_down &&
12710         !player_is_moving_to_valid_field)
12711       player->programmed_action = MV_DOWN;
12712   }
12713 }
12714
12715 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12716 {
12717   return CheckGravityMovement(player);
12718
12719   if (player->gravity && !player->programmed_action)
12720   {
12721     int jx = player->jx, jy = player->jy;
12722     boolean field_under_player_is_free =
12723       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12724     boolean player_is_standing_on_valid_field =
12725       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12726        (IS_WALKABLE(Tile[jx][jy]) &&
12727         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12728
12729     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12730       player->programmed_action = MV_DOWN;
12731   }
12732 }
12733
12734 /*
12735   MovePlayerOneStep()
12736   -----------------------------------------------------------------------------
12737   dx, dy:               direction (non-diagonal) to try to move the player to
12738   real_dx, real_dy:     direction as read from input device (can be diagonal)
12739 */
12740
12741 boolean MovePlayerOneStep(struct PlayerInfo *player,
12742                           int dx, int dy, int real_dx, int real_dy)
12743 {
12744   int jx = player->jx, jy = player->jy;
12745   int new_jx = jx + dx, new_jy = jy + dy;
12746   int can_move;
12747   boolean player_can_move = !player->cannot_move;
12748
12749   if (!player->active || (!dx && !dy))
12750     return MP_NO_ACTION;
12751
12752   player->MovDir = (dx < 0 ? MV_LEFT :
12753                     dx > 0 ? MV_RIGHT :
12754                     dy < 0 ? MV_UP :
12755                     dy > 0 ? MV_DOWN :  MV_NONE);
12756
12757   if (!IN_LEV_FIELD(new_jx, new_jy))
12758     return MP_NO_ACTION;
12759
12760   if (!player_can_move)
12761   {
12762     if (player->MovPos == 0)
12763     {
12764       player->is_moving = FALSE;
12765       player->is_digging = FALSE;
12766       player->is_collecting = FALSE;
12767       player->is_snapping = FALSE;
12768       player->is_pushing = FALSE;
12769     }
12770   }
12771
12772   if (!network.enabled && game.centered_player_nr == -1 &&
12773       !AllPlayersInSight(player, new_jx, new_jy))
12774     return MP_NO_ACTION;
12775
12776   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12777   if (can_move != MP_MOVING)
12778     return can_move;
12779
12780   // check if DigField() has caused relocation of the player
12781   if (player->jx != jx || player->jy != jy)
12782     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12783
12784   StorePlayer[jx][jy] = 0;
12785   player->last_jx = jx;
12786   player->last_jy = jy;
12787   player->jx = new_jx;
12788   player->jy = new_jy;
12789   StorePlayer[new_jx][new_jy] = player->element_nr;
12790
12791   if (player->move_delay_value_next != -1)
12792   {
12793     player->move_delay_value = player->move_delay_value_next;
12794     player->move_delay_value_next = -1;
12795   }
12796
12797   player->MovPos =
12798     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12799
12800   player->step_counter++;
12801
12802   PlayerVisit[jx][jy] = FrameCounter;
12803
12804   player->is_moving = TRUE;
12805
12806 #if 1
12807   // should better be called in MovePlayer(), but this breaks some tapes
12808   ScrollPlayer(player, SCROLL_INIT);
12809 #endif
12810
12811   return MP_MOVING;
12812 }
12813
12814 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12815 {
12816   int jx = player->jx, jy = player->jy;
12817   int old_jx = jx, old_jy = jy;
12818   int moved = MP_NO_ACTION;
12819
12820   if (!player->active)
12821     return FALSE;
12822
12823   if (!dx && !dy)
12824   {
12825     if (player->MovPos == 0)
12826     {
12827       player->is_moving = FALSE;
12828       player->is_digging = FALSE;
12829       player->is_collecting = FALSE;
12830       player->is_snapping = FALSE;
12831       player->is_pushing = FALSE;
12832     }
12833
12834     return FALSE;
12835   }
12836
12837   if (player->move_delay > 0)
12838     return FALSE;
12839
12840   player->move_delay = -1;              // set to "uninitialized" value
12841
12842   // store if player is automatically moved to next field
12843   player->is_auto_moving = (player->programmed_action != MV_NONE);
12844
12845   // remove the last programmed player action
12846   player->programmed_action = 0;
12847
12848   if (player->MovPos)
12849   {
12850     // should only happen if pre-1.2 tape recordings are played
12851     // this is only for backward compatibility
12852
12853     int original_move_delay_value = player->move_delay_value;
12854
12855 #if DEBUG
12856     Debug("game:playing:MovePlayer",
12857           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12858           tape.counter);
12859 #endif
12860
12861     // scroll remaining steps with finest movement resolution
12862     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12863
12864     while (player->MovPos)
12865     {
12866       ScrollPlayer(player, SCROLL_GO_ON);
12867       ScrollScreen(NULL, SCROLL_GO_ON);
12868
12869       AdvanceFrameAndPlayerCounters(player->index_nr);
12870
12871       DrawAllPlayers();
12872       BackToFront_WithFrameDelay(0);
12873     }
12874
12875     player->move_delay_value = original_move_delay_value;
12876   }
12877
12878   player->is_active = FALSE;
12879
12880   if (player->last_move_dir & MV_HORIZONTAL)
12881   {
12882     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12883       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12884   }
12885   else
12886   {
12887     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12888       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12889   }
12890
12891   if (!moved && !player->is_active)
12892   {
12893     player->is_moving = FALSE;
12894     player->is_digging = FALSE;
12895     player->is_collecting = FALSE;
12896     player->is_snapping = FALSE;
12897     player->is_pushing = FALSE;
12898   }
12899
12900   jx = player->jx;
12901   jy = player->jy;
12902
12903   if (moved & MP_MOVING && !ScreenMovPos &&
12904       (player->index_nr == game.centered_player_nr ||
12905        game.centered_player_nr == -1))
12906   {
12907     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12908
12909     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12910     {
12911       // actual player has left the screen -- scroll in that direction
12912       if (jx != old_jx)         // player has moved horizontally
12913         scroll_x += (jx - old_jx);
12914       else                      // player has moved vertically
12915         scroll_y += (jy - old_jy);
12916     }
12917     else
12918     {
12919       int offset_raw = game.scroll_delay_value;
12920
12921       if (jx != old_jx)         // player has moved horizontally
12922       {
12923         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12924         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12925         int new_scroll_x = jx - MIDPOSX + offset_x;
12926
12927         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12928             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12929           scroll_x = new_scroll_x;
12930
12931         // don't scroll over playfield boundaries
12932         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12933
12934         // don't scroll more than one field at a time
12935         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12936
12937         // don't scroll against the player's moving direction
12938         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12939             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12940           scroll_x = old_scroll_x;
12941       }
12942       else                      // player has moved vertically
12943       {
12944         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12945         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12946         int new_scroll_y = jy - MIDPOSY + offset_y;
12947
12948         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12949             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12950           scroll_y = new_scroll_y;
12951
12952         // don't scroll over playfield boundaries
12953         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12954
12955         // don't scroll more than one field at a time
12956         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12957
12958         // don't scroll against the player's moving direction
12959         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12960             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12961           scroll_y = old_scroll_y;
12962       }
12963     }
12964
12965     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12966     {
12967       if (!network.enabled && game.centered_player_nr == -1 &&
12968           !AllPlayersInVisibleScreen())
12969       {
12970         scroll_x = old_scroll_x;
12971         scroll_y = old_scroll_y;
12972       }
12973       else
12974       {
12975         ScrollScreen(player, SCROLL_INIT);
12976         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12977       }
12978     }
12979   }
12980
12981   player->StepFrame = 0;
12982
12983   if (moved & MP_MOVING)
12984   {
12985     if (old_jx != jx && old_jy == jy)
12986       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12987     else if (old_jx == jx && old_jy != jy)
12988       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12989
12990     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12991
12992     player->last_move_dir = player->MovDir;
12993     player->is_moving = TRUE;
12994     player->is_snapping = FALSE;
12995     player->is_switching = FALSE;
12996     player->is_dropping = FALSE;
12997     player->is_dropping_pressed = FALSE;
12998     player->drop_pressed_delay = 0;
12999
13000 #if 0
13001     // should better be called here than above, but this breaks some tapes
13002     ScrollPlayer(player, SCROLL_INIT);
13003 #endif
13004   }
13005   else
13006   {
13007     CheckGravityMovementWhenNotMoving(player);
13008
13009     player->is_moving = FALSE;
13010
13011     /* at this point, the player is allowed to move, but cannot move right now
13012        (e.g. because of something blocking the way) -- ensure that the player
13013        is also allowed to move in the next frame (in old versions before 3.1.1,
13014        the player was forced to wait again for eight frames before next try) */
13015
13016     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13017       player->move_delay = 0;   // allow direct movement in the next frame
13018   }
13019
13020   if (player->move_delay == -1)         // not yet initialized by DigField()
13021     player->move_delay = player->move_delay_value;
13022
13023   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13024   {
13025     TestIfPlayerTouchesBadThing(jx, jy);
13026     TestIfPlayerTouchesCustomElement(jx, jy);
13027   }
13028
13029   if (!player->active)
13030     RemovePlayer(player);
13031
13032   return moved;
13033 }
13034
13035 void ScrollPlayer(struct PlayerInfo *player, int mode)
13036 {
13037   int jx = player->jx, jy = player->jy;
13038   int last_jx = player->last_jx, last_jy = player->last_jy;
13039   int move_stepsize = TILEX / player->move_delay_value;
13040
13041   if (!player->active)
13042     return;
13043
13044   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13045     return;
13046
13047   if (mode == SCROLL_INIT)
13048   {
13049     player->actual_frame_counter = FrameCounter;
13050     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13051
13052     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13053         Tile[last_jx][last_jy] == EL_EMPTY)
13054     {
13055       int last_field_block_delay = 0;   // start with no blocking at all
13056       int block_delay_adjustment = player->block_delay_adjustment;
13057
13058       // if player blocks last field, add delay for exactly one move
13059       if (player->block_last_field)
13060       {
13061         last_field_block_delay += player->move_delay_value;
13062
13063         // when blocking enabled, prevent moving up despite gravity
13064         if (player->gravity && player->MovDir == MV_UP)
13065           block_delay_adjustment = -1;
13066       }
13067
13068       // add block delay adjustment (also possible when not blocking)
13069       last_field_block_delay += block_delay_adjustment;
13070
13071       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13072       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13073     }
13074
13075     if (player->MovPos != 0)    // player has not yet reached destination
13076       return;
13077   }
13078   else if (!FrameReached(&player->actual_frame_counter, 1))
13079     return;
13080
13081   if (player->MovPos != 0)
13082   {
13083     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13084     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13085
13086     // before DrawPlayer() to draw correct player graphic for this case
13087     if (player->MovPos == 0)
13088       CheckGravityMovement(player);
13089   }
13090
13091   if (player->MovPos == 0)      // player reached destination field
13092   {
13093     if (player->move_delay_reset_counter > 0)
13094     {
13095       player->move_delay_reset_counter--;
13096
13097       if (player->move_delay_reset_counter == 0)
13098       {
13099         // continue with normal speed after quickly moving through gate
13100         HALVE_PLAYER_SPEED(player);
13101
13102         // be able to make the next move without delay
13103         player->move_delay = 0;
13104       }
13105     }
13106
13107     player->last_jx = jx;
13108     player->last_jy = jy;
13109
13110     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13111         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13112         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13113         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13114         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13115         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13116         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13117         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13118     {
13119       ExitPlayer(player);
13120
13121       if (game.players_still_needed == 0 &&
13122           (game.friends_still_needed == 0 ||
13123            IS_SP_ELEMENT(Tile[jx][jy])))
13124         LevelSolved();
13125     }
13126
13127     // this breaks one level: "machine", level 000
13128     {
13129       int move_direction = player->MovDir;
13130       int enter_side = MV_DIR_OPPOSITE(move_direction);
13131       int leave_side = move_direction;
13132       int old_jx = last_jx;
13133       int old_jy = last_jy;
13134       int old_element = Tile[old_jx][old_jy];
13135       int new_element = Tile[jx][jy];
13136
13137       if (IS_CUSTOM_ELEMENT(old_element))
13138         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13139                                    CE_LEFT_BY_PLAYER,
13140                                    player->index_bit, leave_side);
13141
13142       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13143                                           CE_PLAYER_LEAVES_X,
13144                                           player->index_bit, leave_side);
13145
13146       if (IS_CUSTOM_ELEMENT(new_element))
13147         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13148                                    player->index_bit, enter_side);
13149
13150       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13151                                           CE_PLAYER_ENTERS_X,
13152                                           player->index_bit, enter_side);
13153
13154       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13155                                         CE_MOVE_OF_X, move_direction);
13156     }
13157
13158     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13159     {
13160       TestIfPlayerTouchesBadThing(jx, jy);
13161       TestIfPlayerTouchesCustomElement(jx, jy);
13162
13163       /* needed because pushed element has not yet reached its destination,
13164          so it would trigger a change event at its previous field location */
13165       if (!player->is_pushing)
13166         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13167
13168       if (level.finish_dig_collect &&
13169           (player->is_digging || player->is_collecting))
13170       {
13171         int last_element = player->last_removed_element;
13172         int move_direction = player->MovDir;
13173         int enter_side = MV_DIR_OPPOSITE(move_direction);
13174         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13175                             CE_PLAYER_COLLECTS_X);
13176
13177         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13178                                             player->index_bit, enter_side);
13179
13180         player->last_removed_element = EL_UNDEFINED;
13181       }
13182
13183       if (!player->active)
13184         RemovePlayer(player);
13185     }
13186
13187     if (level.use_step_counter)
13188     {
13189       int i;
13190
13191       TimePlayed++;
13192
13193       if (TimeLeft > 0)
13194       {
13195         TimeLeft--;
13196
13197         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13198           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13199
13200         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13201
13202         DisplayGameControlValues();
13203
13204         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13205           for (i = 0; i < MAX_PLAYERS; i++)
13206             KillPlayer(&stored_player[i]);
13207       }
13208       else if (game.no_time_limit && !game.all_players_gone)
13209       {
13210         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13211
13212         DisplayGameControlValues();
13213       }
13214     }
13215
13216     if (tape.single_step && tape.recording && !tape.pausing &&
13217         !player->programmed_action)
13218       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13219
13220     if (!player->programmed_action)
13221       CheckSaveEngineSnapshot(player);
13222   }
13223 }
13224
13225 void ScrollScreen(struct PlayerInfo *player, int mode)
13226 {
13227   static unsigned int screen_frame_counter = 0;
13228
13229   if (mode == SCROLL_INIT)
13230   {
13231     // set scrolling step size according to actual player's moving speed
13232     ScrollStepSize = TILEX / player->move_delay_value;
13233
13234     screen_frame_counter = FrameCounter;
13235     ScreenMovDir = player->MovDir;
13236     ScreenMovPos = player->MovPos;
13237     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13238     return;
13239   }
13240   else if (!FrameReached(&screen_frame_counter, 1))
13241     return;
13242
13243   if (ScreenMovPos)
13244   {
13245     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13246     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13247     redraw_mask |= REDRAW_FIELD;
13248   }
13249   else
13250     ScreenMovDir = MV_NONE;
13251 }
13252
13253 void TestIfPlayerTouchesCustomElement(int x, int y)
13254 {
13255   static int xy[4][2] =
13256   {
13257     { 0, -1 },
13258     { -1, 0 },
13259     { +1, 0 },
13260     { 0, +1 }
13261   };
13262   static int trigger_sides[4][2] =
13263   {
13264     // center side       border side
13265     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13266     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13267     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13268     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13269   };
13270   static int touch_dir[4] =
13271   {
13272     MV_LEFT | MV_RIGHT,
13273     MV_UP   | MV_DOWN,
13274     MV_UP   | MV_DOWN,
13275     MV_LEFT | MV_RIGHT
13276   };
13277   int center_element = Tile[x][y];      // should always be non-moving!
13278   int i;
13279
13280   for (i = 0; i < NUM_DIRECTIONS; i++)
13281   {
13282     int xx = x + xy[i][0];
13283     int yy = y + xy[i][1];
13284     int center_side = trigger_sides[i][0];
13285     int border_side = trigger_sides[i][1];
13286     int border_element;
13287
13288     if (!IN_LEV_FIELD(xx, yy))
13289       continue;
13290
13291     if (IS_PLAYER(x, y))                // player found at center element
13292     {
13293       struct PlayerInfo *player = PLAYERINFO(x, y);
13294
13295       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13296         border_element = Tile[xx][yy];          // may be moving!
13297       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13298         border_element = Tile[xx][yy];
13299       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13300         border_element = MovingOrBlocked2Element(xx, yy);
13301       else
13302         continue;               // center and border element do not touch
13303
13304       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13305                                  player->index_bit, border_side);
13306       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13307                                           CE_PLAYER_TOUCHES_X,
13308                                           player->index_bit, border_side);
13309
13310       {
13311         /* use player element that is initially defined in the level playfield,
13312            not the player element that corresponds to the runtime player number
13313            (example: a level that contains EL_PLAYER_3 as the only player would
13314            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13315         int player_element = PLAYERINFO(x, y)->initial_element;
13316
13317         CheckElementChangeBySide(xx, yy, border_element, player_element,
13318                                  CE_TOUCHING_X, border_side);
13319       }
13320     }
13321     else if (IS_PLAYER(xx, yy))         // player found at border element
13322     {
13323       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13324
13325       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13326       {
13327         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13328           continue;             // center and border element do not touch
13329       }
13330
13331       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13332                                  player->index_bit, center_side);
13333       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13334                                           CE_PLAYER_TOUCHES_X,
13335                                           player->index_bit, center_side);
13336
13337       {
13338         /* use player element that is initially defined in the level playfield,
13339            not the player element that corresponds to the runtime player number
13340            (example: a level that contains EL_PLAYER_3 as the only player would
13341            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13342         int player_element = PLAYERINFO(xx, yy)->initial_element;
13343
13344         CheckElementChangeBySide(x, y, center_element, player_element,
13345                                  CE_TOUCHING_X, center_side);
13346       }
13347
13348       break;
13349     }
13350   }
13351 }
13352
13353 void TestIfElementTouchesCustomElement(int x, int y)
13354 {
13355   static int xy[4][2] =
13356   {
13357     { 0, -1 },
13358     { -1, 0 },
13359     { +1, 0 },
13360     { 0, +1 }
13361   };
13362   static int trigger_sides[4][2] =
13363   {
13364     // center side      border side
13365     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13366     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13367     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13368     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13369   };
13370   static int touch_dir[4] =
13371   {
13372     MV_LEFT | MV_RIGHT,
13373     MV_UP   | MV_DOWN,
13374     MV_UP   | MV_DOWN,
13375     MV_LEFT | MV_RIGHT
13376   };
13377   boolean change_center_element = FALSE;
13378   int center_element = Tile[x][y];      // should always be non-moving!
13379   int border_element_old[NUM_DIRECTIONS];
13380   int i;
13381
13382   for (i = 0; i < NUM_DIRECTIONS; i++)
13383   {
13384     int xx = x + xy[i][0];
13385     int yy = y + xy[i][1];
13386     int border_element;
13387
13388     border_element_old[i] = -1;
13389
13390     if (!IN_LEV_FIELD(xx, yy))
13391       continue;
13392
13393     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13394       border_element = Tile[xx][yy];    // may be moving!
13395     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13396       border_element = Tile[xx][yy];
13397     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13398       border_element = MovingOrBlocked2Element(xx, yy);
13399     else
13400       continue;                 // center and border element do not touch
13401
13402     border_element_old[i] = border_element;
13403   }
13404
13405   for (i = 0; i < NUM_DIRECTIONS; i++)
13406   {
13407     int xx = x + xy[i][0];
13408     int yy = y + xy[i][1];
13409     int center_side = trigger_sides[i][0];
13410     int border_element = border_element_old[i];
13411
13412     if (border_element == -1)
13413       continue;
13414
13415     // check for change of border element
13416     CheckElementChangeBySide(xx, yy, border_element, center_element,
13417                              CE_TOUCHING_X, center_side);
13418
13419     // (center element cannot be player, so we dont have to check this here)
13420   }
13421
13422   for (i = 0; i < NUM_DIRECTIONS; i++)
13423   {
13424     int xx = x + xy[i][0];
13425     int yy = y + xy[i][1];
13426     int border_side = trigger_sides[i][1];
13427     int border_element = border_element_old[i];
13428
13429     if (border_element == -1)
13430       continue;
13431
13432     // check for change of center element (but change it only once)
13433     if (!change_center_element)
13434       change_center_element =
13435         CheckElementChangeBySide(x, y, center_element, border_element,
13436                                  CE_TOUCHING_X, border_side);
13437
13438     if (IS_PLAYER(xx, yy))
13439     {
13440       /* use player element that is initially defined in the level playfield,
13441          not the player element that corresponds to the runtime player number
13442          (example: a level that contains EL_PLAYER_3 as the only player would
13443          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13444       int player_element = PLAYERINFO(xx, yy)->initial_element;
13445
13446       CheckElementChangeBySide(x, y, center_element, player_element,
13447                                CE_TOUCHING_X, border_side);
13448     }
13449   }
13450 }
13451
13452 void TestIfElementHitsCustomElement(int x, int y, int direction)
13453 {
13454   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13455   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13456   int hitx = x + dx, hity = y + dy;
13457   int hitting_element = Tile[x][y];
13458   int touched_element;
13459
13460   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13461     return;
13462
13463   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13464                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13465
13466   if (IN_LEV_FIELD(hitx, hity))
13467   {
13468     int opposite_direction = MV_DIR_OPPOSITE(direction);
13469     int hitting_side = direction;
13470     int touched_side = opposite_direction;
13471     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13472                           MovDir[hitx][hity] != direction ||
13473                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13474
13475     object_hit = TRUE;
13476
13477     if (object_hit)
13478     {
13479       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13480                                CE_HITTING_X, touched_side);
13481
13482       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13483                                CE_HIT_BY_X, hitting_side);
13484
13485       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13486                                CE_HIT_BY_SOMETHING, opposite_direction);
13487
13488       if (IS_PLAYER(hitx, hity))
13489       {
13490         /* use player element that is initially defined in the level playfield,
13491            not the player element that corresponds to the runtime player number
13492            (example: a level that contains EL_PLAYER_3 as the only player would
13493            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13494         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13495
13496         CheckElementChangeBySide(x, y, hitting_element, player_element,
13497                                  CE_HITTING_X, touched_side);
13498       }
13499     }
13500   }
13501
13502   // "hitting something" is also true when hitting the playfield border
13503   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13504                            CE_HITTING_SOMETHING, direction);
13505 }
13506
13507 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13508 {
13509   int i, kill_x = -1, kill_y = -1;
13510
13511   int bad_element = -1;
13512   static int test_xy[4][2] =
13513   {
13514     { 0, -1 },
13515     { -1, 0 },
13516     { +1, 0 },
13517     { 0, +1 }
13518   };
13519   static int test_dir[4] =
13520   {
13521     MV_UP,
13522     MV_LEFT,
13523     MV_RIGHT,
13524     MV_DOWN
13525   };
13526
13527   for (i = 0; i < NUM_DIRECTIONS; i++)
13528   {
13529     int test_x, test_y, test_move_dir, test_element;
13530
13531     test_x = good_x + test_xy[i][0];
13532     test_y = good_y + test_xy[i][1];
13533
13534     if (!IN_LEV_FIELD(test_x, test_y))
13535       continue;
13536
13537     test_move_dir =
13538       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13539
13540     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13541
13542     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13543        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13544     */
13545     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13546         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13547     {
13548       kill_x = test_x;
13549       kill_y = test_y;
13550       bad_element = test_element;
13551
13552       break;
13553     }
13554   }
13555
13556   if (kill_x != -1 || kill_y != -1)
13557   {
13558     if (IS_PLAYER(good_x, good_y))
13559     {
13560       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13561
13562       if (player->shield_deadly_time_left > 0 &&
13563           !IS_INDESTRUCTIBLE(bad_element))
13564         Bang(kill_x, kill_y);
13565       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13566         KillPlayer(player);
13567     }
13568     else
13569       Bang(good_x, good_y);
13570   }
13571 }
13572
13573 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13574 {
13575   int i, kill_x = -1, kill_y = -1;
13576   int bad_element = Tile[bad_x][bad_y];
13577   static int test_xy[4][2] =
13578   {
13579     { 0, -1 },
13580     { -1, 0 },
13581     { +1, 0 },
13582     { 0, +1 }
13583   };
13584   static int touch_dir[4] =
13585   {
13586     MV_LEFT | MV_RIGHT,
13587     MV_UP   | MV_DOWN,
13588     MV_UP   | MV_DOWN,
13589     MV_LEFT | MV_RIGHT
13590   };
13591   static int test_dir[4] =
13592   {
13593     MV_UP,
13594     MV_LEFT,
13595     MV_RIGHT,
13596     MV_DOWN
13597   };
13598
13599   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13600     return;
13601
13602   for (i = 0; i < NUM_DIRECTIONS; i++)
13603   {
13604     int test_x, test_y, test_move_dir, test_element;
13605
13606     test_x = bad_x + test_xy[i][0];
13607     test_y = bad_y + test_xy[i][1];
13608
13609     if (!IN_LEV_FIELD(test_x, test_y))
13610       continue;
13611
13612     test_move_dir =
13613       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13614
13615     test_element = Tile[test_x][test_y];
13616
13617     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13618        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13619     */
13620     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13621         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13622     {
13623       // good thing is player or penguin that does not move away
13624       if (IS_PLAYER(test_x, test_y))
13625       {
13626         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13627
13628         if (bad_element == EL_ROBOT && player->is_moving)
13629           continue;     // robot does not kill player if he is moving
13630
13631         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13632         {
13633           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13634             continue;           // center and border element do not touch
13635         }
13636
13637         kill_x = test_x;
13638         kill_y = test_y;
13639
13640         break;
13641       }
13642       else if (test_element == EL_PENGUIN)
13643       {
13644         kill_x = test_x;
13645         kill_y = test_y;
13646
13647         break;
13648       }
13649     }
13650   }
13651
13652   if (kill_x != -1 || kill_y != -1)
13653   {
13654     if (IS_PLAYER(kill_x, kill_y))
13655     {
13656       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13657
13658       if (player->shield_deadly_time_left > 0 &&
13659           !IS_INDESTRUCTIBLE(bad_element))
13660         Bang(bad_x, bad_y);
13661       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13662         KillPlayer(player);
13663     }
13664     else
13665       Bang(kill_x, kill_y);
13666   }
13667 }
13668
13669 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13670 {
13671   int bad_element = Tile[bad_x][bad_y];
13672   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13673   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13674   int test_x = bad_x + dx, test_y = bad_y + dy;
13675   int test_move_dir, test_element;
13676   int kill_x = -1, kill_y = -1;
13677
13678   if (!IN_LEV_FIELD(test_x, test_y))
13679     return;
13680
13681   test_move_dir =
13682     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13683
13684   test_element = Tile[test_x][test_y];
13685
13686   if (test_move_dir != bad_move_dir)
13687   {
13688     // good thing can be player or penguin that does not move away
13689     if (IS_PLAYER(test_x, test_y))
13690     {
13691       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13692
13693       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13694          player as being hit when he is moving towards the bad thing, because
13695          the "get hit by" condition would be lost after the player stops) */
13696       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13697         return;         // player moves away from bad thing
13698
13699       kill_x = test_x;
13700       kill_y = test_y;
13701     }
13702     else if (test_element == EL_PENGUIN)
13703     {
13704       kill_x = test_x;
13705       kill_y = test_y;
13706     }
13707   }
13708
13709   if (kill_x != -1 || kill_y != -1)
13710   {
13711     if (IS_PLAYER(kill_x, kill_y))
13712     {
13713       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13714
13715       if (player->shield_deadly_time_left > 0 &&
13716           !IS_INDESTRUCTIBLE(bad_element))
13717         Bang(bad_x, bad_y);
13718       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13719         KillPlayer(player);
13720     }
13721     else
13722       Bang(kill_x, kill_y);
13723   }
13724 }
13725
13726 void TestIfPlayerTouchesBadThing(int x, int y)
13727 {
13728   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13729 }
13730
13731 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13732 {
13733   TestIfGoodThingHitsBadThing(x, y, move_dir);
13734 }
13735
13736 void TestIfBadThingTouchesPlayer(int x, int y)
13737 {
13738   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13739 }
13740
13741 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13742 {
13743   TestIfBadThingHitsGoodThing(x, y, move_dir);
13744 }
13745
13746 void TestIfFriendTouchesBadThing(int x, int y)
13747 {
13748   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13749 }
13750
13751 void TestIfBadThingTouchesFriend(int x, int y)
13752 {
13753   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13754 }
13755
13756 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13757 {
13758   int i, kill_x = bad_x, kill_y = bad_y;
13759   static int xy[4][2] =
13760   {
13761     { 0, -1 },
13762     { -1, 0 },
13763     { +1, 0 },
13764     { 0, +1 }
13765   };
13766
13767   for (i = 0; i < NUM_DIRECTIONS; i++)
13768   {
13769     int x, y, element;
13770
13771     x = bad_x + xy[i][0];
13772     y = bad_y + xy[i][1];
13773     if (!IN_LEV_FIELD(x, y))
13774       continue;
13775
13776     element = Tile[x][y];
13777     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13778         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13779     {
13780       kill_x = x;
13781       kill_y = y;
13782       break;
13783     }
13784   }
13785
13786   if (kill_x != bad_x || kill_y != bad_y)
13787     Bang(bad_x, bad_y);
13788 }
13789
13790 void KillPlayer(struct PlayerInfo *player)
13791 {
13792   int jx = player->jx, jy = player->jy;
13793
13794   if (!player->active)
13795     return;
13796
13797 #if 0
13798   Debug("game:playing:KillPlayer",
13799         "0: killed == %d, active == %d, reanimated == %d",
13800         player->killed, player->active, player->reanimated);
13801 #endif
13802
13803   /* the following code was introduced to prevent an infinite loop when calling
13804      -> Bang()
13805      -> CheckTriggeredElementChangeExt()
13806      -> ExecuteCustomElementAction()
13807      -> KillPlayer()
13808      -> (infinitely repeating the above sequence of function calls)
13809      which occurs when killing the player while having a CE with the setting
13810      "kill player X when explosion of <player X>"; the solution using a new
13811      field "player->killed" was chosen for backwards compatibility, although
13812      clever use of the fields "player->active" etc. would probably also work */
13813 #if 1
13814   if (player->killed)
13815     return;
13816 #endif
13817
13818   player->killed = TRUE;
13819
13820   // remove accessible field at the player's position
13821   Tile[jx][jy] = EL_EMPTY;
13822
13823   // deactivate shield (else Bang()/Explode() would not work right)
13824   player->shield_normal_time_left = 0;
13825   player->shield_deadly_time_left = 0;
13826
13827 #if 0
13828   Debug("game:playing:KillPlayer",
13829         "1: killed == %d, active == %d, reanimated == %d",
13830         player->killed, player->active, player->reanimated);
13831 #endif
13832
13833   Bang(jx, jy);
13834
13835 #if 0
13836   Debug("game:playing:KillPlayer",
13837         "2: killed == %d, active == %d, reanimated == %d",
13838         player->killed, player->active, player->reanimated);
13839 #endif
13840
13841   if (player->reanimated)       // killed player may have been reanimated
13842     player->killed = player->reanimated = FALSE;
13843   else
13844     BuryPlayer(player);
13845 }
13846
13847 static void KillPlayerUnlessEnemyProtected(int x, int y)
13848 {
13849   if (!PLAYER_ENEMY_PROTECTED(x, y))
13850     KillPlayer(PLAYERINFO(x, y));
13851 }
13852
13853 static void KillPlayerUnlessExplosionProtected(int x, int y)
13854 {
13855   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13856     KillPlayer(PLAYERINFO(x, y));
13857 }
13858
13859 void BuryPlayer(struct PlayerInfo *player)
13860 {
13861   int jx = player->jx, jy = player->jy;
13862
13863   if (!player->active)
13864     return;
13865
13866   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13867   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13868
13869   RemovePlayer(player);
13870
13871   player->buried = TRUE;
13872
13873   if (game.all_players_gone)
13874     game.GameOver = TRUE;
13875 }
13876
13877 void RemovePlayer(struct PlayerInfo *player)
13878 {
13879   int jx = player->jx, jy = player->jy;
13880   int i, found = FALSE;
13881
13882   player->present = FALSE;
13883   player->active = FALSE;
13884
13885   // required for some CE actions (even if the player is not active anymore)
13886   player->MovPos = 0;
13887
13888   if (!ExplodeField[jx][jy])
13889     StorePlayer[jx][jy] = 0;
13890
13891   if (player->is_moving)
13892     TEST_DrawLevelField(player->last_jx, player->last_jy);
13893
13894   for (i = 0; i < MAX_PLAYERS; i++)
13895     if (stored_player[i].active)
13896       found = TRUE;
13897
13898   if (!found)
13899   {
13900     game.all_players_gone = TRUE;
13901     game.GameOver = TRUE;
13902   }
13903
13904   game.exit_x = game.robot_wheel_x = jx;
13905   game.exit_y = game.robot_wheel_y = jy;
13906 }
13907
13908 void ExitPlayer(struct PlayerInfo *player)
13909 {
13910   DrawPlayer(player);   // needed here only to cleanup last field
13911   RemovePlayer(player);
13912
13913   if (game.players_still_needed > 0)
13914     game.players_still_needed--;
13915 }
13916
13917 static void SetFieldForSnapping(int x, int y, int element, int direction,
13918                                 int player_index_bit)
13919 {
13920   struct ElementInfo *ei = &element_info[element];
13921   int direction_bit = MV_DIR_TO_BIT(direction);
13922   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13923   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13924                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13925
13926   Tile[x][y] = EL_ELEMENT_SNAPPING;
13927   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13928   MovDir[x][y] = direction;
13929   Store[x][y] = element;
13930   Store2[x][y] = player_index_bit;
13931
13932   ResetGfxAnimation(x, y);
13933
13934   GfxElement[x][y] = element;
13935   GfxAction[x][y] = action;
13936   GfxDir[x][y] = direction;
13937   GfxFrame[x][y] = -1;
13938 }
13939
13940 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13941                                    int player_index_bit)
13942 {
13943   TestIfElementTouchesCustomElement(x, y);      // for empty space
13944
13945   if (level.finish_dig_collect)
13946   {
13947     int dig_side = MV_DIR_OPPOSITE(direction);
13948
13949     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13950                                         player_index_bit, dig_side);
13951   }
13952 }
13953
13954 /*
13955   =============================================================================
13956   checkDiagonalPushing()
13957   -----------------------------------------------------------------------------
13958   check if diagonal input device direction results in pushing of object
13959   (by checking if the alternative direction is walkable, diggable, ...)
13960   =============================================================================
13961 */
13962
13963 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13964                                     int x, int y, int real_dx, int real_dy)
13965 {
13966   int jx, jy, dx, dy, xx, yy;
13967
13968   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13969     return TRUE;
13970
13971   // diagonal direction: check alternative direction
13972   jx = player->jx;
13973   jy = player->jy;
13974   dx = x - jx;
13975   dy = y - jy;
13976   xx = jx + (dx == 0 ? real_dx : 0);
13977   yy = jy + (dy == 0 ? real_dy : 0);
13978
13979   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13980 }
13981
13982 /*
13983   =============================================================================
13984   DigField()
13985   -----------------------------------------------------------------------------
13986   x, y:                 field next to player (non-diagonal) to try to dig to
13987   real_dx, real_dy:     direction as read from input device (can be diagonal)
13988   =============================================================================
13989 */
13990
13991 static int DigField(struct PlayerInfo *player,
13992                     int oldx, int oldy, int x, int y,
13993                     int real_dx, int real_dy, int mode)
13994 {
13995   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13996   boolean player_was_pushing = player->is_pushing;
13997   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13998   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13999   int jx = oldx, jy = oldy;
14000   int dx = x - jx, dy = y - jy;
14001   int nextx = x + dx, nexty = y + dy;
14002   int move_direction = (dx == -1 ? MV_LEFT  :
14003                         dx == +1 ? MV_RIGHT :
14004                         dy == -1 ? MV_UP    :
14005                         dy == +1 ? MV_DOWN  : MV_NONE);
14006   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14007   int dig_side = MV_DIR_OPPOSITE(move_direction);
14008   int old_element = Tile[jx][jy];
14009   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14010   int collect_count;
14011
14012   if (is_player)                // function can also be called by EL_PENGUIN
14013   {
14014     if (player->MovPos == 0)
14015     {
14016       player->is_digging = FALSE;
14017       player->is_collecting = FALSE;
14018     }
14019
14020     if (player->MovPos == 0)    // last pushing move finished
14021       player->is_pushing = FALSE;
14022
14023     if (mode == DF_NO_PUSH)     // player just stopped pushing
14024     {
14025       player->is_switching = FALSE;
14026       player->push_delay = -1;
14027
14028       return MP_NO_ACTION;
14029     }
14030   }
14031
14032   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14033     old_element = Back[jx][jy];
14034
14035   // in case of element dropped at player position, check background
14036   else if (Back[jx][jy] != EL_EMPTY &&
14037            game.engine_version >= VERSION_IDENT(2,2,0,0))
14038     old_element = Back[jx][jy];
14039
14040   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14041     return MP_NO_ACTION;        // field has no opening in this direction
14042
14043   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14044     return MP_NO_ACTION;        // field has no opening in this direction
14045
14046   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14047   {
14048     SplashAcid(x, y);
14049
14050     Tile[jx][jy] = player->artwork_element;
14051     InitMovingField(jx, jy, MV_DOWN);
14052     Store[jx][jy] = EL_ACID;
14053     ContinueMoving(jx, jy);
14054     BuryPlayer(player);
14055
14056     return MP_DONT_RUN_INTO;
14057   }
14058
14059   if (player_can_move && DONT_RUN_INTO(element))
14060   {
14061     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14062
14063     return MP_DONT_RUN_INTO;
14064   }
14065
14066   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14067     return MP_NO_ACTION;
14068
14069   collect_count = element_info[element].collect_count_initial;
14070
14071   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14072     return MP_NO_ACTION;
14073
14074   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14075     player_can_move = player_can_move_or_snap;
14076
14077   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14078       game.engine_version >= VERSION_IDENT(2,2,0,0))
14079   {
14080     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14081                                player->index_bit, dig_side);
14082     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14083                                         player->index_bit, dig_side);
14084
14085     if (element == EL_DC_LANDMINE)
14086       Bang(x, y);
14087
14088     if (Tile[x][y] != element)          // field changed by snapping
14089       return MP_ACTION;
14090
14091     return MP_NO_ACTION;
14092   }
14093
14094   if (player->gravity && is_player && !player->is_auto_moving &&
14095       canFallDown(player) && move_direction != MV_DOWN &&
14096       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14097     return MP_NO_ACTION;        // player cannot walk here due to gravity
14098
14099   if (player_can_move &&
14100       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14101   {
14102     int sound_element = SND_ELEMENT(element);
14103     int sound_action = ACTION_WALKING;
14104
14105     if (IS_RND_GATE(element))
14106     {
14107       if (!player->key[RND_GATE_NR(element)])
14108         return MP_NO_ACTION;
14109     }
14110     else if (IS_RND_GATE_GRAY(element))
14111     {
14112       if (!player->key[RND_GATE_GRAY_NR(element)])
14113         return MP_NO_ACTION;
14114     }
14115     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14116     {
14117       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14118         return MP_NO_ACTION;
14119     }
14120     else if (element == EL_EXIT_OPEN ||
14121              element == EL_EM_EXIT_OPEN ||
14122              element == EL_EM_EXIT_OPENING ||
14123              element == EL_STEEL_EXIT_OPEN ||
14124              element == EL_EM_STEEL_EXIT_OPEN ||
14125              element == EL_EM_STEEL_EXIT_OPENING ||
14126              element == EL_SP_EXIT_OPEN ||
14127              element == EL_SP_EXIT_OPENING)
14128     {
14129       sound_action = ACTION_PASSING;    // player is passing exit
14130     }
14131     else if (element == EL_EMPTY)
14132     {
14133       sound_action = ACTION_MOVING;             // nothing to walk on
14134     }
14135
14136     // play sound from background or player, whatever is available
14137     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14138       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14139     else
14140       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14141   }
14142   else if (player_can_move &&
14143            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14144   {
14145     if (!ACCESS_FROM(element, opposite_direction))
14146       return MP_NO_ACTION;      // field not accessible from this direction
14147
14148     if (CAN_MOVE(element))      // only fixed elements can be passed!
14149       return MP_NO_ACTION;
14150
14151     if (IS_EM_GATE(element))
14152     {
14153       if (!player->key[EM_GATE_NR(element)])
14154         return MP_NO_ACTION;
14155     }
14156     else if (IS_EM_GATE_GRAY(element))
14157     {
14158       if (!player->key[EM_GATE_GRAY_NR(element)])
14159         return MP_NO_ACTION;
14160     }
14161     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14162     {
14163       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14164         return MP_NO_ACTION;
14165     }
14166     else if (IS_EMC_GATE(element))
14167     {
14168       if (!player->key[EMC_GATE_NR(element)])
14169         return MP_NO_ACTION;
14170     }
14171     else if (IS_EMC_GATE_GRAY(element))
14172     {
14173       if (!player->key[EMC_GATE_GRAY_NR(element)])
14174         return MP_NO_ACTION;
14175     }
14176     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14177     {
14178       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14179         return MP_NO_ACTION;
14180     }
14181     else if (element == EL_DC_GATE_WHITE ||
14182              element == EL_DC_GATE_WHITE_GRAY ||
14183              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14184     {
14185       if (player->num_white_keys == 0)
14186         return MP_NO_ACTION;
14187
14188       player->num_white_keys--;
14189     }
14190     else if (IS_SP_PORT(element))
14191     {
14192       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14193           element == EL_SP_GRAVITY_PORT_RIGHT ||
14194           element == EL_SP_GRAVITY_PORT_UP ||
14195           element == EL_SP_GRAVITY_PORT_DOWN)
14196         player->gravity = !player->gravity;
14197       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14198                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14199                element == EL_SP_GRAVITY_ON_PORT_UP ||
14200                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14201         player->gravity = TRUE;
14202       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14203                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14204                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14205                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14206         player->gravity = FALSE;
14207     }
14208
14209     // automatically move to the next field with double speed
14210     player->programmed_action = move_direction;
14211
14212     if (player->move_delay_reset_counter == 0)
14213     {
14214       player->move_delay_reset_counter = 2;     // two double speed steps
14215
14216       DOUBLE_PLAYER_SPEED(player);
14217     }
14218
14219     PlayLevelSoundAction(x, y, ACTION_PASSING);
14220   }
14221   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14222   {
14223     RemoveField(x, y);
14224
14225     if (mode != DF_SNAP)
14226     {
14227       GfxElement[x][y] = GFX_ELEMENT(element);
14228       player->is_digging = TRUE;
14229     }
14230
14231     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14232
14233     // use old behaviour for old levels (digging)
14234     if (!level.finish_dig_collect)
14235     {
14236       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14237                                           player->index_bit, dig_side);
14238
14239       // if digging triggered player relocation, finish digging tile
14240       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14241         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14242     }
14243
14244     if (mode == DF_SNAP)
14245     {
14246       if (level.block_snap_field)
14247         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14248       else
14249         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14250
14251       // use old behaviour for old levels (snapping)
14252       if (!level.finish_dig_collect)
14253         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14254                                             player->index_bit, dig_side);
14255     }
14256   }
14257   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14258   {
14259     RemoveField(x, y);
14260
14261     if (is_player && mode != DF_SNAP)
14262     {
14263       GfxElement[x][y] = element;
14264       player->is_collecting = TRUE;
14265     }
14266
14267     if (element == EL_SPEED_PILL)
14268     {
14269       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14270     }
14271     else if (element == EL_EXTRA_TIME && level.time > 0)
14272     {
14273       TimeLeft += level.extra_time;
14274
14275       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14276
14277       DisplayGameControlValues();
14278     }
14279     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14280     {
14281       player->shield_normal_time_left += level.shield_normal_time;
14282       if (element == EL_SHIELD_DEADLY)
14283         player->shield_deadly_time_left += level.shield_deadly_time;
14284     }
14285     else if (element == EL_DYNAMITE ||
14286              element == EL_EM_DYNAMITE ||
14287              element == EL_SP_DISK_RED)
14288     {
14289       if (player->inventory_size < MAX_INVENTORY_SIZE)
14290         player->inventory_element[player->inventory_size++] = element;
14291
14292       DrawGameDoorValues();
14293     }
14294     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14295     {
14296       player->dynabomb_count++;
14297       player->dynabombs_left++;
14298     }
14299     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14300     {
14301       player->dynabomb_size++;
14302     }
14303     else if (element == EL_DYNABOMB_INCREASE_POWER)
14304     {
14305       player->dynabomb_xl = TRUE;
14306     }
14307     else if (IS_KEY(element))
14308     {
14309       player->key[KEY_NR(element)] = TRUE;
14310
14311       DrawGameDoorValues();
14312     }
14313     else if (element == EL_DC_KEY_WHITE)
14314     {
14315       player->num_white_keys++;
14316
14317       // display white keys?
14318       // DrawGameDoorValues();
14319     }
14320     else if (IS_ENVELOPE(element))
14321     {
14322       player->show_envelope = element;
14323     }
14324     else if (element == EL_EMC_LENSES)
14325     {
14326       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14327
14328       RedrawAllInvisibleElementsForLenses();
14329     }
14330     else if (element == EL_EMC_MAGNIFIER)
14331     {
14332       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14333
14334       RedrawAllInvisibleElementsForMagnifier();
14335     }
14336     else if (IS_DROPPABLE(element) ||
14337              IS_THROWABLE(element))     // can be collected and dropped
14338     {
14339       int i;
14340
14341       if (collect_count == 0)
14342         player->inventory_infinite_element = element;
14343       else
14344         for (i = 0; i < collect_count; i++)
14345           if (player->inventory_size < MAX_INVENTORY_SIZE)
14346             player->inventory_element[player->inventory_size++] = element;
14347
14348       DrawGameDoorValues();
14349     }
14350     else if (collect_count > 0)
14351     {
14352       game.gems_still_needed -= collect_count;
14353       if (game.gems_still_needed < 0)
14354         game.gems_still_needed = 0;
14355
14356       game.snapshot.collected_item = TRUE;
14357
14358       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14359
14360       DisplayGameControlValues();
14361     }
14362
14363     RaiseScoreElement(element);
14364     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14365
14366     // use old behaviour for old levels (collecting)
14367     if (!level.finish_dig_collect && is_player)
14368     {
14369       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14370                                           player->index_bit, dig_side);
14371
14372       // if collecting triggered player relocation, finish collecting tile
14373       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14374         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14375     }
14376
14377     if (mode == DF_SNAP)
14378     {
14379       if (level.block_snap_field)
14380         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14381       else
14382         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14383
14384       // use old behaviour for old levels (snapping)
14385       if (!level.finish_dig_collect)
14386         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14387                                             player->index_bit, dig_side);
14388     }
14389   }
14390   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14391   {
14392     if (mode == DF_SNAP && element != EL_BD_ROCK)
14393       return MP_NO_ACTION;
14394
14395     if (CAN_FALL(element) && dy)
14396       return MP_NO_ACTION;
14397
14398     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14399         !(element == EL_SPRING && level.use_spring_bug))
14400       return MP_NO_ACTION;
14401
14402     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14403         ((move_direction & MV_VERTICAL &&
14404           ((element_info[element].move_pattern & MV_LEFT &&
14405             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14406            (element_info[element].move_pattern & MV_RIGHT &&
14407             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14408          (move_direction & MV_HORIZONTAL &&
14409           ((element_info[element].move_pattern & MV_UP &&
14410             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14411            (element_info[element].move_pattern & MV_DOWN &&
14412             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14413       return MP_NO_ACTION;
14414
14415     // do not push elements already moving away faster than player
14416     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14417         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14418       return MP_NO_ACTION;
14419
14420     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14421     {
14422       if (player->push_delay_value == -1 || !player_was_pushing)
14423         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14424     }
14425     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14426     {
14427       if (player->push_delay_value == -1)
14428         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14429     }
14430     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14431     {
14432       if (!player->is_pushing)
14433         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14434     }
14435
14436     player->is_pushing = TRUE;
14437     player->is_active = TRUE;
14438
14439     if (!(IN_LEV_FIELD(nextx, nexty) &&
14440           (IS_FREE(nextx, nexty) ||
14441            (IS_SB_ELEMENT(element) &&
14442             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14443            (IS_CUSTOM_ELEMENT(element) &&
14444             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14445       return MP_NO_ACTION;
14446
14447     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14448       return MP_NO_ACTION;
14449
14450     if (player->push_delay == -1)       // new pushing; restart delay
14451       player->push_delay = 0;
14452
14453     if (player->push_delay < player->push_delay_value &&
14454         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14455         element != EL_SPRING && element != EL_BALLOON)
14456     {
14457       // make sure that there is no move delay before next try to push
14458       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14459         player->move_delay = 0;
14460
14461       return MP_NO_ACTION;
14462     }
14463
14464     if (IS_CUSTOM_ELEMENT(element) &&
14465         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14466     {
14467       if (!DigFieldByCE(nextx, nexty, element))
14468         return MP_NO_ACTION;
14469     }
14470
14471     if (IS_SB_ELEMENT(element))
14472     {
14473       boolean sokoban_task_solved = FALSE;
14474
14475       if (element == EL_SOKOBAN_FIELD_FULL)
14476       {
14477         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14478
14479         IncrementSokobanFieldsNeeded();
14480         IncrementSokobanObjectsNeeded();
14481       }
14482
14483       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14484       {
14485         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14486
14487         DecrementSokobanFieldsNeeded();
14488         DecrementSokobanObjectsNeeded();
14489
14490         // sokoban object was pushed from empty field to sokoban field
14491         if (Back[x][y] == EL_EMPTY)
14492           sokoban_task_solved = TRUE;
14493       }
14494
14495       Tile[x][y] = EL_SOKOBAN_OBJECT;
14496
14497       if (Back[x][y] == Back[nextx][nexty])
14498         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14499       else if (Back[x][y] != 0)
14500         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14501                                     ACTION_EMPTYING);
14502       else
14503         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14504                                     ACTION_FILLING);
14505
14506       if (sokoban_task_solved &&
14507           game.sokoban_fields_still_needed == 0 &&
14508           game.sokoban_objects_still_needed == 0 &&
14509           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14510       {
14511         game.players_still_needed = 0;
14512
14513         LevelSolved();
14514
14515         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14516       }
14517     }
14518     else
14519       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14520
14521     InitMovingField(x, y, move_direction);
14522     GfxAction[x][y] = ACTION_PUSHING;
14523
14524     if (mode == DF_SNAP)
14525       ContinueMoving(x, y);
14526     else
14527       MovPos[x][y] = (dx != 0 ? dx : dy);
14528
14529     Pushed[x][y] = TRUE;
14530     Pushed[nextx][nexty] = TRUE;
14531
14532     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14533       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14534     else
14535       player->push_delay_value = -1;    // get new value later
14536
14537     // check for element change _after_ element has been pushed
14538     if (game.use_change_when_pushing_bug)
14539     {
14540       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14541                                  player->index_bit, dig_side);
14542       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14543                                           player->index_bit, dig_side);
14544     }
14545   }
14546   else if (IS_SWITCHABLE(element))
14547   {
14548     if (PLAYER_SWITCHING(player, x, y))
14549     {
14550       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14551                                           player->index_bit, dig_side);
14552
14553       return MP_ACTION;
14554     }
14555
14556     player->is_switching = TRUE;
14557     player->switch_x = x;
14558     player->switch_y = y;
14559
14560     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14561
14562     if (element == EL_ROBOT_WHEEL)
14563     {
14564       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14565
14566       game.robot_wheel_x = x;
14567       game.robot_wheel_y = y;
14568       game.robot_wheel_active = TRUE;
14569
14570       TEST_DrawLevelField(x, y);
14571     }
14572     else if (element == EL_SP_TERMINAL)
14573     {
14574       int xx, yy;
14575
14576       SCAN_PLAYFIELD(xx, yy)
14577       {
14578         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14579         {
14580           Bang(xx, yy);
14581         }
14582         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14583         {
14584           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14585
14586           ResetGfxAnimation(xx, yy);
14587           TEST_DrawLevelField(xx, yy);
14588         }
14589       }
14590     }
14591     else if (IS_BELT_SWITCH(element))
14592     {
14593       ToggleBeltSwitch(x, y);
14594     }
14595     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14596              element == EL_SWITCHGATE_SWITCH_DOWN ||
14597              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14598              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14599     {
14600       ToggleSwitchgateSwitch(x, y);
14601     }
14602     else if (element == EL_LIGHT_SWITCH ||
14603              element == EL_LIGHT_SWITCH_ACTIVE)
14604     {
14605       ToggleLightSwitch(x, y);
14606     }
14607     else if (element == EL_TIMEGATE_SWITCH ||
14608              element == EL_DC_TIMEGATE_SWITCH)
14609     {
14610       ActivateTimegateSwitch(x, y);
14611     }
14612     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14613              element == EL_BALLOON_SWITCH_RIGHT ||
14614              element == EL_BALLOON_SWITCH_UP    ||
14615              element == EL_BALLOON_SWITCH_DOWN  ||
14616              element == EL_BALLOON_SWITCH_NONE  ||
14617              element == EL_BALLOON_SWITCH_ANY)
14618     {
14619       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14620                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14621                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14622                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14623                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14624                              move_direction);
14625     }
14626     else if (element == EL_LAMP)
14627     {
14628       Tile[x][y] = EL_LAMP_ACTIVE;
14629       game.lights_still_needed--;
14630
14631       ResetGfxAnimation(x, y);
14632       TEST_DrawLevelField(x, y);
14633     }
14634     else if (element == EL_TIME_ORB_FULL)
14635     {
14636       Tile[x][y] = EL_TIME_ORB_EMPTY;
14637
14638       if (level.time > 0 || level.use_time_orb_bug)
14639       {
14640         TimeLeft += level.time_orb_time;
14641         game.no_time_limit = FALSE;
14642
14643         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14644
14645         DisplayGameControlValues();
14646       }
14647
14648       ResetGfxAnimation(x, y);
14649       TEST_DrawLevelField(x, y);
14650     }
14651     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14652              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14653     {
14654       int xx, yy;
14655
14656       game.ball_active = !game.ball_active;
14657
14658       SCAN_PLAYFIELD(xx, yy)
14659       {
14660         int e = Tile[xx][yy];
14661
14662         if (game.ball_active)
14663         {
14664           if (e == EL_EMC_MAGIC_BALL)
14665             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14666           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14667             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14668         }
14669         else
14670         {
14671           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14672             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14673           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14674             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14675         }
14676       }
14677     }
14678
14679     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14680                                         player->index_bit, dig_side);
14681
14682     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14683                                         player->index_bit, dig_side);
14684
14685     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14686                                         player->index_bit, dig_side);
14687
14688     return MP_ACTION;
14689   }
14690   else
14691   {
14692     if (!PLAYER_SWITCHING(player, x, y))
14693     {
14694       player->is_switching = TRUE;
14695       player->switch_x = x;
14696       player->switch_y = y;
14697
14698       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14699                                  player->index_bit, dig_side);
14700       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14701                                           player->index_bit, dig_side);
14702
14703       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14704                                  player->index_bit, dig_side);
14705       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14706                                           player->index_bit, dig_side);
14707     }
14708
14709     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14710                                player->index_bit, dig_side);
14711     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14712                                         player->index_bit, dig_side);
14713
14714     return MP_NO_ACTION;
14715   }
14716
14717   player->push_delay = -1;
14718
14719   if (is_player)                // function can also be called by EL_PENGUIN
14720   {
14721     if (Tile[x][y] != element)          // really digged/collected something
14722     {
14723       player->is_collecting = !player->is_digging;
14724       player->is_active = TRUE;
14725
14726       player->last_removed_element = element;
14727     }
14728   }
14729
14730   return MP_MOVING;
14731 }
14732
14733 static boolean DigFieldByCE(int x, int y, int digging_element)
14734 {
14735   int element = Tile[x][y];
14736
14737   if (!IS_FREE(x, y))
14738   {
14739     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14740                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14741                   ACTION_BREAKING);
14742
14743     // no element can dig solid indestructible elements
14744     if (IS_INDESTRUCTIBLE(element) &&
14745         !IS_DIGGABLE(element) &&
14746         !IS_COLLECTIBLE(element))
14747       return FALSE;
14748
14749     if (AmoebaNr[x][y] &&
14750         (element == EL_AMOEBA_FULL ||
14751          element == EL_BD_AMOEBA ||
14752          element == EL_AMOEBA_GROWING))
14753     {
14754       AmoebaCnt[AmoebaNr[x][y]]--;
14755       AmoebaCnt2[AmoebaNr[x][y]]--;
14756     }
14757
14758     if (IS_MOVING(x, y))
14759       RemoveMovingField(x, y);
14760     else
14761     {
14762       RemoveField(x, y);
14763       TEST_DrawLevelField(x, y);
14764     }
14765
14766     // if digged element was about to explode, prevent the explosion
14767     ExplodeField[x][y] = EX_TYPE_NONE;
14768
14769     PlayLevelSoundAction(x, y, action);
14770   }
14771
14772   Store[x][y] = EL_EMPTY;
14773
14774   // this makes it possible to leave the removed element again
14775   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14776     Store[x][y] = element;
14777
14778   return TRUE;
14779 }
14780
14781 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14782 {
14783   int jx = player->jx, jy = player->jy;
14784   int x = jx + dx, y = jy + dy;
14785   int snap_direction = (dx == -1 ? MV_LEFT  :
14786                         dx == +1 ? MV_RIGHT :
14787                         dy == -1 ? MV_UP    :
14788                         dy == +1 ? MV_DOWN  : MV_NONE);
14789   boolean can_continue_snapping = (level.continuous_snapping &&
14790                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14791
14792   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14793     return FALSE;
14794
14795   if (!player->active || !IN_LEV_FIELD(x, y))
14796     return FALSE;
14797
14798   if (dx && dy)
14799     return FALSE;
14800
14801   if (!dx && !dy)
14802   {
14803     if (player->MovPos == 0)
14804       player->is_pushing = FALSE;
14805
14806     player->is_snapping = FALSE;
14807
14808     if (player->MovPos == 0)
14809     {
14810       player->is_moving = FALSE;
14811       player->is_digging = FALSE;
14812       player->is_collecting = FALSE;
14813     }
14814
14815     return FALSE;
14816   }
14817
14818   // prevent snapping with already pressed snap key when not allowed
14819   if (player->is_snapping && !can_continue_snapping)
14820     return FALSE;
14821
14822   player->MovDir = snap_direction;
14823
14824   if (player->MovPos == 0)
14825   {
14826     player->is_moving = FALSE;
14827     player->is_digging = FALSE;
14828     player->is_collecting = FALSE;
14829   }
14830
14831   player->is_dropping = FALSE;
14832   player->is_dropping_pressed = FALSE;
14833   player->drop_pressed_delay = 0;
14834
14835   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14836     return FALSE;
14837
14838   player->is_snapping = TRUE;
14839   player->is_active = TRUE;
14840
14841   if (player->MovPos == 0)
14842   {
14843     player->is_moving = FALSE;
14844     player->is_digging = FALSE;
14845     player->is_collecting = FALSE;
14846   }
14847
14848   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14849     TEST_DrawLevelField(player->last_jx, player->last_jy);
14850
14851   TEST_DrawLevelField(x, y);
14852
14853   return TRUE;
14854 }
14855
14856 static boolean DropElement(struct PlayerInfo *player)
14857 {
14858   int old_element, new_element;
14859   int dropx = player->jx, dropy = player->jy;
14860   int drop_direction = player->MovDir;
14861   int drop_side = drop_direction;
14862   int drop_element = get_next_dropped_element(player);
14863
14864   /* do not drop an element on top of another element; when holding drop key
14865      pressed without moving, dropped element must move away before the next
14866      element can be dropped (this is especially important if the next element
14867      is dynamite, which can be placed on background for historical reasons) */
14868   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14869     return MP_ACTION;
14870
14871   if (IS_THROWABLE(drop_element))
14872   {
14873     dropx += GET_DX_FROM_DIR(drop_direction);
14874     dropy += GET_DY_FROM_DIR(drop_direction);
14875
14876     if (!IN_LEV_FIELD(dropx, dropy))
14877       return FALSE;
14878   }
14879
14880   old_element = Tile[dropx][dropy];     // old element at dropping position
14881   new_element = drop_element;           // default: no change when dropping
14882
14883   // check if player is active, not moving and ready to drop
14884   if (!player->active || player->MovPos || player->drop_delay > 0)
14885     return FALSE;
14886
14887   // check if player has anything that can be dropped
14888   if (new_element == EL_UNDEFINED)
14889     return FALSE;
14890
14891   // only set if player has anything that can be dropped
14892   player->is_dropping_pressed = TRUE;
14893
14894   // check if drop key was pressed long enough for EM style dynamite
14895   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14896     return FALSE;
14897
14898   // check if anything can be dropped at the current position
14899   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14900     return FALSE;
14901
14902   // collected custom elements can only be dropped on empty fields
14903   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14904     return FALSE;
14905
14906   if (old_element != EL_EMPTY)
14907     Back[dropx][dropy] = old_element;   // store old element on this field
14908
14909   ResetGfxAnimation(dropx, dropy);
14910   ResetRandomAnimationValue(dropx, dropy);
14911
14912   if (player->inventory_size > 0 ||
14913       player->inventory_infinite_element != EL_UNDEFINED)
14914   {
14915     if (player->inventory_size > 0)
14916     {
14917       player->inventory_size--;
14918
14919       DrawGameDoorValues();
14920
14921       if (new_element == EL_DYNAMITE)
14922         new_element = EL_DYNAMITE_ACTIVE;
14923       else if (new_element == EL_EM_DYNAMITE)
14924         new_element = EL_EM_DYNAMITE_ACTIVE;
14925       else if (new_element == EL_SP_DISK_RED)
14926         new_element = EL_SP_DISK_RED_ACTIVE;
14927     }
14928
14929     Tile[dropx][dropy] = new_element;
14930
14931     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14932       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14933                           el2img(Tile[dropx][dropy]), 0);
14934
14935     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14936
14937     // needed if previous element just changed to "empty" in the last frame
14938     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14939
14940     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14941                                player->index_bit, drop_side);
14942     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14943                                         CE_PLAYER_DROPS_X,
14944                                         player->index_bit, drop_side);
14945
14946     TestIfElementTouchesCustomElement(dropx, dropy);
14947   }
14948   else          // player is dropping a dyna bomb
14949   {
14950     player->dynabombs_left--;
14951
14952     Tile[dropx][dropy] = new_element;
14953
14954     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14955       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14956                           el2img(Tile[dropx][dropy]), 0);
14957
14958     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14959   }
14960
14961   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14962     InitField_WithBug1(dropx, dropy, FALSE);
14963
14964   new_element = Tile[dropx][dropy];     // element might have changed
14965
14966   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14967       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14968   {
14969     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14970       MovDir[dropx][dropy] = drop_direction;
14971
14972     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14973
14974     // do not cause impact style collision by dropping elements that can fall
14975     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14976   }
14977
14978   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14979   player->is_dropping = TRUE;
14980
14981   player->drop_pressed_delay = 0;
14982   player->is_dropping_pressed = FALSE;
14983
14984   player->drop_x = dropx;
14985   player->drop_y = dropy;
14986
14987   return TRUE;
14988 }
14989
14990 // ----------------------------------------------------------------------------
14991 // game sound playing functions
14992 // ----------------------------------------------------------------------------
14993
14994 static int *loop_sound_frame = NULL;
14995 static int *loop_sound_volume = NULL;
14996
14997 void InitPlayLevelSound(void)
14998 {
14999   int num_sounds = getSoundListSize();
15000
15001   checked_free(loop_sound_frame);
15002   checked_free(loop_sound_volume);
15003
15004   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15005   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15006 }
15007
15008 static void PlayLevelSound(int x, int y, int nr)
15009 {
15010   int sx = SCREENX(x), sy = SCREENY(y);
15011   int volume, stereo_position;
15012   int max_distance = 8;
15013   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15014
15015   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15016       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15017     return;
15018
15019   if (!IN_LEV_FIELD(x, y) ||
15020       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15021       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15022     return;
15023
15024   volume = SOUND_MAX_VOLUME;
15025
15026   if (!IN_SCR_FIELD(sx, sy))
15027   {
15028     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15029     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15030
15031     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15032   }
15033
15034   stereo_position = (SOUND_MAX_LEFT +
15035                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15036                      (SCR_FIELDX + 2 * max_distance));
15037
15038   if (IS_LOOP_SOUND(nr))
15039   {
15040     /* This assures that quieter loop sounds do not overwrite louder ones,
15041        while restarting sound volume comparison with each new game frame. */
15042
15043     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15044       return;
15045
15046     loop_sound_volume[nr] = volume;
15047     loop_sound_frame[nr] = FrameCounter;
15048   }
15049
15050   PlaySoundExt(nr, volume, stereo_position, type);
15051 }
15052
15053 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15054 {
15055   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15056                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15057                  y < LEVELY(BY1) ? LEVELY(BY1) :
15058                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15059                  sound_action);
15060 }
15061
15062 static void PlayLevelSoundAction(int x, int y, int action)
15063 {
15064   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15065 }
15066
15067 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15068 {
15069   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15070
15071   if (sound_effect != SND_UNDEFINED)
15072     PlayLevelSound(x, y, sound_effect);
15073 }
15074
15075 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15076                                               int action)
15077 {
15078   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15079
15080   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15081     PlayLevelSound(x, y, sound_effect);
15082 }
15083
15084 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15085 {
15086   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15087
15088   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15089     PlayLevelSound(x, y, sound_effect);
15090 }
15091
15092 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15093 {
15094   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15095
15096   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15097     StopSound(sound_effect);
15098 }
15099
15100 static int getLevelMusicNr(void)
15101 {
15102   if (levelset.music[level_nr] != MUS_UNDEFINED)
15103     return levelset.music[level_nr];            // from config file
15104   else
15105     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15106 }
15107
15108 static void FadeLevelSounds(void)
15109 {
15110   FadeSounds();
15111 }
15112
15113 static void FadeLevelMusic(void)
15114 {
15115   int music_nr = getLevelMusicNr();
15116   char *curr_music = getCurrentlyPlayingMusicFilename();
15117   char *next_music = getMusicInfoEntryFilename(music_nr);
15118
15119   if (!strEqual(curr_music, next_music))
15120     FadeMusic();
15121 }
15122
15123 void FadeLevelSoundsAndMusic(void)
15124 {
15125   FadeLevelSounds();
15126   FadeLevelMusic();
15127 }
15128
15129 static void PlayLevelMusic(void)
15130 {
15131   int music_nr = getLevelMusicNr();
15132   char *curr_music = getCurrentlyPlayingMusicFilename();
15133   char *next_music = getMusicInfoEntryFilename(music_nr);
15134
15135   if (!strEqual(curr_music, next_music))
15136     PlayMusicLoop(music_nr);
15137 }
15138
15139 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15140 {
15141   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15142   int offset = 0;
15143   int x = xx - offset;
15144   int y = yy - offset;
15145
15146   switch (sample)
15147   {
15148     case SOUND_blank:
15149       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15150       break;
15151
15152     case SOUND_roll:
15153       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15154       break;
15155
15156     case SOUND_stone:
15157       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15158       break;
15159
15160     case SOUND_nut:
15161       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15162       break;
15163
15164     case SOUND_crack:
15165       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15166       break;
15167
15168     case SOUND_bug:
15169       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15170       break;
15171
15172     case SOUND_tank:
15173       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15174       break;
15175
15176     case SOUND_android_clone:
15177       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15178       break;
15179
15180     case SOUND_android_move:
15181       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15182       break;
15183
15184     case SOUND_spring:
15185       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15186       break;
15187
15188     case SOUND_slurp:
15189       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15190       break;
15191
15192     case SOUND_eater:
15193       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15194       break;
15195
15196     case SOUND_eater_eat:
15197       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15198       break;
15199
15200     case SOUND_alien:
15201       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15202       break;
15203
15204     case SOUND_collect:
15205       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15206       break;
15207
15208     case SOUND_diamond:
15209       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15210       break;
15211
15212     case SOUND_squash:
15213       // !!! CHECK THIS !!!
15214 #if 1
15215       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15216 #else
15217       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15218 #endif
15219       break;
15220
15221     case SOUND_wonderfall:
15222       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15223       break;
15224
15225     case SOUND_drip:
15226       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15227       break;
15228
15229     case SOUND_push:
15230       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15231       break;
15232
15233     case SOUND_dirt:
15234       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15235       break;
15236
15237     case SOUND_acid:
15238       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15239       break;
15240
15241     case SOUND_ball:
15242       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15243       break;
15244
15245     case SOUND_slide:
15246       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15247       break;
15248
15249     case SOUND_wonder:
15250       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15251       break;
15252
15253     case SOUND_door:
15254       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15255       break;
15256
15257     case SOUND_exit_open:
15258       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15259       break;
15260
15261     case SOUND_exit_leave:
15262       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15263       break;
15264
15265     case SOUND_dynamite:
15266       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15267       break;
15268
15269     case SOUND_tick:
15270       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15271       break;
15272
15273     case SOUND_press:
15274       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15275       break;
15276
15277     case SOUND_wheel:
15278       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15279       break;
15280
15281     case SOUND_boom:
15282       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15283       break;
15284
15285     case SOUND_die:
15286       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15287       break;
15288
15289     case SOUND_time:
15290       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15291       break;
15292
15293     default:
15294       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15295       break;
15296   }
15297 }
15298
15299 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15300 {
15301   int element = map_element_SP_to_RND(element_sp);
15302   int action = map_action_SP_to_RND(action_sp);
15303   int offset = (setup.sp_show_border_elements ? 0 : 1);
15304   int x = xx - offset;
15305   int y = yy - offset;
15306
15307   PlayLevelSoundElementAction(x, y, element, action);
15308 }
15309
15310 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15311 {
15312   int element = map_element_MM_to_RND(element_mm);
15313   int action = map_action_MM_to_RND(action_mm);
15314   int offset = 0;
15315   int x = xx - offset;
15316   int y = yy - offset;
15317
15318   if (!IS_MM_ELEMENT(element))
15319     element = EL_MM_DEFAULT;
15320
15321   PlayLevelSoundElementAction(x, y, element, action);
15322 }
15323
15324 void PlaySound_MM(int sound_mm)
15325 {
15326   int sound = map_sound_MM_to_RND(sound_mm);
15327
15328   if (sound == SND_UNDEFINED)
15329     return;
15330
15331   PlaySound(sound);
15332 }
15333
15334 void PlaySoundLoop_MM(int sound_mm)
15335 {
15336   int sound = map_sound_MM_to_RND(sound_mm);
15337
15338   if (sound == SND_UNDEFINED)
15339     return;
15340
15341   PlaySoundLoop(sound);
15342 }
15343
15344 void StopSound_MM(int sound_mm)
15345 {
15346   int sound = map_sound_MM_to_RND(sound_mm);
15347
15348   if (sound == SND_UNDEFINED)
15349     return;
15350
15351   StopSound(sound);
15352 }
15353
15354 void RaiseScore(int value)
15355 {
15356   game.score += value;
15357
15358   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15359
15360   DisplayGameControlValues();
15361 }
15362
15363 void RaiseScoreElement(int element)
15364 {
15365   switch (element)
15366   {
15367     case EL_EMERALD:
15368     case EL_BD_DIAMOND:
15369     case EL_EMERALD_YELLOW:
15370     case EL_EMERALD_RED:
15371     case EL_EMERALD_PURPLE:
15372     case EL_SP_INFOTRON:
15373       RaiseScore(level.score[SC_EMERALD]);
15374       break;
15375     case EL_DIAMOND:
15376       RaiseScore(level.score[SC_DIAMOND]);
15377       break;
15378     case EL_CRYSTAL:
15379       RaiseScore(level.score[SC_CRYSTAL]);
15380       break;
15381     case EL_PEARL:
15382       RaiseScore(level.score[SC_PEARL]);
15383       break;
15384     case EL_BUG:
15385     case EL_BD_BUTTERFLY:
15386     case EL_SP_ELECTRON:
15387       RaiseScore(level.score[SC_BUG]);
15388       break;
15389     case EL_SPACESHIP:
15390     case EL_BD_FIREFLY:
15391     case EL_SP_SNIKSNAK:
15392       RaiseScore(level.score[SC_SPACESHIP]);
15393       break;
15394     case EL_YAMYAM:
15395     case EL_DARK_YAMYAM:
15396       RaiseScore(level.score[SC_YAMYAM]);
15397       break;
15398     case EL_ROBOT:
15399       RaiseScore(level.score[SC_ROBOT]);
15400       break;
15401     case EL_PACMAN:
15402       RaiseScore(level.score[SC_PACMAN]);
15403       break;
15404     case EL_NUT:
15405       RaiseScore(level.score[SC_NUT]);
15406       break;
15407     case EL_DYNAMITE:
15408     case EL_EM_DYNAMITE:
15409     case EL_SP_DISK_RED:
15410     case EL_DYNABOMB_INCREASE_NUMBER:
15411     case EL_DYNABOMB_INCREASE_SIZE:
15412     case EL_DYNABOMB_INCREASE_POWER:
15413       RaiseScore(level.score[SC_DYNAMITE]);
15414       break;
15415     case EL_SHIELD_NORMAL:
15416     case EL_SHIELD_DEADLY:
15417       RaiseScore(level.score[SC_SHIELD]);
15418       break;
15419     case EL_EXTRA_TIME:
15420       RaiseScore(level.extra_time_score);
15421       break;
15422     case EL_KEY_1:
15423     case EL_KEY_2:
15424     case EL_KEY_3:
15425     case EL_KEY_4:
15426     case EL_EM_KEY_1:
15427     case EL_EM_KEY_2:
15428     case EL_EM_KEY_3:
15429     case EL_EM_KEY_4:
15430     case EL_EMC_KEY_5:
15431     case EL_EMC_KEY_6:
15432     case EL_EMC_KEY_7:
15433     case EL_EMC_KEY_8:
15434     case EL_DC_KEY_WHITE:
15435       RaiseScore(level.score[SC_KEY]);
15436       break;
15437     default:
15438       RaiseScore(element_info[element].collect_score);
15439       break;
15440   }
15441 }
15442
15443 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15444 {
15445   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15446   {
15447     if (!quick_quit)
15448     {
15449       // prevent short reactivation of overlay buttons while closing door
15450       SetOverlayActive(FALSE);
15451
15452       // door may still be open due to skipped or envelope style request
15453       CloseDoor(DOOR_CLOSE_1);
15454     }
15455
15456     if (network.enabled)
15457       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15458     else
15459     {
15460       if (quick_quit)
15461         FadeSkipNextFadeIn();
15462
15463       SetGameStatus(GAME_MODE_MAIN);
15464
15465       DrawMainMenu();
15466     }
15467   }
15468   else          // continue playing the game
15469   {
15470     if (tape.playing && tape.deactivate_display)
15471       TapeDeactivateDisplayOff(TRUE);
15472
15473     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15474
15475     if (tape.playing && tape.deactivate_display)
15476       TapeDeactivateDisplayOn();
15477   }
15478 }
15479
15480 void RequestQuitGame(boolean escape_key_pressed)
15481 {
15482   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15483   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15484                         level_editor_test_game);
15485   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15486                           quick_quit);
15487
15488   RequestQuitGameExt(skip_request, quick_quit,
15489                      "Do you really want to quit the game?");
15490 }
15491
15492 void RequestRestartGame(char *message)
15493 {
15494   game.restart_game_message = NULL;
15495
15496   boolean has_started_game = hasStartedNetworkGame();
15497   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15498
15499   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15500   {
15501     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15502   }
15503   else
15504   {
15505     // needed in case of envelope request to close game panel
15506     CloseDoor(DOOR_CLOSE_1);
15507
15508     SetGameStatus(GAME_MODE_MAIN);
15509
15510     DrawMainMenu();
15511   }
15512 }
15513
15514 void CheckGameOver(void)
15515 {
15516   static boolean last_game_over = FALSE;
15517   static int game_over_delay = 0;
15518   int game_over_delay_value = 50;
15519   boolean game_over = checkGameFailed();
15520
15521   // do not handle game over if request dialog is already active
15522   if (game.request_active)
15523     return;
15524
15525   // do not ask to play again if game was never actually played
15526   if (!game.GamePlayed)
15527     return;
15528
15529   if (!game_over)
15530   {
15531     last_game_over = FALSE;
15532     game_over_delay = game_over_delay_value;
15533
15534     return;
15535   }
15536
15537   if (game_over_delay > 0)
15538   {
15539     game_over_delay--;
15540
15541     return;
15542   }
15543
15544   if (last_game_over != game_over)
15545     game.restart_game_message = (hasStartedNetworkGame() ?
15546                                  "Game over! Play it again?" :
15547                                  "Game over!");
15548
15549   last_game_over = game_over;
15550 }
15551
15552 boolean checkGameSolved(void)
15553 {
15554   // set for all game engines if level was solved
15555   return game.LevelSolved_GameEnd;
15556 }
15557
15558 boolean checkGameFailed(void)
15559 {
15560   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15561     return (game_em.game_over && !game_em.level_solved);
15562   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15563     return (game_sp.game_over && !game_sp.level_solved);
15564   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15565     return (game_mm.game_over && !game_mm.level_solved);
15566   else                          // GAME_ENGINE_TYPE_RND
15567     return (game.GameOver && !game.LevelSolved);
15568 }
15569
15570 boolean checkGameEnded(void)
15571 {
15572   return (checkGameSolved() || checkGameFailed());
15573 }
15574
15575
15576 // ----------------------------------------------------------------------------
15577 // random generator functions
15578 // ----------------------------------------------------------------------------
15579
15580 unsigned int InitEngineRandom_RND(int seed)
15581 {
15582   game.num_random_calls = 0;
15583
15584   return InitEngineRandom(seed);
15585 }
15586
15587 unsigned int RND(int max)
15588 {
15589   if (max > 0)
15590   {
15591     game.num_random_calls++;
15592
15593     return GetEngineRandom(max);
15594   }
15595
15596   return 0;
15597 }
15598
15599
15600 // ----------------------------------------------------------------------------
15601 // game engine snapshot handling functions
15602 // ----------------------------------------------------------------------------
15603
15604 struct EngineSnapshotInfo
15605 {
15606   // runtime values for custom element collect score
15607   int collect_score[NUM_CUSTOM_ELEMENTS];
15608
15609   // runtime values for group element choice position
15610   int choice_pos[NUM_GROUP_ELEMENTS];
15611
15612   // runtime values for belt position animations
15613   int belt_graphic[4][NUM_BELT_PARTS];
15614   int belt_anim_mode[4][NUM_BELT_PARTS];
15615 };
15616
15617 static struct EngineSnapshotInfo engine_snapshot_rnd;
15618 static char *snapshot_level_identifier = NULL;
15619 static int snapshot_level_nr = -1;
15620
15621 static void SaveEngineSnapshotValues_RND(void)
15622 {
15623   static int belt_base_active_element[4] =
15624   {
15625     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15626     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15627     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15628     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15629   };
15630   int i, j;
15631
15632   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15633   {
15634     int element = EL_CUSTOM_START + i;
15635
15636     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15637   }
15638
15639   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15640   {
15641     int element = EL_GROUP_START + i;
15642
15643     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15644   }
15645
15646   for (i = 0; i < 4; i++)
15647   {
15648     for (j = 0; j < NUM_BELT_PARTS; j++)
15649     {
15650       int element = belt_base_active_element[i] + j;
15651       int graphic = el2img(element);
15652       int anim_mode = graphic_info[graphic].anim_mode;
15653
15654       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15655       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15656     }
15657   }
15658 }
15659
15660 static void LoadEngineSnapshotValues_RND(void)
15661 {
15662   unsigned int num_random_calls = game.num_random_calls;
15663   int i, j;
15664
15665   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15666   {
15667     int element = EL_CUSTOM_START + i;
15668
15669     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15670   }
15671
15672   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15673   {
15674     int element = EL_GROUP_START + i;
15675
15676     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15677   }
15678
15679   for (i = 0; i < 4; i++)
15680   {
15681     for (j = 0; j < NUM_BELT_PARTS; j++)
15682     {
15683       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15684       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15685
15686       graphic_info[graphic].anim_mode = anim_mode;
15687     }
15688   }
15689
15690   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15691   {
15692     InitRND(tape.random_seed);
15693     for (i = 0; i < num_random_calls; i++)
15694       RND(1);
15695   }
15696
15697   if (game.num_random_calls != num_random_calls)
15698   {
15699     Error("number of random calls out of sync");
15700     Error("number of random calls should be %d", num_random_calls);
15701     Error("number of random calls is %d", game.num_random_calls);
15702
15703     Fail("this should not happen -- please debug");
15704   }
15705 }
15706
15707 void FreeEngineSnapshotSingle(void)
15708 {
15709   FreeSnapshotSingle();
15710
15711   setString(&snapshot_level_identifier, NULL);
15712   snapshot_level_nr = -1;
15713 }
15714
15715 void FreeEngineSnapshotList(void)
15716 {
15717   FreeSnapshotList();
15718 }
15719
15720 static ListNode *SaveEngineSnapshotBuffers(void)
15721 {
15722   ListNode *buffers = NULL;
15723
15724   // copy some special values to a structure better suited for the snapshot
15725
15726   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15727     SaveEngineSnapshotValues_RND();
15728   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15729     SaveEngineSnapshotValues_EM();
15730   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15731     SaveEngineSnapshotValues_SP(&buffers);
15732   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15733     SaveEngineSnapshotValues_MM(&buffers);
15734
15735   // save values stored in special snapshot structure
15736
15737   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15738     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15739   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15740     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15741   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15742     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15743   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15744     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15745
15746   // save further RND engine values
15747
15748   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15751
15752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15757
15758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15760   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15761
15762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15763
15764   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15765   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15766
15767   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15768   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15769   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15770   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15771   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15772   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15773   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15774   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15775   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15776   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15777   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15778   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15779   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15780   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15781   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15782   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15783   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15784   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15785
15786   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15787   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15788
15789   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15790   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15791   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15792
15793   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15794   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15795
15796   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15797   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15798   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15799   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15800   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15801
15802   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15803   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15804
15805 #if 0
15806   ListNode *node = engine_snapshot_list_rnd;
15807   int num_bytes = 0;
15808
15809   while (node != NULL)
15810   {
15811     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15812
15813     node = node->next;
15814   }
15815
15816   Debug("game:playing:SaveEngineSnapshotBuffers",
15817         "size of engine snapshot: %d bytes", num_bytes);
15818 #endif
15819
15820   return buffers;
15821 }
15822
15823 void SaveEngineSnapshotSingle(void)
15824 {
15825   ListNode *buffers = SaveEngineSnapshotBuffers();
15826
15827   // finally save all snapshot buffers to single snapshot
15828   SaveSnapshotSingle(buffers);
15829
15830   // save level identification information
15831   setString(&snapshot_level_identifier, leveldir_current->identifier);
15832   snapshot_level_nr = level_nr;
15833 }
15834
15835 boolean CheckSaveEngineSnapshotToList(void)
15836 {
15837   boolean save_snapshot =
15838     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15839      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15840       game.snapshot.changed_action) ||
15841      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15842       game.snapshot.collected_item));
15843
15844   game.snapshot.changed_action = FALSE;
15845   game.snapshot.collected_item = FALSE;
15846   game.snapshot.save_snapshot = save_snapshot;
15847
15848   return save_snapshot;
15849 }
15850
15851 void SaveEngineSnapshotToList(void)
15852 {
15853   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15854       tape.quick_resume)
15855     return;
15856
15857   ListNode *buffers = SaveEngineSnapshotBuffers();
15858
15859   // finally save all snapshot buffers to snapshot list
15860   SaveSnapshotToList(buffers);
15861 }
15862
15863 void SaveEngineSnapshotToListInitial(void)
15864 {
15865   FreeEngineSnapshotList();
15866
15867   SaveEngineSnapshotToList();
15868 }
15869
15870 static void LoadEngineSnapshotValues(void)
15871 {
15872   // restore special values from snapshot structure
15873
15874   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15875     LoadEngineSnapshotValues_RND();
15876   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15877     LoadEngineSnapshotValues_EM();
15878   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15879     LoadEngineSnapshotValues_SP();
15880   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15881     LoadEngineSnapshotValues_MM();
15882 }
15883
15884 void LoadEngineSnapshotSingle(void)
15885 {
15886   LoadSnapshotSingle();
15887
15888   LoadEngineSnapshotValues();
15889 }
15890
15891 static void LoadEngineSnapshot_Undo(int steps)
15892 {
15893   LoadSnapshotFromList_Older(steps);
15894
15895   LoadEngineSnapshotValues();
15896 }
15897
15898 static void LoadEngineSnapshot_Redo(int steps)
15899 {
15900   LoadSnapshotFromList_Newer(steps);
15901
15902   LoadEngineSnapshotValues();
15903 }
15904
15905 boolean CheckEngineSnapshotSingle(void)
15906 {
15907   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15908           snapshot_level_nr == level_nr);
15909 }
15910
15911 boolean CheckEngineSnapshotList(void)
15912 {
15913   return CheckSnapshotList();
15914 }
15915
15916
15917 // ---------- new game button stuff -------------------------------------------
15918
15919 static struct
15920 {
15921   int graphic;
15922   struct XY *pos;
15923   int gadget_id;
15924   boolean *setup_value;
15925   boolean allowed_on_tape;
15926   boolean is_touch_button;
15927   char *infotext;
15928 } gamebutton_info[NUM_GAME_BUTTONS] =
15929 {
15930   {
15931     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15932     GAME_CTRL_ID_STOP,                          NULL,
15933     TRUE, FALSE,                                "stop game"
15934   },
15935   {
15936     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15937     GAME_CTRL_ID_PAUSE,                         NULL,
15938     TRUE, FALSE,                                "pause game"
15939   },
15940   {
15941     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15942     GAME_CTRL_ID_PLAY,                          NULL,
15943     TRUE, FALSE,                                "play game"
15944   },
15945   {
15946     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15947     GAME_CTRL_ID_UNDO,                          NULL,
15948     TRUE, FALSE,                                "undo step"
15949   },
15950   {
15951     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15952     GAME_CTRL_ID_REDO,                          NULL,
15953     TRUE, FALSE,                                "redo step"
15954   },
15955   {
15956     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15957     GAME_CTRL_ID_SAVE,                          NULL,
15958     TRUE, FALSE,                                "save game"
15959   },
15960   {
15961     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15962     GAME_CTRL_ID_PAUSE2,                        NULL,
15963     TRUE, FALSE,                                "pause game"
15964   },
15965   {
15966     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15967     GAME_CTRL_ID_LOAD,                          NULL,
15968     TRUE, FALSE,                                "load game"
15969   },
15970   {
15971     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15972     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15973     FALSE, FALSE,                               "stop game"
15974   },
15975   {
15976     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15977     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15978     FALSE, FALSE,                               "pause game"
15979   },
15980   {
15981     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15982     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15983     FALSE, FALSE,                               "play game"
15984   },
15985   {
15986     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15987     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15988     FALSE, TRUE,                                "stop game"
15989   },
15990   {
15991     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15992     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15993     FALSE, TRUE,                                "pause game"
15994   },
15995   {
15996     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15997     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15998     TRUE, FALSE,                                "background music on/off"
15999   },
16000   {
16001     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16002     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16003     TRUE, FALSE,                                "sound loops on/off"
16004   },
16005   {
16006     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16007     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16008     TRUE, FALSE,                                "normal sounds on/off"
16009   },
16010   {
16011     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16012     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16013     FALSE, FALSE,                               "background music on/off"
16014   },
16015   {
16016     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16017     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16018     FALSE, FALSE,                               "sound loops on/off"
16019   },
16020   {
16021     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16022     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16023     FALSE, FALSE,                               "normal sounds on/off"
16024   }
16025 };
16026
16027 void CreateGameButtons(void)
16028 {
16029   int i;
16030
16031   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16032   {
16033     int graphic = gamebutton_info[i].graphic;
16034     struct GraphicInfo *gfx = &graphic_info[graphic];
16035     struct XY *pos = gamebutton_info[i].pos;
16036     struct GadgetInfo *gi;
16037     int button_type;
16038     boolean checked;
16039     unsigned int event_mask;
16040     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16041     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16042     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16043     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16044     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16045     int gd_x   = gfx->src_x;
16046     int gd_y   = gfx->src_y;
16047     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16048     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16049     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16050     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16051     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16052     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16053     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16054     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16055     int id = i;
16056
16057     if (gfx->bitmap == NULL)
16058     {
16059       game_gadget[id] = NULL;
16060
16061       continue;
16062     }
16063
16064     if (id == GAME_CTRL_ID_STOP ||
16065         id == GAME_CTRL_ID_PANEL_STOP ||
16066         id == GAME_CTRL_ID_TOUCH_STOP ||
16067         id == GAME_CTRL_ID_PLAY ||
16068         id == GAME_CTRL_ID_PANEL_PLAY ||
16069         id == GAME_CTRL_ID_SAVE ||
16070         id == GAME_CTRL_ID_LOAD)
16071     {
16072       button_type = GD_TYPE_NORMAL_BUTTON;
16073       checked = FALSE;
16074       event_mask = GD_EVENT_RELEASED;
16075     }
16076     else if (id == GAME_CTRL_ID_UNDO ||
16077              id == GAME_CTRL_ID_REDO)
16078     {
16079       button_type = GD_TYPE_NORMAL_BUTTON;
16080       checked = FALSE;
16081       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16082     }
16083     else
16084     {
16085       button_type = GD_TYPE_CHECK_BUTTON;
16086       checked = (gamebutton_info[i].setup_value != NULL ?
16087                  *gamebutton_info[i].setup_value : FALSE);
16088       event_mask = GD_EVENT_PRESSED;
16089     }
16090
16091     gi = CreateGadget(GDI_CUSTOM_ID, id,
16092                       GDI_IMAGE_ID, graphic,
16093                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16094                       GDI_X, base_x + x,
16095                       GDI_Y, base_y + y,
16096                       GDI_WIDTH, gfx->width,
16097                       GDI_HEIGHT, gfx->height,
16098                       GDI_TYPE, button_type,
16099                       GDI_STATE, GD_BUTTON_UNPRESSED,
16100                       GDI_CHECKED, checked,
16101                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16102                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16103                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16104                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16105                       GDI_DIRECT_DRAW, FALSE,
16106                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16107                       GDI_EVENT_MASK, event_mask,
16108                       GDI_CALLBACK_ACTION, HandleGameButtons,
16109                       GDI_END);
16110
16111     if (gi == NULL)
16112       Fail("cannot create gadget");
16113
16114     game_gadget[id] = gi;
16115   }
16116 }
16117
16118 void FreeGameButtons(void)
16119 {
16120   int i;
16121
16122   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16123     FreeGadget(game_gadget[i]);
16124 }
16125
16126 static void UnmapGameButtonsAtSamePosition(int id)
16127 {
16128   int i;
16129
16130   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16131     if (i != id &&
16132         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16133         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16134       UnmapGadget(game_gadget[i]);
16135 }
16136
16137 static void UnmapGameButtonsAtSamePosition_All(void)
16138 {
16139   if (setup.show_snapshot_buttons)
16140   {
16141     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16142     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16143     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16144   }
16145   else
16146   {
16147     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16148     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16149     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16150
16151     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16152     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16153     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16154   }
16155 }
16156
16157 static void MapGameButtonsAtSamePosition(int id)
16158 {
16159   int i;
16160
16161   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16162     if (i != id &&
16163         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16164         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16165       MapGadget(game_gadget[i]);
16166
16167   UnmapGameButtonsAtSamePosition_All();
16168 }
16169
16170 void MapUndoRedoButtons(void)
16171 {
16172   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16173   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16174
16175   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16176   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16177 }
16178
16179 void UnmapUndoRedoButtons(void)
16180 {
16181   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16182   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16183
16184   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16185   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16186 }
16187
16188 void ModifyPauseButtons(void)
16189 {
16190   static int ids[] =
16191   {
16192     GAME_CTRL_ID_PAUSE,
16193     GAME_CTRL_ID_PAUSE2,
16194     GAME_CTRL_ID_PANEL_PAUSE,
16195     GAME_CTRL_ID_TOUCH_PAUSE,
16196     -1
16197   };
16198   int i;
16199
16200   for (i = 0; ids[i] > -1; i++)
16201     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16202 }
16203
16204 static void MapGameButtonsExt(boolean on_tape)
16205 {
16206   int i;
16207
16208   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16209     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16210         i != GAME_CTRL_ID_UNDO &&
16211         i != GAME_CTRL_ID_REDO)
16212       MapGadget(game_gadget[i]);
16213
16214   UnmapGameButtonsAtSamePosition_All();
16215
16216   RedrawGameButtons();
16217 }
16218
16219 static void UnmapGameButtonsExt(boolean on_tape)
16220 {
16221   int i;
16222
16223   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16224     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16225       UnmapGadget(game_gadget[i]);
16226 }
16227
16228 static void RedrawGameButtonsExt(boolean on_tape)
16229 {
16230   int i;
16231
16232   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16233     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16234       RedrawGadget(game_gadget[i]);
16235 }
16236
16237 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16238 {
16239   if (gi == NULL)
16240     return;
16241
16242   gi->checked = state;
16243 }
16244
16245 static void RedrawSoundButtonGadget(int id)
16246 {
16247   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16248              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16249              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16250              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16251              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16252              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16253              id);
16254
16255   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16256   RedrawGadget(game_gadget[id2]);
16257 }
16258
16259 void MapGameButtons(void)
16260 {
16261   MapGameButtonsExt(FALSE);
16262 }
16263
16264 void UnmapGameButtons(void)
16265 {
16266   UnmapGameButtonsExt(FALSE);
16267 }
16268
16269 void RedrawGameButtons(void)
16270 {
16271   RedrawGameButtonsExt(FALSE);
16272 }
16273
16274 void MapGameButtonsOnTape(void)
16275 {
16276   MapGameButtonsExt(TRUE);
16277 }
16278
16279 void UnmapGameButtonsOnTape(void)
16280 {
16281   UnmapGameButtonsExt(TRUE);
16282 }
16283
16284 void RedrawGameButtonsOnTape(void)
16285 {
16286   RedrawGameButtonsExt(TRUE);
16287 }
16288
16289 static void GameUndoRedoExt(void)
16290 {
16291   ClearPlayerAction();
16292
16293   tape.pausing = TRUE;
16294
16295   RedrawPlayfield();
16296   UpdateAndDisplayGameControlValues();
16297
16298   DrawCompleteVideoDisplay();
16299   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16300   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16301   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16302
16303   BackToFront();
16304 }
16305
16306 static void GameUndo(int steps)
16307 {
16308   if (!CheckEngineSnapshotList())
16309     return;
16310
16311   LoadEngineSnapshot_Undo(steps);
16312
16313   GameUndoRedoExt();
16314 }
16315
16316 static void GameRedo(int steps)
16317 {
16318   if (!CheckEngineSnapshotList())
16319     return;
16320
16321   LoadEngineSnapshot_Redo(steps);
16322
16323   GameUndoRedoExt();
16324 }
16325
16326 static void HandleGameButtonsExt(int id, int button)
16327 {
16328   static boolean game_undo_executed = FALSE;
16329   int steps = BUTTON_STEPSIZE(button);
16330   boolean handle_game_buttons =
16331     (game_status == GAME_MODE_PLAYING ||
16332      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16333
16334   if (!handle_game_buttons)
16335     return;
16336
16337   switch (id)
16338   {
16339     case GAME_CTRL_ID_STOP:
16340     case GAME_CTRL_ID_PANEL_STOP:
16341     case GAME_CTRL_ID_TOUCH_STOP:
16342       if (game_status == GAME_MODE_MAIN)
16343         break;
16344
16345       if (tape.playing)
16346         TapeStop();
16347       else
16348         RequestQuitGame(FALSE);
16349
16350       break;
16351
16352     case GAME_CTRL_ID_PAUSE:
16353     case GAME_CTRL_ID_PAUSE2:
16354     case GAME_CTRL_ID_PANEL_PAUSE:
16355     case GAME_CTRL_ID_TOUCH_PAUSE:
16356       if (network.enabled && game_status == GAME_MODE_PLAYING)
16357       {
16358         if (tape.pausing)
16359           SendToServer_ContinuePlaying();
16360         else
16361           SendToServer_PausePlaying();
16362       }
16363       else
16364         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16365
16366       game_undo_executed = FALSE;
16367
16368       break;
16369
16370     case GAME_CTRL_ID_PLAY:
16371     case GAME_CTRL_ID_PANEL_PLAY:
16372       if (game_status == GAME_MODE_MAIN)
16373       {
16374         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16375       }
16376       else if (tape.pausing)
16377       {
16378         if (network.enabled)
16379           SendToServer_ContinuePlaying();
16380         else
16381           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16382       }
16383       break;
16384
16385     case GAME_CTRL_ID_UNDO:
16386       // Important: When using "save snapshot when collecting an item" mode,
16387       // load last (current) snapshot for first "undo" after pressing "pause"
16388       // (else the last-but-one snapshot would be loaded, because the snapshot
16389       // pointer already points to the last snapshot when pressing "pause",
16390       // which is fine for "every step/move" mode, but not for "every collect")
16391       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16392           !game_undo_executed)
16393         steps--;
16394
16395       game_undo_executed = TRUE;
16396
16397       GameUndo(steps);
16398       break;
16399
16400     case GAME_CTRL_ID_REDO:
16401       GameRedo(steps);
16402       break;
16403
16404     case GAME_CTRL_ID_SAVE:
16405       TapeQuickSave();
16406       break;
16407
16408     case GAME_CTRL_ID_LOAD:
16409       TapeQuickLoad();
16410       break;
16411
16412     case SOUND_CTRL_ID_MUSIC:
16413     case SOUND_CTRL_ID_PANEL_MUSIC:
16414       if (setup.sound_music)
16415       { 
16416         setup.sound_music = FALSE;
16417
16418         FadeMusic();
16419       }
16420       else if (audio.music_available)
16421       { 
16422         setup.sound = setup.sound_music = TRUE;
16423
16424         SetAudioMode(setup.sound);
16425
16426         if (game_status == GAME_MODE_PLAYING)
16427           PlayLevelMusic();
16428       }
16429
16430       RedrawSoundButtonGadget(id);
16431
16432       break;
16433
16434     case SOUND_CTRL_ID_LOOPS:
16435     case SOUND_CTRL_ID_PANEL_LOOPS:
16436       if (setup.sound_loops)
16437         setup.sound_loops = FALSE;
16438       else if (audio.loops_available)
16439       {
16440         setup.sound = setup.sound_loops = TRUE;
16441
16442         SetAudioMode(setup.sound);
16443       }
16444
16445       RedrawSoundButtonGadget(id);
16446
16447       break;
16448
16449     case SOUND_CTRL_ID_SIMPLE:
16450     case SOUND_CTRL_ID_PANEL_SIMPLE:
16451       if (setup.sound_simple)
16452         setup.sound_simple = FALSE;
16453       else if (audio.sound_available)
16454       {
16455         setup.sound = setup.sound_simple = TRUE;
16456
16457         SetAudioMode(setup.sound);
16458       }
16459
16460       RedrawSoundButtonGadget(id);
16461
16462       break;
16463
16464     default:
16465       break;
16466   }
16467 }
16468
16469 static void HandleGameButtons(struct GadgetInfo *gi)
16470 {
16471   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16472 }
16473
16474 void HandleSoundButtonKeys(Key key)
16475 {
16476   if (key == setup.shortcut.sound_simple)
16477     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16478   else if (key == setup.shortcut.sound_loops)
16479     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16480   else if (key == setup.shortcut.sound_music)
16481     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16482 }