minor whitespace change
[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 int NewHiScore(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   int hi_pos;
4964
4965   game.LevelSolved_GameEnd = TRUE;
4966
4967   if (game.LevelSolved_SaveTape)
4968   {
4969     // make sure that request dialog to save tape does not open door again
4970     if (!global.use_envelope_request)
4971       CloseDoor(DOOR_CLOSE_1);
4972
4973     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4974
4975     // set unique basename for score tape (also saved in high score table)
4976     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
4977   }
4978
4979   // if no tape is to be saved, close both doors simultaneously
4980   CloseDoor(DOOR_CLOSE_ALL);
4981
4982   if (level_editor_test_game)
4983   {
4984     SetGameStatus(GAME_MODE_MAIN);
4985
4986     DrawMainMenu();
4987
4988     return;
4989   }
4990
4991   if (!game.LevelSolved_SaveScore)
4992   {
4993     SetGameStatus(GAME_MODE_MAIN);
4994
4995     DrawMainMenu();
4996
4997     return;
4998   }
4999
5000   if (level_nr == leveldir_current->handicap_level)
5001   {
5002     leveldir_current->handicap_level++;
5003
5004     SaveLevelSetup_SeriesInfo();
5005   }
5006
5007   if (setup.increment_levels &&
5008       level_nr < leveldir_current->last_level &&
5009       !network_playing)
5010   {
5011     level_nr++;         // advance to next level
5012     TapeErase();        // start with empty tape
5013
5014     if (setup.auto_play_next_level)
5015     {
5016       LoadLevel(level_nr);
5017
5018       SaveLevelSetup_SeriesInfo();
5019     }
5020   }
5021
5022   hi_pos = NewHiScore(last_level_nr);
5023
5024   if (hi_pos >= 0 && setup.show_scores_after_game)
5025   {
5026     SetGameStatus(GAME_MODE_SCORES);
5027
5028     DrawHallOfFame(last_level_nr, hi_pos);
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 int NewHiScore(int level_nr)
5045 {
5046   int k, l;
5047   int position = -1;
5048   boolean one_score_entry_per_name = !program.many_scores_per_name;
5049
5050   LoadScore(level_nr);
5051
5052   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5053       game.score_final < scores.entry[MAX_SCORE_ENTRIES - 1].score)
5054     return -1;
5055
5056   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5057   {
5058     struct ScoreEntry *entry = &scores.entry[k];
5059     boolean score_is_better = (game.score_final      >  entry->score);
5060     boolean score_is_equal  = (game.score_final      == entry->score);
5061     boolean time_is_better  = (game.score_time_final <  entry->time);
5062     boolean time_is_equal   = (game.score_time_final == entry->time);
5063     boolean better_by_score = (score_is_better ||
5064                                (score_is_equal && time_is_better));
5065     boolean better_by_time  = (time_is_better ||
5066                                (time_is_equal && score_is_better));
5067     boolean is_better = (level.rate_time_over_score ? better_by_time :
5068                          better_by_score);
5069     boolean entry_is_empty = (entry->score == 0 &&
5070                               entry->time == 0);
5071
5072     if (is_better || entry_is_empty)
5073     {
5074       // player has made it to the hall of fame
5075
5076       if (k < MAX_SCORE_ENTRIES - 1)
5077       {
5078         int m = MAX_SCORE_ENTRIES - 1;
5079
5080         if (one_score_entry_per_name)
5081         {
5082           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5083             if (strEqual(setup.player_name, scores.entry[l].name))
5084               m = l;
5085
5086           if (m == k)   // player's new highscore overwrites his old one
5087             goto put_into_list;
5088         }
5089
5090         for (l = m; l > k; l--)
5091         {
5092           strcpy(scores.entry[l].name, scores.entry[l - 1].name);
5093           scores.entry[l].score = scores.entry[l - 1].score;
5094           scores.entry[l].time  = scores.entry[l - 1].time;
5095         }
5096       }
5097
5098       put_into_list:
5099
5100       strncpy(entry->name, setup.player_name, MAX_PLAYER_NAME_LEN);
5101       entry->name[MAX_PLAYER_NAME_LEN] = '\0';
5102       entry->score = game.score_final;
5103       entry->time = game.score_time_final;
5104       position = k;
5105
5106       break;
5107     }
5108     else if (one_score_entry_per_name &&
5109              !strncmp(setup.player_name, entry->name, MAX_PLAYER_NAME_LEN))
5110       break;    // player already there with a higher score
5111   }
5112
5113   if (position >= 0)
5114   {
5115     SaveScoreTape(level_nr);
5116     SaveScore(level_nr);
5117   }
5118
5119   return position;
5120 }
5121
5122 static int getElementMoveStepsizeExt(int x, int y, int direction)
5123 {
5124   int element = Tile[x][y];
5125   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5126   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5127   int horiz_move = (dx != 0);
5128   int sign = (horiz_move ? dx : dy);
5129   int step = sign * element_info[element].move_stepsize;
5130
5131   // special values for move stepsize for spring and things on conveyor belt
5132   if (horiz_move)
5133   {
5134     if (CAN_FALL(element) &&
5135         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5136       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5137     else if (element == EL_SPRING)
5138       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5139   }
5140
5141   return step;
5142 }
5143
5144 static int getElementMoveStepsize(int x, int y)
5145 {
5146   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5147 }
5148
5149 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5150 {
5151   if (player->GfxAction != action || player->GfxDir != dir)
5152   {
5153     player->GfxAction = action;
5154     player->GfxDir = dir;
5155     player->Frame = 0;
5156     player->StepFrame = 0;
5157   }
5158 }
5159
5160 static void ResetGfxFrame(int x, int y)
5161 {
5162   // profiling showed that "autotest" spends 10~20% of its time in this function
5163   if (DrawingDeactivatedField())
5164     return;
5165
5166   int element = Tile[x][y];
5167   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5168
5169   if (graphic_info[graphic].anim_global_sync)
5170     GfxFrame[x][y] = FrameCounter;
5171   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5172     GfxFrame[x][y] = CustomValue[x][y];
5173   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5174     GfxFrame[x][y] = element_info[element].collect_score;
5175   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5176     GfxFrame[x][y] = ChangeDelay[x][y];
5177 }
5178
5179 static void ResetGfxAnimation(int x, int y)
5180 {
5181   GfxAction[x][y] = ACTION_DEFAULT;
5182   GfxDir[x][y] = MovDir[x][y];
5183   GfxFrame[x][y] = 0;
5184
5185   ResetGfxFrame(x, y);
5186 }
5187
5188 static void ResetRandomAnimationValue(int x, int y)
5189 {
5190   GfxRandom[x][y] = INIT_GFX_RANDOM();
5191 }
5192
5193 static void InitMovingField(int x, int y, int direction)
5194 {
5195   int element = Tile[x][y];
5196   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5197   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5198   int newx = x + dx;
5199   int newy = y + dy;
5200   boolean is_moving_before, is_moving_after;
5201
5202   // check if element was/is moving or being moved before/after mode change
5203   is_moving_before = (WasJustMoving[x][y] != 0);
5204   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5205
5206   // reset animation only for moving elements which change direction of moving
5207   // or which just started or stopped moving
5208   // (else CEs with property "can move" / "not moving" are reset each frame)
5209   if (is_moving_before != is_moving_after ||
5210       direction != MovDir[x][y])
5211     ResetGfxAnimation(x, y);
5212
5213   MovDir[x][y] = direction;
5214   GfxDir[x][y] = direction;
5215
5216   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5217                      direction == MV_DOWN && CAN_FALL(element) ?
5218                      ACTION_FALLING : ACTION_MOVING);
5219
5220   // this is needed for CEs with property "can move" / "not moving"
5221
5222   if (is_moving_after)
5223   {
5224     if (Tile[newx][newy] == EL_EMPTY)
5225       Tile[newx][newy] = EL_BLOCKED;
5226
5227     MovDir[newx][newy] = MovDir[x][y];
5228
5229     CustomValue[newx][newy] = CustomValue[x][y];
5230
5231     GfxFrame[newx][newy] = GfxFrame[x][y];
5232     GfxRandom[newx][newy] = GfxRandom[x][y];
5233     GfxAction[newx][newy] = GfxAction[x][y];
5234     GfxDir[newx][newy] = GfxDir[x][y];
5235   }
5236 }
5237
5238 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5239 {
5240   int direction = MovDir[x][y];
5241   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5242   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5243
5244   *goes_to_x = newx;
5245   *goes_to_y = newy;
5246 }
5247
5248 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5249 {
5250   int oldx = x, oldy = y;
5251   int direction = MovDir[x][y];
5252
5253   if (direction == MV_LEFT)
5254     oldx++;
5255   else if (direction == MV_RIGHT)
5256     oldx--;
5257   else if (direction == MV_UP)
5258     oldy++;
5259   else if (direction == MV_DOWN)
5260     oldy--;
5261
5262   *comes_from_x = oldx;
5263   *comes_from_y = oldy;
5264 }
5265
5266 static int MovingOrBlocked2Element(int x, int y)
5267 {
5268   int element = Tile[x][y];
5269
5270   if (element == EL_BLOCKED)
5271   {
5272     int oldx, oldy;
5273
5274     Blocked2Moving(x, y, &oldx, &oldy);
5275     return Tile[oldx][oldy];
5276   }
5277   else
5278     return element;
5279 }
5280
5281 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5282 {
5283   // like MovingOrBlocked2Element(), but if element is moving
5284   // and (x,y) is the field the moving element is just leaving,
5285   // return EL_BLOCKED instead of the element value
5286   int element = Tile[x][y];
5287
5288   if (IS_MOVING(x, y))
5289   {
5290     if (element == EL_BLOCKED)
5291     {
5292       int oldx, oldy;
5293
5294       Blocked2Moving(x, y, &oldx, &oldy);
5295       return Tile[oldx][oldy];
5296     }
5297     else
5298       return EL_BLOCKED;
5299   }
5300   else
5301     return element;
5302 }
5303
5304 static void RemoveField(int x, int y)
5305 {
5306   Tile[x][y] = EL_EMPTY;
5307
5308   MovPos[x][y] = 0;
5309   MovDir[x][y] = 0;
5310   MovDelay[x][y] = 0;
5311
5312   CustomValue[x][y] = 0;
5313
5314   AmoebaNr[x][y] = 0;
5315   ChangeDelay[x][y] = 0;
5316   ChangePage[x][y] = -1;
5317   Pushed[x][y] = FALSE;
5318
5319   GfxElement[x][y] = EL_UNDEFINED;
5320   GfxAction[x][y] = ACTION_DEFAULT;
5321   GfxDir[x][y] = MV_NONE;
5322 }
5323
5324 static void RemoveMovingField(int x, int y)
5325 {
5326   int oldx = x, oldy = y, newx = x, newy = y;
5327   int element = Tile[x][y];
5328   int next_element = EL_UNDEFINED;
5329
5330   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5331     return;
5332
5333   if (IS_MOVING(x, y))
5334   {
5335     Moving2Blocked(x, y, &newx, &newy);
5336
5337     if (Tile[newx][newy] != EL_BLOCKED)
5338     {
5339       // element is moving, but target field is not free (blocked), but
5340       // already occupied by something different (example: acid pool);
5341       // in this case, only remove the moving field, but not the target
5342
5343       RemoveField(oldx, oldy);
5344
5345       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5346
5347       TEST_DrawLevelField(oldx, oldy);
5348
5349       return;
5350     }
5351   }
5352   else if (element == EL_BLOCKED)
5353   {
5354     Blocked2Moving(x, y, &oldx, &oldy);
5355     if (!IS_MOVING(oldx, oldy))
5356       return;
5357   }
5358
5359   if (element == EL_BLOCKED &&
5360       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5361        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5362        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5363        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5364        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5365        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5366     next_element = get_next_element(Tile[oldx][oldy]);
5367
5368   RemoveField(oldx, oldy);
5369   RemoveField(newx, newy);
5370
5371   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5372
5373   if (next_element != EL_UNDEFINED)
5374     Tile[oldx][oldy] = next_element;
5375
5376   TEST_DrawLevelField(oldx, oldy);
5377   TEST_DrawLevelField(newx, newy);
5378 }
5379
5380 void DrawDynamite(int x, int y)
5381 {
5382   int sx = SCREENX(x), sy = SCREENY(y);
5383   int graphic = el2img(Tile[x][y]);
5384   int frame;
5385
5386   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5387     return;
5388
5389   if (IS_WALKABLE_INSIDE(Back[x][y]))
5390     return;
5391
5392   if (Back[x][y])
5393     DrawLevelElement(x, y, Back[x][y]);
5394   else if (Store[x][y])
5395     DrawLevelElement(x, y, Store[x][y]);
5396   else if (game.use_masked_elements)
5397     DrawLevelElement(x, y, EL_EMPTY);
5398
5399   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5400
5401   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5402     DrawGraphicThruMask(sx, sy, graphic, frame);
5403   else
5404     DrawGraphic(sx, sy, graphic, frame);
5405 }
5406
5407 static void CheckDynamite(int x, int y)
5408 {
5409   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5410   {
5411     MovDelay[x][y]--;
5412
5413     if (MovDelay[x][y] != 0)
5414     {
5415       DrawDynamite(x, y);
5416       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5417
5418       return;
5419     }
5420   }
5421
5422   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5423
5424   Bang(x, y);
5425 }
5426
5427 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5428 {
5429   boolean num_checked_players = 0;
5430   int i;
5431
5432   for (i = 0; i < MAX_PLAYERS; i++)
5433   {
5434     if (stored_player[i].active)
5435     {
5436       int sx = stored_player[i].jx;
5437       int sy = stored_player[i].jy;
5438
5439       if (num_checked_players == 0)
5440       {
5441         *sx1 = *sx2 = sx;
5442         *sy1 = *sy2 = sy;
5443       }
5444       else
5445       {
5446         *sx1 = MIN(*sx1, sx);
5447         *sy1 = MIN(*sy1, sy);
5448         *sx2 = MAX(*sx2, sx);
5449         *sy2 = MAX(*sy2, sy);
5450       }
5451
5452       num_checked_players++;
5453     }
5454   }
5455 }
5456
5457 static boolean checkIfAllPlayersFitToScreen_RND(void)
5458 {
5459   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5460
5461   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5462
5463   return (sx2 - sx1 < SCR_FIELDX &&
5464           sy2 - sy1 < SCR_FIELDY);
5465 }
5466
5467 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5468 {
5469   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5470
5471   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5472
5473   *sx = (sx1 + sx2) / 2;
5474   *sy = (sy1 + sy2) / 2;
5475 }
5476
5477 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5478                                boolean center_screen, boolean quick_relocation)
5479 {
5480   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5481   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5482   boolean no_delay = (tape.warp_forward);
5483   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5484   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5485   int new_scroll_x, new_scroll_y;
5486
5487   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5488   {
5489     // case 1: quick relocation inside visible screen (without scrolling)
5490
5491     RedrawPlayfield();
5492
5493     return;
5494   }
5495
5496   if (!level.shifted_relocation || center_screen)
5497   {
5498     // relocation _with_ centering of screen
5499
5500     new_scroll_x = SCROLL_POSITION_X(x);
5501     new_scroll_y = SCROLL_POSITION_Y(y);
5502   }
5503   else
5504   {
5505     // relocation _without_ centering of screen
5506
5507     int center_scroll_x = SCROLL_POSITION_X(old_x);
5508     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5509     int offset_x = x + (scroll_x - center_scroll_x);
5510     int offset_y = y + (scroll_y - center_scroll_y);
5511
5512     // for new screen position, apply previous offset to center position
5513     new_scroll_x = SCROLL_POSITION_X(offset_x);
5514     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5515   }
5516
5517   if (quick_relocation)
5518   {
5519     // case 2: quick relocation (redraw without visible scrolling)
5520
5521     scroll_x = new_scroll_x;
5522     scroll_y = new_scroll_y;
5523
5524     RedrawPlayfield();
5525
5526     return;
5527   }
5528
5529   // case 3: visible relocation (with scrolling to new position)
5530
5531   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5532
5533   SetVideoFrameDelay(wait_delay_value);
5534
5535   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5536   {
5537     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5538     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5539
5540     if (dx == 0 && dy == 0)             // no scrolling needed at all
5541       break;
5542
5543     scroll_x -= dx;
5544     scroll_y -= dy;
5545
5546     // set values for horizontal/vertical screen scrolling (half tile size)
5547     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5548     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5549     int pos_x = dx * TILEX / 2;
5550     int pos_y = dy * TILEY / 2;
5551     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5552     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5553
5554     ScrollLevel(dx, dy);
5555     DrawAllPlayers();
5556
5557     // scroll in two steps of half tile size to make things smoother
5558     BlitScreenToBitmapExt_RND(window, fx, fy);
5559
5560     // scroll second step to align at full tile size
5561     BlitScreenToBitmap(window);
5562   }
5563
5564   DrawAllPlayers();
5565   BackToFront();
5566
5567   SetVideoFrameDelay(frame_delay_value_old);
5568 }
5569
5570 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5571 {
5572   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5573   int player_nr = GET_PLAYER_NR(el_player);
5574   struct PlayerInfo *player = &stored_player[player_nr];
5575   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5576   boolean no_delay = (tape.warp_forward);
5577   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5578   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5579   int old_jx = player->jx;
5580   int old_jy = player->jy;
5581   int old_element = Tile[old_jx][old_jy];
5582   int element = Tile[jx][jy];
5583   boolean player_relocated = (old_jx != jx || old_jy != jy);
5584
5585   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5586   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5587   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5588   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5589   int leave_side_horiz = move_dir_horiz;
5590   int leave_side_vert  = move_dir_vert;
5591   int enter_side = enter_side_horiz | enter_side_vert;
5592   int leave_side = leave_side_horiz | leave_side_vert;
5593
5594   if (player->buried)           // do not reanimate dead player
5595     return;
5596
5597   if (!player_relocated)        // no need to relocate the player
5598     return;
5599
5600   if (IS_PLAYER(jx, jy))        // player already placed at new position
5601   {
5602     RemoveField(jx, jy);        // temporarily remove newly placed player
5603     DrawLevelField(jx, jy);
5604   }
5605
5606   if (player->present)
5607   {
5608     while (player->MovPos)
5609     {
5610       ScrollPlayer(player, SCROLL_GO_ON);
5611       ScrollScreen(NULL, SCROLL_GO_ON);
5612
5613       AdvanceFrameAndPlayerCounters(player->index_nr);
5614
5615       DrawPlayer(player);
5616
5617       BackToFront_WithFrameDelay(wait_delay_value);
5618     }
5619
5620     DrawPlayer(player);         // needed here only to cleanup last field
5621     DrawLevelField(player->jx, player->jy);     // remove player graphic
5622
5623     player->is_moving = FALSE;
5624   }
5625
5626   if (IS_CUSTOM_ELEMENT(old_element))
5627     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5628                                CE_LEFT_BY_PLAYER,
5629                                player->index_bit, leave_side);
5630
5631   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5632                                       CE_PLAYER_LEAVES_X,
5633                                       player->index_bit, leave_side);
5634
5635   Tile[jx][jy] = el_player;
5636   InitPlayerField(jx, jy, el_player, TRUE);
5637
5638   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5639      possible that the relocation target field did not contain a player element,
5640      but a walkable element, to which the new player was relocated -- in this
5641      case, restore that (already initialized!) element on the player field */
5642   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5643   {
5644     Tile[jx][jy] = element;     // restore previously existing element
5645   }
5646
5647   // only visually relocate centered player
5648   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5649                      FALSE, level.instant_relocation);
5650
5651   TestIfPlayerTouchesBadThing(jx, jy);
5652   TestIfPlayerTouchesCustomElement(jx, jy);
5653
5654   if (IS_CUSTOM_ELEMENT(element))
5655     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5656                                player->index_bit, enter_side);
5657
5658   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5659                                       player->index_bit, enter_side);
5660
5661   if (player->is_switching)
5662   {
5663     /* ensure that relocation while still switching an element does not cause
5664        a new element to be treated as also switched directly after relocation
5665        (this is important for teleporter switches that teleport the player to
5666        a place where another teleporter switch is in the same direction, which
5667        would then incorrectly be treated as immediately switched before the
5668        direction key that caused the switch was released) */
5669
5670     player->switch_x += jx - old_jx;
5671     player->switch_y += jy - old_jy;
5672   }
5673 }
5674
5675 static void Explode(int ex, int ey, int phase, int mode)
5676 {
5677   int x, y;
5678   int last_phase;
5679   int border_element;
5680
5681   // !!! eliminate this variable !!!
5682   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5683
5684   if (game.explosions_delayed)
5685   {
5686     ExplodeField[ex][ey] = mode;
5687     return;
5688   }
5689
5690   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5691   {
5692     int center_element = Tile[ex][ey];
5693     int artwork_element, explosion_element;     // set these values later
5694
5695     // remove things displayed in background while burning dynamite
5696     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5697       Back[ex][ey] = 0;
5698
5699     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5700     {
5701       // put moving element to center field (and let it explode there)
5702       center_element = MovingOrBlocked2Element(ex, ey);
5703       RemoveMovingField(ex, ey);
5704       Tile[ex][ey] = center_element;
5705     }
5706
5707     // now "center_element" is finally determined -- set related values now
5708     artwork_element = center_element;           // for custom player artwork
5709     explosion_element = center_element;         // for custom player artwork
5710
5711     if (IS_PLAYER(ex, ey))
5712     {
5713       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5714
5715       artwork_element = stored_player[player_nr].artwork_element;
5716
5717       if (level.use_explosion_element[player_nr])
5718       {
5719         explosion_element = level.explosion_element[player_nr];
5720         artwork_element = explosion_element;
5721       }
5722     }
5723
5724     if (mode == EX_TYPE_NORMAL ||
5725         mode == EX_TYPE_CENTER ||
5726         mode == EX_TYPE_CROSS)
5727       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5728
5729     last_phase = element_info[explosion_element].explosion_delay + 1;
5730
5731     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5732     {
5733       int xx = x - ex + 1;
5734       int yy = y - ey + 1;
5735       int element;
5736
5737       if (!IN_LEV_FIELD(x, y) ||
5738           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5739           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5740         continue;
5741
5742       element = Tile[x][y];
5743
5744       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5745       {
5746         element = MovingOrBlocked2Element(x, y);
5747
5748         if (!IS_EXPLOSION_PROOF(element))
5749           RemoveMovingField(x, y);
5750       }
5751
5752       // indestructible elements can only explode in center (but not flames)
5753       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5754                                            mode == EX_TYPE_BORDER)) ||
5755           element == EL_FLAMES)
5756         continue;
5757
5758       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5759          behaviour, for example when touching a yamyam that explodes to rocks
5760          with active deadly shield, a rock is created under the player !!! */
5761       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5762 #if 0
5763       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5764           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5765            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5766 #else
5767       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5768 #endif
5769       {
5770         if (IS_ACTIVE_BOMB(element))
5771         {
5772           // re-activate things under the bomb like gate or penguin
5773           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5774           Back[x][y] = 0;
5775         }
5776
5777         continue;
5778       }
5779
5780       // save walkable background elements while explosion on same tile
5781       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5782           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5783         Back[x][y] = element;
5784
5785       // ignite explodable elements reached by other explosion
5786       if (element == EL_EXPLOSION)
5787         element = Store2[x][y];
5788
5789       if (AmoebaNr[x][y] &&
5790           (element == EL_AMOEBA_FULL ||
5791            element == EL_BD_AMOEBA ||
5792            element == EL_AMOEBA_GROWING))
5793       {
5794         AmoebaCnt[AmoebaNr[x][y]]--;
5795         AmoebaCnt2[AmoebaNr[x][y]]--;
5796       }
5797
5798       RemoveField(x, y);
5799
5800       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5801       {
5802         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5803
5804         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5805
5806         if (PLAYERINFO(ex, ey)->use_murphy)
5807           Store[x][y] = EL_EMPTY;
5808       }
5809
5810       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5811       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5812       else if (ELEM_IS_PLAYER(center_element))
5813         Store[x][y] = EL_EMPTY;
5814       else if (center_element == EL_YAMYAM)
5815         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5816       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5817         Store[x][y] = element_info[center_element].content.e[xx][yy];
5818 #if 1
5819       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5820       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5821       // otherwise) -- FIX THIS !!!
5822       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5823         Store[x][y] = element_info[element].content.e[1][1];
5824 #else
5825       else if (!CAN_EXPLODE(element))
5826         Store[x][y] = element_info[element].content.e[1][1];
5827 #endif
5828       else
5829         Store[x][y] = EL_EMPTY;
5830
5831       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5832           center_element == EL_AMOEBA_TO_DIAMOND)
5833         Store2[x][y] = element;
5834
5835       Tile[x][y] = EL_EXPLOSION;
5836       GfxElement[x][y] = artwork_element;
5837
5838       ExplodePhase[x][y] = 1;
5839       ExplodeDelay[x][y] = last_phase;
5840
5841       Stop[x][y] = TRUE;
5842     }
5843
5844     if (center_element == EL_YAMYAM)
5845       game.yamyam_content_nr =
5846         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5847
5848     return;
5849   }
5850
5851   if (Stop[ex][ey])
5852     return;
5853
5854   x = ex;
5855   y = ey;
5856
5857   if (phase == 1)
5858     GfxFrame[x][y] = 0;         // restart explosion animation
5859
5860   last_phase = ExplodeDelay[x][y];
5861
5862   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5863
5864   // this can happen if the player leaves an explosion just in time
5865   if (GfxElement[x][y] == EL_UNDEFINED)
5866     GfxElement[x][y] = EL_EMPTY;
5867
5868   border_element = Store2[x][y];
5869   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5870     border_element = StorePlayer[x][y];
5871
5872   if (phase == element_info[border_element].ignition_delay ||
5873       phase == last_phase)
5874   {
5875     boolean border_explosion = FALSE;
5876
5877     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5878         !PLAYER_EXPLOSION_PROTECTED(x, y))
5879     {
5880       KillPlayerUnlessExplosionProtected(x, y);
5881       border_explosion = TRUE;
5882     }
5883     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5884     {
5885       Tile[x][y] = Store2[x][y];
5886       Store2[x][y] = 0;
5887       Bang(x, y);
5888       border_explosion = TRUE;
5889     }
5890     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5891     {
5892       AmoebaToDiamond(x, y);
5893       Store2[x][y] = 0;
5894       border_explosion = TRUE;
5895     }
5896
5897     // if an element just explodes due to another explosion (chain-reaction),
5898     // do not immediately end the new explosion when it was the last frame of
5899     // the explosion (as it would be done in the following "if"-statement!)
5900     if (border_explosion && phase == last_phase)
5901       return;
5902   }
5903
5904   if (phase == last_phase)
5905   {
5906     int element;
5907
5908     element = Tile[x][y] = Store[x][y];
5909     Store[x][y] = Store2[x][y] = 0;
5910     GfxElement[x][y] = EL_UNDEFINED;
5911
5912     // player can escape from explosions and might therefore be still alive
5913     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5914         element <= EL_PLAYER_IS_EXPLODING_4)
5915     {
5916       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5917       int explosion_element = EL_PLAYER_1 + player_nr;
5918       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5919       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5920
5921       if (level.use_explosion_element[player_nr])
5922         explosion_element = level.explosion_element[player_nr];
5923
5924       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5925                     element_info[explosion_element].content.e[xx][yy]);
5926     }
5927
5928     // restore probably existing indestructible background element
5929     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5930       element = Tile[x][y] = Back[x][y];
5931     Back[x][y] = 0;
5932
5933     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5934     GfxDir[x][y] = MV_NONE;
5935     ChangeDelay[x][y] = 0;
5936     ChangePage[x][y] = -1;
5937
5938     CustomValue[x][y] = 0;
5939
5940     InitField_WithBug2(x, y, FALSE);
5941
5942     TEST_DrawLevelField(x, y);
5943
5944     TestIfElementTouchesCustomElement(x, y);
5945
5946     if (GFX_CRUMBLED(element))
5947       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5948
5949     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5950       StorePlayer[x][y] = 0;
5951
5952     if (ELEM_IS_PLAYER(element))
5953       RelocatePlayer(x, y, element);
5954   }
5955   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5956   {
5957     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5958     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5959
5960     if (phase == delay)
5961       TEST_DrawLevelFieldCrumbled(x, y);
5962
5963     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5964     {
5965       DrawLevelElement(x, y, Back[x][y]);
5966       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5967     }
5968     else if (IS_WALKABLE_UNDER(Back[x][y]))
5969     {
5970       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5971       DrawLevelElementThruMask(x, y, Back[x][y]);
5972     }
5973     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5974       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5975   }
5976 }
5977
5978 static void DynaExplode(int ex, int ey)
5979 {
5980   int i, j;
5981   int dynabomb_element = Tile[ex][ey];
5982   int dynabomb_size = 1;
5983   boolean dynabomb_xl = FALSE;
5984   struct PlayerInfo *player;
5985   static int xy[4][2] =
5986   {
5987     { 0, -1 },
5988     { -1, 0 },
5989     { +1, 0 },
5990     { 0, +1 }
5991   };
5992
5993   if (IS_ACTIVE_BOMB(dynabomb_element))
5994   {
5995     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5996     dynabomb_size = player->dynabomb_size;
5997     dynabomb_xl = player->dynabomb_xl;
5998     player->dynabombs_left++;
5999   }
6000
6001   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6002
6003   for (i = 0; i < NUM_DIRECTIONS; i++)
6004   {
6005     for (j = 1; j <= dynabomb_size; j++)
6006     {
6007       int x = ex + j * xy[i][0];
6008       int y = ey + j * xy[i][1];
6009       int element;
6010
6011       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6012         break;
6013
6014       element = Tile[x][y];
6015
6016       // do not restart explosions of fields with active bombs
6017       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6018         continue;
6019
6020       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6021
6022       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6023           !IS_DIGGABLE(element) && !dynabomb_xl)
6024         break;
6025     }
6026   }
6027 }
6028
6029 void Bang(int x, int y)
6030 {
6031   int element = MovingOrBlocked2Element(x, y);
6032   int explosion_type = EX_TYPE_NORMAL;
6033
6034   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6035   {
6036     struct PlayerInfo *player = PLAYERINFO(x, y);
6037
6038     element = Tile[x][y] = player->initial_element;
6039
6040     if (level.use_explosion_element[player->index_nr])
6041     {
6042       int explosion_element = level.explosion_element[player->index_nr];
6043
6044       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6045         explosion_type = EX_TYPE_CROSS;
6046       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6047         explosion_type = EX_TYPE_CENTER;
6048     }
6049   }
6050
6051   switch (element)
6052   {
6053     case EL_BUG:
6054     case EL_SPACESHIP:
6055     case EL_BD_BUTTERFLY:
6056     case EL_BD_FIREFLY:
6057     case EL_YAMYAM:
6058     case EL_DARK_YAMYAM:
6059     case EL_ROBOT:
6060     case EL_PACMAN:
6061     case EL_MOLE:
6062       RaiseScoreElement(element);
6063       break;
6064
6065     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6066     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6067     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6068     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6069     case EL_DYNABOMB_INCREASE_NUMBER:
6070     case EL_DYNABOMB_INCREASE_SIZE:
6071     case EL_DYNABOMB_INCREASE_POWER:
6072       explosion_type = EX_TYPE_DYNA;
6073       break;
6074
6075     case EL_DC_LANDMINE:
6076       explosion_type = EX_TYPE_CENTER;
6077       break;
6078
6079     case EL_PENGUIN:
6080     case EL_LAMP:
6081     case EL_LAMP_ACTIVE:
6082     case EL_AMOEBA_TO_DIAMOND:
6083       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6084         explosion_type = EX_TYPE_CENTER;
6085       break;
6086
6087     default:
6088       if (element_info[element].explosion_type == EXPLODES_CROSS)
6089         explosion_type = EX_TYPE_CROSS;
6090       else if (element_info[element].explosion_type == EXPLODES_1X1)
6091         explosion_type = EX_TYPE_CENTER;
6092       break;
6093   }
6094
6095   if (explosion_type == EX_TYPE_DYNA)
6096     DynaExplode(x, y);
6097   else
6098     Explode(x, y, EX_PHASE_START, explosion_type);
6099
6100   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6101 }
6102
6103 static void SplashAcid(int x, int y)
6104 {
6105   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6106       (!IN_LEV_FIELD(x - 1, y - 2) ||
6107        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6108     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6109
6110   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6111       (!IN_LEV_FIELD(x + 1, y - 2) ||
6112        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6113     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6114
6115   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6116 }
6117
6118 static void InitBeltMovement(void)
6119 {
6120   static int belt_base_element[4] =
6121   {
6122     EL_CONVEYOR_BELT_1_LEFT,
6123     EL_CONVEYOR_BELT_2_LEFT,
6124     EL_CONVEYOR_BELT_3_LEFT,
6125     EL_CONVEYOR_BELT_4_LEFT
6126   };
6127   static int belt_base_active_element[4] =
6128   {
6129     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6130     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6131     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6132     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6133   };
6134
6135   int x, y, i, j;
6136
6137   // set frame order for belt animation graphic according to belt direction
6138   for (i = 0; i < NUM_BELTS; i++)
6139   {
6140     int belt_nr = i;
6141
6142     for (j = 0; j < NUM_BELT_PARTS; j++)
6143     {
6144       int element = belt_base_active_element[belt_nr] + j;
6145       int graphic_1 = el2img(element);
6146       int graphic_2 = el2panelimg(element);
6147
6148       if (game.belt_dir[i] == MV_LEFT)
6149       {
6150         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6151         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6152       }
6153       else
6154       {
6155         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6156         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6157       }
6158     }
6159   }
6160
6161   SCAN_PLAYFIELD(x, y)
6162   {
6163     int element = Tile[x][y];
6164
6165     for (i = 0; i < NUM_BELTS; i++)
6166     {
6167       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6168       {
6169         int e_belt_nr = getBeltNrFromBeltElement(element);
6170         int belt_nr = i;
6171
6172         if (e_belt_nr == belt_nr)
6173         {
6174           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6175
6176           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6177         }
6178       }
6179     }
6180   }
6181 }
6182
6183 static void ToggleBeltSwitch(int x, int y)
6184 {
6185   static int belt_base_element[4] =
6186   {
6187     EL_CONVEYOR_BELT_1_LEFT,
6188     EL_CONVEYOR_BELT_2_LEFT,
6189     EL_CONVEYOR_BELT_3_LEFT,
6190     EL_CONVEYOR_BELT_4_LEFT
6191   };
6192   static int belt_base_active_element[4] =
6193   {
6194     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6195     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6196     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6197     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6198   };
6199   static int belt_base_switch_element[4] =
6200   {
6201     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6202     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6203     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6204     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6205   };
6206   static int belt_move_dir[4] =
6207   {
6208     MV_LEFT,
6209     MV_NONE,
6210     MV_RIGHT,
6211     MV_NONE,
6212   };
6213
6214   int element = Tile[x][y];
6215   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6216   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6217   int belt_dir = belt_move_dir[belt_dir_nr];
6218   int xx, yy, i;
6219
6220   if (!IS_BELT_SWITCH(element))
6221     return;
6222
6223   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6224   game.belt_dir[belt_nr] = belt_dir;
6225
6226   if (belt_dir_nr == 3)
6227     belt_dir_nr = 1;
6228
6229   // set frame order for belt animation graphic according to belt direction
6230   for (i = 0; i < NUM_BELT_PARTS; i++)
6231   {
6232     int element = belt_base_active_element[belt_nr] + i;
6233     int graphic_1 = el2img(element);
6234     int graphic_2 = el2panelimg(element);
6235
6236     if (belt_dir == MV_LEFT)
6237     {
6238       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6239       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6240     }
6241     else
6242     {
6243       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6244       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6245     }
6246   }
6247
6248   SCAN_PLAYFIELD(xx, yy)
6249   {
6250     int element = Tile[xx][yy];
6251
6252     if (IS_BELT_SWITCH(element))
6253     {
6254       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6255
6256       if (e_belt_nr == belt_nr)
6257       {
6258         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6259         TEST_DrawLevelField(xx, yy);
6260       }
6261     }
6262     else if (IS_BELT(element) && belt_dir != MV_NONE)
6263     {
6264       int e_belt_nr = getBeltNrFromBeltElement(element);
6265
6266       if (e_belt_nr == belt_nr)
6267       {
6268         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6269
6270         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6271         TEST_DrawLevelField(xx, yy);
6272       }
6273     }
6274     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6275     {
6276       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6277
6278       if (e_belt_nr == belt_nr)
6279       {
6280         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6281
6282         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6283         TEST_DrawLevelField(xx, yy);
6284       }
6285     }
6286   }
6287 }
6288
6289 static void ToggleSwitchgateSwitch(int x, int y)
6290 {
6291   int xx, yy;
6292
6293   game.switchgate_pos = !game.switchgate_pos;
6294
6295   SCAN_PLAYFIELD(xx, yy)
6296   {
6297     int element = Tile[xx][yy];
6298
6299     if (element == EL_SWITCHGATE_SWITCH_UP)
6300     {
6301       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6302       TEST_DrawLevelField(xx, yy);
6303     }
6304     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6305     {
6306       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6307       TEST_DrawLevelField(xx, yy);
6308     }
6309     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6310     {
6311       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6312       TEST_DrawLevelField(xx, yy);
6313     }
6314     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6315     {
6316       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6317       TEST_DrawLevelField(xx, yy);
6318     }
6319     else if (element == EL_SWITCHGATE_OPEN ||
6320              element == EL_SWITCHGATE_OPENING)
6321     {
6322       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6323
6324       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6325     }
6326     else if (element == EL_SWITCHGATE_CLOSED ||
6327              element == EL_SWITCHGATE_CLOSING)
6328     {
6329       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6330
6331       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6332     }
6333   }
6334 }
6335
6336 static int getInvisibleActiveFromInvisibleElement(int element)
6337 {
6338   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6339           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6340           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6341           element);
6342 }
6343
6344 static int getInvisibleFromInvisibleActiveElement(int element)
6345 {
6346   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6347           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6348           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6349           element);
6350 }
6351
6352 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6353 {
6354   int x, y;
6355
6356   SCAN_PLAYFIELD(x, y)
6357   {
6358     int element = Tile[x][y];
6359
6360     if (element == EL_LIGHT_SWITCH &&
6361         game.light_time_left > 0)
6362     {
6363       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6364       TEST_DrawLevelField(x, y);
6365     }
6366     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6367              game.light_time_left == 0)
6368     {
6369       Tile[x][y] = EL_LIGHT_SWITCH;
6370       TEST_DrawLevelField(x, y);
6371     }
6372     else if (element == EL_EMC_DRIPPER &&
6373              game.light_time_left > 0)
6374     {
6375       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6376       TEST_DrawLevelField(x, y);
6377     }
6378     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6379              game.light_time_left == 0)
6380     {
6381       Tile[x][y] = EL_EMC_DRIPPER;
6382       TEST_DrawLevelField(x, y);
6383     }
6384     else if (element == EL_INVISIBLE_STEELWALL ||
6385              element == EL_INVISIBLE_WALL ||
6386              element == EL_INVISIBLE_SAND)
6387     {
6388       if (game.light_time_left > 0)
6389         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6390
6391       TEST_DrawLevelField(x, y);
6392
6393       // uncrumble neighbour fields, if needed
6394       if (element == EL_INVISIBLE_SAND)
6395         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6396     }
6397     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6398              element == EL_INVISIBLE_WALL_ACTIVE ||
6399              element == EL_INVISIBLE_SAND_ACTIVE)
6400     {
6401       if (game.light_time_left == 0)
6402         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6403
6404       TEST_DrawLevelField(x, y);
6405
6406       // re-crumble neighbour fields, if needed
6407       if (element == EL_INVISIBLE_SAND)
6408         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6409     }
6410   }
6411 }
6412
6413 static void RedrawAllInvisibleElementsForLenses(void)
6414 {
6415   int x, y;
6416
6417   SCAN_PLAYFIELD(x, y)
6418   {
6419     int element = Tile[x][y];
6420
6421     if (element == EL_EMC_DRIPPER &&
6422         game.lenses_time_left > 0)
6423     {
6424       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6425       TEST_DrawLevelField(x, y);
6426     }
6427     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6428              game.lenses_time_left == 0)
6429     {
6430       Tile[x][y] = EL_EMC_DRIPPER;
6431       TEST_DrawLevelField(x, y);
6432     }
6433     else if (element == EL_INVISIBLE_STEELWALL ||
6434              element == EL_INVISIBLE_WALL ||
6435              element == EL_INVISIBLE_SAND)
6436     {
6437       if (game.lenses_time_left > 0)
6438         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6439
6440       TEST_DrawLevelField(x, y);
6441
6442       // uncrumble neighbour fields, if needed
6443       if (element == EL_INVISIBLE_SAND)
6444         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6445     }
6446     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6447              element == EL_INVISIBLE_WALL_ACTIVE ||
6448              element == EL_INVISIBLE_SAND_ACTIVE)
6449     {
6450       if (game.lenses_time_left == 0)
6451         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6452
6453       TEST_DrawLevelField(x, y);
6454
6455       // re-crumble neighbour fields, if needed
6456       if (element == EL_INVISIBLE_SAND)
6457         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6458     }
6459   }
6460 }
6461
6462 static void RedrawAllInvisibleElementsForMagnifier(void)
6463 {
6464   int x, y;
6465
6466   SCAN_PLAYFIELD(x, y)
6467   {
6468     int element = Tile[x][y];
6469
6470     if (element == EL_EMC_FAKE_GRASS &&
6471         game.magnify_time_left > 0)
6472     {
6473       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6474       TEST_DrawLevelField(x, y);
6475     }
6476     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6477              game.magnify_time_left == 0)
6478     {
6479       Tile[x][y] = EL_EMC_FAKE_GRASS;
6480       TEST_DrawLevelField(x, y);
6481     }
6482     else if (IS_GATE_GRAY(element) &&
6483              game.magnify_time_left > 0)
6484     {
6485       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6486                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6487                     IS_EM_GATE_GRAY(element) ?
6488                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6489                     IS_EMC_GATE_GRAY(element) ?
6490                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6491                     IS_DC_GATE_GRAY(element) ?
6492                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6493                     element);
6494       TEST_DrawLevelField(x, y);
6495     }
6496     else if (IS_GATE_GRAY_ACTIVE(element) &&
6497              game.magnify_time_left == 0)
6498     {
6499       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6500                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6501                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6502                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6503                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6504                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6505                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6506                     EL_DC_GATE_WHITE_GRAY :
6507                     element);
6508       TEST_DrawLevelField(x, y);
6509     }
6510   }
6511 }
6512
6513 static void ToggleLightSwitch(int x, int y)
6514 {
6515   int element = Tile[x][y];
6516
6517   game.light_time_left =
6518     (element == EL_LIGHT_SWITCH ?
6519      level.time_light * FRAMES_PER_SECOND : 0);
6520
6521   RedrawAllLightSwitchesAndInvisibleElements();
6522 }
6523
6524 static void ActivateTimegateSwitch(int x, int y)
6525 {
6526   int xx, yy;
6527
6528   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6529
6530   SCAN_PLAYFIELD(xx, yy)
6531   {
6532     int element = Tile[xx][yy];
6533
6534     if (element == EL_TIMEGATE_CLOSED ||
6535         element == EL_TIMEGATE_CLOSING)
6536     {
6537       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6538       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6539     }
6540
6541     /*
6542     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6543     {
6544       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6545       TEST_DrawLevelField(xx, yy);
6546     }
6547     */
6548
6549   }
6550
6551   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6552                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6553 }
6554
6555 static void Impact(int x, int y)
6556 {
6557   boolean last_line = (y == lev_fieldy - 1);
6558   boolean object_hit = FALSE;
6559   boolean impact = (last_line || object_hit);
6560   int element = Tile[x][y];
6561   int smashed = EL_STEELWALL;
6562
6563   if (!last_line)       // check if element below was hit
6564   {
6565     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6566       return;
6567
6568     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6569                                          MovDir[x][y + 1] != MV_DOWN ||
6570                                          MovPos[x][y + 1] <= TILEY / 2));
6571
6572     // do not smash moving elements that left the smashed field in time
6573     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6574         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6575       object_hit = FALSE;
6576
6577 #if USE_QUICKSAND_IMPACT_BUGFIX
6578     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6579     {
6580       RemoveMovingField(x, y + 1);
6581       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6582       Tile[x][y + 2] = EL_ROCK;
6583       TEST_DrawLevelField(x, y + 2);
6584
6585       object_hit = TRUE;
6586     }
6587
6588     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6589     {
6590       RemoveMovingField(x, y + 1);
6591       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6592       Tile[x][y + 2] = EL_ROCK;
6593       TEST_DrawLevelField(x, y + 2);
6594
6595       object_hit = TRUE;
6596     }
6597 #endif
6598
6599     if (object_hit)
6600       smashed = MovingOrBlocked2Element(x, y + 1);
6601
6602     impact = (last_line || object_hit);
6603   }
6604
6605   if (!last_line && smashed == EL_ACID) // element falls into acid
6606   {
6607     SplashAcid(x, y + 1);
6608     return;
6609   }
6610
6611   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6612   // only reset graphic animation if graphic really changes after impact
6613   if (impact &&
6614       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6615   {
6616     ResetGfxAnimation(x, y);
6617     TEST_DrawLevelField(x, y);
6618   }
6619
6620   if (impact && CAN_EXPLODE_IMPACT(element))
6621   {
6622     Bang(x, y);
6623     return;
6624   }
6625   else if (impact && element == EL_PEARL &&
6626            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6627   {
6628     ResetGfxAnimation(x, y);
6629
6630     Tile[x][y] = EL_PEARL_BREAKING;
6631     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6632     return;
6633   }
6634   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6635   {
6636     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6637
6638     return;
6639   }
6640
6641   if (impact && element == EL_AMOEBA_DROP)
6642   {
6643     if (object_hit && IS_PLAYER(x, y + 1))
6644       KillPlayerUnlessEnemyProtected(x, y + 1);
6645     else if (object_hit && smashed == EL_PENGUIN)
6646       Bang(x, y + 1);
6647     else
6648     {
6649       Tile[x][y] = EL_AMOEBA_GROWING;
6650       Store[x][y] = EL_AMOEBA_WET;
6651
6652       ResetRandomAnimationValue(x, y);
6653     }
6654     return;
6655   }
6656
6657   if (object_hit)               // check which object was hit
6658   {
6659     if ((CAN_PASS_MAGIC_WALL(element) && 
6660          (smashed == EL_MAGIC_WALL ||
6661           smashed == EL_BD_MAGIC_WALL)) ||
6662         (CAN_PASS_DC_MAGIC_WALL(element) &&
6663          smashed == EL_DC_MAGIC_WALL))
6664     {
6665       int xx, yy;
6666       int activated_magic_wall =
6667         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6668          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6669          EL_DC_MAGIC_WALL_ACTIVE);
6670
6671       // activate magic wall / mill
6672       SCAN_PLAYFIELD(xx, yy)
6673       {
6674         if (Tile[xx][yy] == smashed)
6675           Tile[xx][yy] = activated_magic_wall;
6676       }
6677
6678       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6679       game.magic_wall_active = TRUE;
6680
6681       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6682                             SND_MAGIC_WALL_ACTIVATING :
6683                             smashed == EL_BD_MAGIC_WALL ?
6684                             SND_BD_MAGIC_WALL_ACTIVATING :
6685                             SND_DC_MAGIC_WALL_ACTIVATING));
6686     }
6687
6688     if (IS_PLAYER(x, y + 1))
6689     {
6690       if (CAN_SMASH_PLAYER(element))
6691       {
6692         KillPlayerUnlessEnemyProtected(x, y + 1);
6693         return;
6694       }
6695     }
6696     else if (smashed == EL_PENGUIN)
6697     {
6698       if (CAN_SMASH_PLAYER(element))
6699       {
6700         Bang(x, y + 1);
6701         return;
6702       }
6703     }
6704     else if (element == EL_BD_DIAMOND)
6705     {
6706       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6707       {
6708         Bang(x, y + 1);
6709         return;
6710       }
6711     }
6712     else if (((element == EL_SP_INFOTRON ||
6713                element == EL_SP_ZONK) &&
6714               (smashed == EL_SP_SNIKSNAK ||
6715                smashed == EL_SP_ELECTRON ||
6716                smashed == EL_SP_DISK_ORANGE)) ||
6717              (element == EL_SP_INFOTRON &&
6718               smashed == EL_SP_DISK_YELLOW))
6719     {
6720       Bang(x, y + 1);
6721       return;
6722     }
6723     else if (CAN_SMASH_EVERYTHING(element))
6724     {
6725       if (IS_CLASSIC_ENEMY(smashed) ||
6726           CAN_EXPLODE_SMASHED(smashed))
6727       {
6728         Bang(x, y + 1);
6729         return;
6730       }
6731       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6732       {
6733         if (smashed == EL_LAMP ||
6734             smashed == EL_LAMP_ACTIVE)
6735         {
6736           Bang(x, y + 1);
6737           return;
6738         }
6739         else if (smashed == EL_NUT)
6740         {
6741           Tile[x][y + 1] = EL_NUT_BREAKING;
6742           PlayLevelSound(x, y, SND_NUT_BREAKING);
6743           RaiseScoreElement(EL_NUT);
6744           return;
6745         }
6746         else if (smashed == EL_PEARL)
6747         {
6748           ResetGfxAnimation(x, y);
6749
6750           Tile[x][y + 1] = EL_PEARL_BREAKING;
6751           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6752           return;
6753         }
6754         else if (smashed == EL_DIAMOND)
6755         {
6756           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6757           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6758           return;
6759         }
6760         else if (IS_BELT_SWITCH(smashed))
6761         {
6762           ToggleBeltSwitch(x, y + 1);
6763         }
6764         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6765                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6766                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6767                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6768         {
6769           ToggleSwitchgateSwitch(x, y + 1);
6770         }
6771         else if (smashed == EL_LIGHT_SWITCH ||
6772                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6773         {
6774           ToggleLightSwitch(x, y + 1);
6775         }
6776         else
6777         {
6778           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6779
6780           CheckElementChangeBySide(x, y + 1, smashed, element,
6781                                    CE_SWITCHED, CH_SIDE_TOP);
6782           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6783                                             CH_SIDE_TOP);
6784         }
6785       }
6786       else
6787       {
6788         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6789       }
6790     }
6791   }
6792
6793   // play sound of magic wall / mill
6794   if (!last_line &&
6795       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6796        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6797        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6798   {
6799     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6800       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6801     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6802       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6803     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6804       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6805
6806     return;
6807   }
6808
6809   // play sound of object that hits the ground
6810   if (last_line || object_hit)
6811     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6812 }
6813
6814 static void TurnRoundExt(int x, int y)
6815 {
6816   static struct
6817   {
6818     int dx, dy;
6819   } move_xy[] =
6820   {
6821     {  0,  0 },
6822     { -1,  0 },
6823     { +1,  0 },
6824     {  0,  0 },
6825     {  0, -1 },
6826     {  0,  0 }, { 0, 0 }, { 0, 0 },
6827     {  0, +1 }
6828   };
6829   static struct
6830   {
6831     int left, right, back;
6832   } turn[] =
6833   {
6834     { 0,        0,              0        },
6835     { MV_DOWN,  MV_UP,          MV_RIGHT },
6836     { MV_UP,    MV_DOWN,        MV_LEFT  },
6837     { 0,        0,              0        },
6838     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6839     { 0,        0,              0        },
6840     { 0,        0,              0        },
6841     { 0,        0,              0        },
6842     { MV_RIGHT, MV_LEFT,        MV_UP    }
6843   };
6844
6845   int element = Tile[x][y];
6846   int move_pattern = element_info[element].move_pattern;
6847
6848   int old_move_dir = MovDir[x][y];
6849   int left_dir  = turn[old_move_dir].left;
6850   int right_dir = turn[old_move_dir].right;
6851   int back_dir  = turn[old_move_dir].back;
6852
6853   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6854   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6855   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6856   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6857
6858   int left_x  = x + left_dx,  left_y  = y + left_dy;
6859   int right_x = x + right_dx, right_y = y + right_dy;
6860   int move_x  = x + move_dx,  move_y  = y + move_dy;
6861
6862   int xx, yy;
6863
6864   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6865   {
6866     TestIfBadThingTouchesOtherBadThing(x, y);
6867
6868     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6869       MovDir[x][y] = right_dir;
6870     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6871       MovDir[x][y] = left_dir;
6872
6873     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6874       MovDelay[x][y] = 9;
6875     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6876       MovDelay[x][y] = 1;
6877   }
6878   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6879   {
6880     TestIfBadThingTouchesOtherBadThing(x, y);
6881
6882     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6883       MovDir[x][y] = left_dir;
6884     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6885       MovDir[x][y] = right_dir;
6886
6887     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6888       MovDelay[x][y] = 9;
6889     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6890       MovDelay[x][y] = 1;
6891   }
6892   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6893   {
6894     TestIfBadThingTouchesOtherBadThing(x, y);
6895
6896     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6897       MovDir[x][y] = left_dir;
6898     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6899       MovDir[x][y] = right_dir;
6900
6901     if (MovDir[x][y] != old_move_dir)
6902       MovDelay[x][y] = 9;
6903   }
6904   else if (element == EL_YAMYAM)
6905   {
6906     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6907     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6908
6909     if (can_turn_left && can_turn_right)
6910       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6911     else if (can_turn_left)
6912       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6913     else if (can_turn_right)
6914       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6915     else
6916       MovDir[x][y] = back_dir;
6917
6918     MovDelay[x][y] = 16 + 16 * RND(3);
6919   }
6920   else if (element == EL_DARK_YAMYAM)
6921   {
6922     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6923                                                          left_x, left_y);
6924     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6925                                                          right_x, right_y);
6926
6927     if (can_turn_left && can_turn_right)
6928       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6929     else if (can_turn_left)
6930       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6931     else if (can_turn_right)
6932       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6933     else
6934       MovDir[x][y] = back_dir;
6935
6936     MovDelay[x][y] = 16 + 16 * RND(3);
6937   }
6938   else if (element == EL_PACMAN)
6939   {
6940     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6941     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6942
6943     if (can_turn_left && can_turn_right)
6944       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6945     else if (can_turn_left)
6946       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6947     else if (can_turn_right)
6948       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6949     else
6950       MovDir[x][y] = back_dir;
6951
6952     MovDelay[x][y] = 6 + RND(40);
6953   }
6954   else if (element == EL_PIG)
6955   {
6956     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6957     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6958     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6959     boolean should_turn_left, should_turn_right, should_move_on;
6960     int rnd_value = 24;
6961     int rnd = RND(rnd_value);
6962
6963     should_turn_left = (can_turn_left &&
6964                         (!can_move_on ||
6965                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6966                                                    y + back_dy + left_dy)));
6967     should_turn_right = (can_turn_right &&
6968                          (!can_move_on ||
6969                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6970                                                     y + back_dy + right_dy)));
6971     should_move_on = (can_move_on &&
6972                       (!can_turn_left ||
6973                        !can_turn_right ||
6974                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6975                                                  y + move_dy + left_dy) ||
6976                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6977                                                  y + move_dy + right_dy)));
6978
6979     if (should_turn_left || should_turn_right || should_move_on)
6980     {
6981       if (should_turn_left && should_turn_right && should_move_on)
6982         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6983                         rnd < 2 * rnd_value / 3 ? right_dir :
6984                         old_move_dir);
6985       else if (should_turn_left && should_turn_right)
6986         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6987       else if (should_turn_left && should_move_on)
6988         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6989       else if (should_turn_right && should_move_on)
6990         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6991       else if (should_turn_left)
6992         MovDir[x][y] = left_dir;
6993       else if (should_turn_right)
6994         MovDir[x][y] = right_dir;
6995       else if (should_move_on)
6996         MovDir[x][y] = old_move_dir;
6997     }
6998     else if (can_move_on && rnd > rnd_value / 8)
6999       MovDir[x][y] = old_move_dir;
7000     else if (can_turn_left && can_turn_right)
7001       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7002     else if (can_turn_left && rnd > rnd_value / 8)
7003       MovDir[x][y] = left_dir;
7004     else if (can_turn_right && rnd > rnd_value/8)
7005       MovDir[x][y] = right_dir;
7006     else
7007       MovDir[x][y] = back_dir;
7008
7009     xx = x + move_xy[MovDir[x][y]].dx;
7010     yy = y + move_xy[MovDir[x][y]].dy;
7011
7012     if (!IN_LEV_FIELD(xx, yy) ||
7013         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7014       MovDir[x][y] = old_move_dir;
7015
7016     MovDelay[x][y] = 0;
7017   }
7018   else if (element == EL_DRAGON)
7019   {
7020     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7021     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7022     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7023     int rnd_value = 24;
7024     int rnd = RND(rnd_value);
7025
7026     if (can_move_on && rnd > rnd_value / 8)
7027       MovDir[x][y] = old_move_dir;
7028     else if (can_turn_left && can_turn_right)
7029       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7030     else if (can_turn_left && rnd > rnd_value / 8)
7031       MovDir[x][y] = left_dir;
7032     else if (can_turn_right && rnd > rnd_value / 8)
7033       MovDir[x][y] = right_dir;
7034     else
7035       MovDir[x][y] = back_dir;
7036
7037     xx = x + move_xy[MovDir[x][y]].dx;
7038     yy = y + move_xy[MovDir[x][y]].dy;
7039
7040     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7041       MovDir[x][y] = old_move_dir;
7042
7043     MovDelay[x][y] = 0;
7044   }
7045   else if (element == EL_MOLE)
7046   {
7047     boolean can_move_on =
7048       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7049                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7050                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7051     if (!can_move_on)
7052     {
7053       boolean can_turn_left =
7054         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7055                               IS_AMOEBOID(Tile[left_x][left_y])));
7056
7057       boolean can_turn_right =
7058         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7059                               IS_AMOEBOID(Tile[right_x][right_y])));
7060
7061       if (can_turn_left && can_turn_right)
7062         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7063       else if (can_turn_left)
7064         MovDir[x][y] = left_dir;
7065       else
7066         MovDir[x][y] = right_dir;
7067     }
7068
7069     if (MovDir[x][y] != old_move_dir)
7070       MovDelay[x][y] = 9;
7071   }
7072   else if (element == EL_BALLOON)
7073   {
7074     MovDir[x][y] = game.wind_direction;
7075     MovDelay[x][y] = 0;
7076   }
7077   else if (element == EL_SPRING)
7078   {
7079     if (MovDir[x][y] & MV_HORIZONTAL)
7080     {
7081       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7082           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7083       {
7084         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7085         ResetGfxAnimation(move_x, move_y);
7086         TEST_DrawLevelField(move_x, move_y);
7087
7088         MovDir[x][y] = back_dir;
7089       }
7090       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7091                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7092         MovDir[x][y] = MV_NONE;
7093     }
7094
7095     MovDelay[x][y] = 0;
7096   }
7097   else if (element == EL_ROBOT ||
7098            element == EL_SATELLITE ||
7099            element == EL_PENGUIN ||
7100            element == EL_EMC_ANDROID)
7101   {
7102     int attr_x = -1, attr_y = -1;
7103
7104     if (game.all_players_gone)
7105     {
7106       attr_x = game.exit_x;
7107       attr_y = game.exit_y;
7108     }
7109     else
7110     {
7111       int i;
7112
7113       for (i = 0; i < MAX_PLAYERS; i++)
7114       {
7115         struct PlayerInfo *player = &stored_player[i];
7116         int jx = player->jx, jy = player->jy;
7117
7118         if (!player->active)
7119           continue;
7120
7121         if (attr_x == -1 ||
7122             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7123         {
7124           attr_x = jx;
7125           attr_y = jy;
7126         }
7127       }
7128     }
7129
7130     if (element == EL_ROBOT &&
7131         game.robot_wheel_x >= 0 &&
7132         game.robot_wheel_y >= 0 &&
7133         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7134          game.engine_version < VERSION_IDENT(3,1,0,0)))
7135     {
7136       attr_x = game.robot_wheel_x;
7137       attr_y = game.robot_wheel_y;
7138     }
7139
7140     if (element == EL_PENGUIN)
7141     {
7142       int i;
7143       static int xy[4][2] =
7144       {
7145         { 0, -1 },
7146         { -1, 0 },
7147         { +1, 0 },
7148         { 0, +1 }
7149       };
7150
7151       for (i = 0; i < NUM_DIRECTIONS; i++)
7152       {
7153         int ex = x + xy[i][0];
7154         int ey = y + xy[i][1];
7155
7156         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7157                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7158                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7159                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7160         {
7161           attr_x = ex;
7162           attr_y = ey;
7163           break;
7164         }
7165       }
7166     }
7167
7168     MovDir[x][y] = MV_NONE;
7169     if (attr_x < x)
7170       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7171     else if (attr_x > x)
7172       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7173     if (attr_y < y)
7174       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7175     else if (attr_y > y)
7176       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7177
7178     if (element == EL_ROBOT)
7179     {
7180       int newx, newy;
7181
7182       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7183         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7184       Moving2Blocked(x, y, &newx, &newy);
7185
7186       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7187         MovDelay[x][y] = 8 + 8 * !RND(3);
7188       else
7189         MovDelay[x][y] = 16;
7190     }
7191     else if (element == EL_PENGUIN)
7192     {
7193       int newx, newy;
7194
7195       MovDelay[x][y] = 1;
7196
7197       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7198       {
7199         boolean first_horiz = RND(2);
7200         int new_move_dir = MovDir[x][y];
7201
7202         MovDir[x][y] =
7203           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7204         Moving2Blocked(x, y, &newx, &newy);
7205
7206         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7207           return;
7208
7209         MovDir[x][y] =
7210           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7211         Moving2Blocked(x, y, &newx, &newy);
7212
7213         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7214           return;
7215
7216         MovDir[x][y] = old_move_dir;
7217         return;
7218       }
7219     }
7220     else if (element == EL_SATELLITE)
7221     {
7222       int newx, newy;
7223
7224       MovDelay[x][y] = 1;
7225
7226       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7227       {
7228         boolean first_horiz = RND(2);
7229         int new_move_dir = MovDir[x][y];
7230
7231         MovDir[x][y] =
7232           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7233         Moving2Blocked(x, y, &newx, &newy);
7234
7235         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7236           return;
7237
7238         MovDir[x][y] =
7239           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7240         Moving2Blocked(x, y, &newx, &newy);
7241
7242         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7243           return;
7244
7245         MovDir[x][y] = old_move_dir;
7246         return;
7247       }
7248     }
7249     else if (element == EL_EMC_ANDROID)
7250     {
7251       static int check_pos[16] =
7252       {
7253         -1,             //  0 => (invalid)
7254         7,              //  1 => MV_LEFT
7255         3,              //  2 => MV_RIGHT
7256         -1,             //  3 => (invalid)
7257         1,              //  4 =>            MV_UP
7258         0,              //  5 => MV_LEFT  | MV_UP
7259         2,              //  6 => MV_RIGHT | MV_UP
7260         -1,             //  7 => (invalid)
7261         5,              //  8 =>            MV_DOWN
7262         6,              //  9 => MV_LEFT  | MV_DOWN
7263         4,              // 10 => MV_RIGHT | MV_DOWN
7264         -1,             // 11 => (invalid)
7265         -1,             // 12 => (invalid)
7266         -1,             // 13 => (invalid)
7267         -1,             // 14 => (invalid)
7268         -1,             // 15 => (invalid)
7269       };
7270       static struct
7271       {
7272         int dx, dy;
7273         int dir;
7274       } check_xy[8] =
7275       {
7276         { -1, -1,       MV_LEFT  | MV_UP   },
7277         {  0, -1,                  MV_UP   },
7278         { +1, -1,       MV_RIGHT | MV_UP   },
7279         { +1,  0,       MV_RIGHT           },
7280         { +1, +1,       MV_RIGHT | MV_DOWN },
7281         {  0, +1,                  MV_DOWN },
7282         { -1, +1,       MV_LEFT  | MV_DOWN },
7283         { -1,  0,       MV_LEFT            },
7284       };
7285       int start_pos, check_order;
7286       boolean can_clone = FALSE;
7287       int i;
7288
7289       // check if there is any free field around current position
7290       for (i = 0; i < 8; i++)
7291       {
7292         int newx = x + check_xy[i].dx;
7293         int newy = y + check_xy[i].dy;
7294
7295         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7296         {
7297           can_clone = TRUE;
7298
7299           break;
7300         }
7301       }
7302
7303       if (can_clone)            // randomly find an element to clone
7304       {
7305         can_clone = FALSE;
7306
7307         start_pos = check_pos[RND(8)];
7308         check_order = (RND(2) ? -1 : +1);
7309
7310         for (i = 0; i < 8; i++)
7311         {
7312           int pos_raw = start_pos + i * check_order;
7313           int pos = (pos_raw + 8) % 8;
7314           int newx = x + check_xy[pos].dx;
7315           int newy = y + check_xy[pos].dy;
7316
7317           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7318           {
7319             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7320             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7321
7322             Store[x][y] = Tile[newx][newy];
7323
7324             can_clone = TRUE;
7325
7326             break;
7327           }
7328         }
7329       }
7330
7331       if (can_clone)            // randomly find a direction to move
7332       {
7333         can_clone = FALSE;
7334
7335         start_pos = check_pos[RND(8)];
7336         check_order = (RND(2) ? -1 : +1);
7337
7338         for (i = 0; i < 8; i++)
7339         {
7340           int pos_raw = start_pos + i * check_order;
7341           int pos = (pos_raw + 8) % 8;
7342           int newx = x + check_xy[pos].dx;
7343           int newy = y + check_xy[pos].dy;
7344           int new_move_dir = check_xy[pos].dir;
7345
7346           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7347           {
7348             MovDir[x][y] = new_move_dir;
7349             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7350
7351             can_clone = TRUE;
7352
7353             break;
7354           }
7355         }
7356       }
7357
7358       if (can_clone)            // cloning and moving successful
7359         return;
7360
7361       // cannot clone -- try to move towards player
7362
7363       start_pos = check_pos[MovDir[x][y] & 0x0f];
7364       check_order = (RND(2) ? -1 : +1);
7365
7366       for (i = 0; i < 3; i++)
7367       {
7368         // first check start_pos, then previous/next or (next/previous) pos
7369         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7370         int pos = (pos_raw + 8) % 8;
7371         int newx = x + check_xy[pos].dx;
7372         int newy = y + check_xy[pos].dy;
7373         int new_move_dir = check_xy[pos].dir;
7374
7375         if (IS_PLAYER(newx, newy))
7376           break;
7377
7378         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7379         {
7380           MovDir[x][y] = new_move_dir;
7381           MovDelay[x][y] = level.android_move_time * 8 + 1;
7382
7383           break;
7384         }
7385       }
7386     }
7387   }
7388   else if (move_pattern == MV_TURNING_LEFT ||
7389            move_pattern == MV_TURNING_RIGHT ||
7390            move_pattern == MV_TURNING_LEFT_RIGHT ||
7391            move_pattern == MV_TURNING_RIGHT_LEFT ||
7392            move_pattern == MV_TURNING_RANDOM ||
7393            move_pattern == MV_ALL_DIRECTIONS)
7394   {
7395     boolean can_turn_left =
7396       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7397     boolean can_turn_right =
7398       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7399
7400     if (element_info[element].move_stepsize == 0)       // "not moving"
7401       return;
7402
7403     if (move_pattern == MV_TURNING_LEFT)
7404       MovDir[x][y] = left_dir;
7405     else if (move_pattern == MV_TURNING_RIGHT)
7406       MovDir[x][y] = right_dir;
7407     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7408       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7409     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7410       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7411     else if (move_pattern == MV_TURNING_RANDOM)
7412       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7413                       can_turn_right && !can_turn_left ? right_dir :
7414                       RND(2) ? left_dir : right_dir);
7415     else if (can_turn_left && can_turn_right)
7416       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7417     else if (can_turn_left)
7418       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7419     else if (can_turn_right)
7420       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7421     else
7422       MovDir[x][y] = back_dir;
7423
7424     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7425   }
7426   else if (move_pattern == MV_HORIZONTAL ||
7427            move_pattern == MV_VERTICAL)
7428   {
7429     if (move_pattern & old_move_dir)
7430       MovDir[x][y] = back_dir;
7431     else if (move_pattern == MV_HORIZONTAL)
7432       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7433     else if (move_pattern == MV_VERTICAL)
7434       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7435
7436     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7437   }
7438   else if (move_pattern & MV_ANY_DIRECTION)
7439   {
7440     MovDir[x][y] = move_pattern;
7441     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7442   }
7443   else if (move_pattern & MV_WIND_DIRECTION)
7444   {
7445     MovDir[x][y] = game.wind_direction;
7446     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7447   }
7448   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7449   {
7450     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7451       MovDir[x][y] = left_dir;
7452     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7453       MovDir[x][y] = right_dir;
7454
7455     if (MovDir[x][y] != old_move_dir)
7456       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7457   }
7458   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7459   {
7460     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7461       MovDir[x][y] = right_dir;
7462     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7463       MovDir[x][y] = left_dir;
7464
7465     if (MovDir[x][y] != old_move_dir)
7466       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7467   }
7468   else if (move_pattern == MV_TOWARDS_PLAYER ||
7469            move_pattern == MV_AWAY_FROM_PLAYER)
7470   {
7471     int attr_x = -1, attr_y = -1;
7472     int newx, newy;
7473     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7474
7475     if (game.all_players_gone)
7476     {
7477       attr_x = game.exit_x;
7478       attr_y = game.exit_y;
7479     }
7480     else
7481     {
7482       int i;
7483
7484       for (i = 0; i < MAX_PLAYERS; i++)
7485       {
7486         struct PlayerInfo *player = &stored_player[i];
7487         int jx = player->jx, jy = player->jy;
7488
7489         if (!player->active)
7490           continue;
7491
7492         if (attr_x == -1 ||
7493             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7494         {
7495           attr_x = jx;
7496           attr_y = jy;
7497         }
7498       }
7499     }
7500
7501     MovDir[x][y] = MV_NONE;
7502     if (attr_x < x)
7503       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7504     else if (attr_x > x)
7505       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7506     if (attr_y < y)
7507       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7508     else if (attr_y > y)
7509       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7510
7511     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7512
7513     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7514     {
7515       boolean first_horiz = RND(2);
7516       int new_move_dir = MovDir[x][y];
7517
7518       if (element_info[element].move_stepsize == 0)     // "not moving"
7519       {
7520         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7521         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7522
7523         return;
7524       }
7525
7526       MovDir[x][y] =
7527         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7528       Moving2Blocked(x, y, &newx, &newy);
7529
7530       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7531         return;
7532
7533       MovDir[x][y] =
7534         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7535       Moving2Blocked(x, y, &newx, &newy);
7536
7537       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7538         return;
7539
7540       MovDir[x][y] = old_move_dir;
7541     }
7542   }
7543   else if (move_pattern == MV_WHEN_PUSHED ||
7544            move_pattern == MV_WHEN_DROPPED)
7545   {
7546     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7547       MovDir[x][y] = MV_NONE;
7548
7549     MovDelay[x][y] = 0;
7550   }
7551   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7552   {
7553     static int test_xy[7][2] =
7554     {
7555       { 0, -1 },
7556       { -1, 0 },
7557       { +1, 0 },
7558       { 0, +1 },
7559       { 0, -1 },
7560       { -1, 0 },
7561       { +1, 0 },
7562     };
7563     static int test_dir[7] =
7564     {
7565       MV_UP,
7566       MV_LEFT,
7567       MV_RIGHT,
7568       MV_DOWN,
7569       MV_UP,
7570       MV_LEFT,
7571       MV_RIGHT,
7572     };
7573     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7574     int move_preference = -1000000;     // start with very low preference
7575     int new_move_dir = MV_NONE;
7576     int start_test = RND(4);
7577     int i;
7578
7579     for (i = 0; i < NUM_DIRECTIONS; i++)
7580     {
7581       int move_dir = test_dir[start_test + i];
7582       int move_dir_preference;
7583
7584       xx = x + test_xy[start_test + i][0];
7585       yy = y + test_xy[start_test + i][1];
7586
7587       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7588           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7589       {
7590         new_move_dir = move_dir;
7591
7592         break;
7593       }
7594
7595       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7596         continue;
7597
7598       move_dir_preference = -1 * RunnerVisit[xx][yy];
7599       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7600         move_dir_preference = PlayerVisit[xx][yy];
7601
7602       if (move_dir_preference > move_preference)
7603       {
7604         // prefer field that has not been visited for the longest time
7605         move_preference = move_dir_preference;
7606         new_move_dir = move_dir;
7607       }
7608       else if (move_dir_preference == move_preference &&
7609                move_dir == old_move_dir)
7610       {
7611         // prefer last direction when all directions are preferred equally
7612         move_preference = move_dir_preference;
7613         new_move_dir = move_dir;
7614       }
7615     }
7616
7617     MovDir[x][y] = new_move_dir;
7618     if (old_move_dir != new_move_dir)
7619       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7620   }
7621 }
7622
7623 static void TurnRound(int x, int y)
7624 {
7625   int direction = MovDir[x][y];
7626
7627   TurnRoundExt(x, y);
7628
7629   GfxDir[x][y] = MovDir[x][y];
7630
7631   if (direction != MovDir[x][y])
7632     GfxFrame[x][y] = 0;
7633
7634   if (MovDelay[x][y])
7635     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7636
7637   ResetGfxFrame(x, y);
7638 }
7639
7640 static boolean JustBeingPushed(int x, int y)
7641 {
7642   int i;
7643
7644   for (i = 0; i < MAX_PLAYERS; i++)
7645   {
7646     struct PlayerInfo *player = &stored_player[i];
7647
7648     if (player->active && player->is_pushing && player->MovPos)
7649     {
7650       int next_jx = player->jx + (player->jx - player->last_jx);
7651       int next_jy = player->jy + (player->jy - player->last_jy);
7652
7653       if (x == next_jx && y == next_jy)
7654         return TRUE;
7655     }
7656   }
7657
7658   return FALSE;
7659 }
7660
7661 static void StartMoving(int x, int y)
7662 {
7663   boolean started_moving = FALSE;       // some elements can fall _and_ move
7664   int element = Tile[x][y];
7665
7666   if (Stop[x][y])
7667     return;
7668
7669   if (MovDelay[x][y] == 0)
7670     GfxAction[x][y] = ACTION_DEFAULT;
7671
7672   if (CAN_FALL(element) && y < lev_fieldy - 1)
7673   {
7674     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7675         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7676       if (JustBeingPushed(x, y))
7677         return;
7678
7679     if (element == EL_QUICKSAND_FULL)
7680     {
7681       if (IS_FREE(x, y + 1))
7682       {
7683         InitMovingField(x, y, MV_DOWN);
7684         started_moving = TRUE;
7685
7686         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7687 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7688         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7689           Store[x][y] = EL_ROCK;
7690 #else
7691         Store[x][y] = EL_ROCK;
7692 #endif
7693
7694         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7695       }
7696       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7697       {
7698         if (!MovDelay[x][y])
7699         {
7700           MovDelay[x][y] = TILEY + 1;
7701
7702           ResetGfxAnimation(x, y);
7703           ResetGfxAnimation(x, y + 1);
7704         }
7705
7706         if (MovDelay[x][y])
7707         {
7708           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7709           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7710
7711           MovDelay[x][y]--;
7712           if (MovDelay[x][y])
7713             return;
7714         }
7715
7716         Tile[x][y] = EL_QUICKSAND_EMPTY;
7717         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7718         Store[x][y + 1] = Store[x][y];
7719         Store[x][y] = 0;
7720
7721         PlayLevelSoundAction(x, y, ACTION_FILLING);
7722       }
7723       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7724       {
7725         if (!MovDelay[x][y])
7726         {
7727           MovDelay[x][y] = TILEY + 1;
7728
7729           ResetGfxAnimation(x, y);
7730           ResetGfxAnimation(x, y + 1);
7731         }
7732
7733         if (MovDelay[x][y])
7734         {
7735           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7736           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7737
7738           MovDelay[x][y]--;
7739           if (MovDelay[x][y])
7740             return;
7741         }
7742
7743         Tile[x][y] = EL_QUICKSAND_EMPTY;
7744         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7745         Store[x][y + 1] = Store[x][y];
7746         Store[x][y] = 0;
7747
7748         PlayLevelSoundAction(x, y, ACTION_FILLING);
7749       }
7750     }
7751     else if (element == EL_QUICKSAND_FAST_FULL)
7752     {
7753       if (IS_FREE(x, y + 1))
7754       {
7755         InitMovingField(x, y, MV_DOWN);
7756         started_moving = TRUE;
7757
7758         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7759 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7760         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7761           Store[x][y] = EL_ROCK;
7762 #else
7763         Store[x][y] = EL_ROCK;
7764 #endif
7765
7766         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7767       }
7768       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7769       {
7770         if (!MovDelay[x][y])
7771         {
7772           MovDelay[x][y] = TILEY + 1;
7773
7774           ResetGfxAnimation(x, y);
7775           ResetGfxAnimation(x, y + 1);
7776         }
7777
7778         if (MovDelay[x][y])
7779         {
7780           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7781           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7782
7783           MovDelay[x][y]--;
7784           if (MovDelay[x][y])
7785             return;
7786         }
7787
7788         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7789         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7790         Store[x][y + 1] = Store[x][y];
7791         Store[x][y] = 0;
7792
7793         PlayLevelSoundAction(x, y, ACTION_FILLING);
7794       }
7795       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7796       {
7797         if (!MovDelay[x][y])
7798         {
7799           MovDelay[x][y] = TILEY + 1;
7800
7801           ResetGfxAnimation(x, y);
7802           ResetGfxAnimation(x, y + 1);
7803         }
7804
7805         if (MovDelay[x][y])
7806         {
7807           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7808           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7809
7810           MovDelay[x][y]--;
7811           if (MovDelay[x][y])
7812             return;
7813         }
7814
7815         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7816         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7817         Store[x][y + 1] = Store[x][y];
7818         Store[x][y] = 0;
7819
7820         PlayLevelSoundAction(x, y, ACTION_FILLING);
7821       }
7822     }
7823     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7824              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7825     {
7826       InitMovingField(x, y, MV_DOWN);
7827       started_moving = TRUE;
7828
7829       Tile[x][y] = EL_QUICKSAND_FILLING;
7830       Store[x][y] = element;
7831
7832       PlayLevelSoundAction(x, y, ACTION_FILLING);
7833     }
7834     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7835              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7836     {
7837       InitMovingField(x, y, MV_DOWN);
7838       started_moving = TRUE;
7839
7840       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7841       Store[x][y] = element;
7842
7843       PlayLevelSoundAction(x, y, ACTION_FILLING);
7844     }
7845     else if (element == EL_MAGIC_WALL_FULL)
7846     {
7847       if (IS_FREE(x, y + 1))
7848       {
7849         InitMovingField(x, y, MV_DOWN);
7850         started_moving = TRUE;
7851
7852         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7853         Store[x][y] = EL_CHANGED(Store[x][y]);
7854       }
7855       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7856       {
7857         if (!MovDelay[x][y])
7858           MovDelay[x][y] = TILEY / 4 + 1;
7859
7860         if (MovDelay[x][y])
7861         {
7862           MovDelay[x][y]--;
7863           if (MovDelay[x][y])
7864             return;
7865         }
7866
7867         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7868         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7869         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7870         Store[x][y] = 0;
7871       }
7872     }
7873     else if (element == EL_BD_MAGIC_WALL_FULL)
7874     {
7875       if (IS_FREE(x, y + 1))
7876       {
7877         InitMovingField(x, y, MV_DOWN);
7878         started_moving = TRUE;
7879
7880         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7881         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7882       }
7883       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7884       {
7885         if (!MovDelay[x][y])
7886           MovDelay[x][y] = TILEY / 4 + 1;
7887
7888         if (MovDelay[x][y])
7889         {
7890           MovDelay[x][y]--;
7891           if (MovDelay[x][y])
7892             return;
7893         }
7894
7895         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7896         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7897         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7898         Store[x][y] = 0;
7899       }
7900     }
7901     else if (element == EL_DC_MAGIC_WALL_FULL)
7902     {
7903       if (IS_FREE(x, y + 1))
7904       {
7905         InitMovingField(x, y, MV_DOWN);
7906         started_moving = TRUE;
7907
7908         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7909         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7910       }
7911       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7912       {
7913         if (!MovDelay[x][y])
7914           MovDelay[x][y] = TILEY / 4 + 1;
7915
7916         if (MovDelay[x][y])
7917         {
7918           MovDelay[x][y]--;
7919           if (MovDelay[x][y])
7920             return;
7921         }
7922
7923         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7924         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7925         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7926         Store[x][y] = 0;
7927       }
7928     }
7929     else if ((CAN_PASS_MAGIC_WALL(element) &&
7930               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7931                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7932              (CAN_PASS_DC_MAGIC_WALL(element) &&
7933               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7934
7935     {
7936       InitMovingField(x, y, MV_DOWN);
7937       started_moving = TRUE;
7938
7939       Tile[x][y] =
7940         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7941          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7942          EL_DC_MAGIC_WALL_FILLING);
7943       Store[x][y] = element;
7944     }
7945     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7946     {
7947       SplashAcid(x, y + 1);
7948
7949       InitMovingField(x, y, MV_DOWN);
7950       started_moving = TRUE;
7951
7952       Store[x][y] = EL_ACID;
7953     }
7954     else if (
7955              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7956               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7957              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7958               CAN_FALL(element) && WasJustFalling[x][y] &&
7959               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7960
7961              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7962               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7963               (Tile[x][y + 1] == EL_BLOCKED)))
7964     {
7965       /* this is needed for a special case not covered by calling "Impact()"
7966          from "ContinueMoving()": if an element moves to a tile directly below
7967          another element which was just falling on that tile (which was empty
7968          in the previous frame), the falling element above would just stop
7969          instead of smashing the element below (in previous version, the above
7970          element was just checked for "moving" instead of "falling", resulting
7971          in incorrect smashes caused by horizontal movement of the above
7972          element; also, the case of the player being the element to smash was
7973          simply not covered here... :-/ ) */
7974
7975       CheckCollision[x][y] = 0;
7976       CheckImpact[x][y] = 0;
7977
7978       Impact(x, y);
7979     }
7980     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7981     {
7982       if (MovDir[x][y] == MV_NONE)
7983       {
7984         InitMovingField(x, y, MV_DOWN);
7985         started_moving = TRUE;
7986       }
7987     }
7988     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7989     {
7990       if (WasJustFalling[x][y]) // prevent animation from being restarted
7991         MovDir[x][y] = MV_DOWN;
7992
7993       InitMovingField(x, y, MV_DOWN);
7994       started_moving = TRUE;
7995     }
7996     else if (element == EL_AMOEBA_DROP)
7997     {
7998       Tile[x][y] = EL_AMOEBA_GROWING;
7999       Store[x][y] = EL_AMOEBA_WET;
8000     }
8001     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8002               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8003              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8004              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8005     {
8006       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8007                                 (IS_FREE(x - 1, y + 1) ||
8008                                  Tile[x - 1][y + 1] == EL_ACID));
8009       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8010                                 (IS_FREE(x + 1, y + 1) ||
8011                                  Tile[x + 1][y + 1] == EL_ACID));
8012       boolean can_fall_any  = (can_fall_left || can_fall_right);
8013       boolean can_fall_both = (can_fall_left && can_fall_right);
8014       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8015
8016       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8017       {
8018         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8019           can_fall_right = FALSE;
8020         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8021           can_fall_left = FALSE;
8022         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8023           can_fall_right = FALSE;
8024         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8025           can_fall_left = FALSE;
8026
8027         can_fall_any  = (can_fall_left || can_fall_right);
8028         can_fall_both = FALSE;
8029       }
8030
8031       if (can_fall_both)
8032       {
8033         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8034           can_fall_right = FALSE;       // slip down on left side
8035         else
8036           can_fall_left = !(can_fall_right = RND(2));
8037
8038         can_fall_both = FALSE;
8039       }
8040
8041       if (can_fall_any)
8042       {
8043         // if not determined otherwise, prefer left side for slipping down
8044         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8045         started_moving = TRUE;
8046       }
8047     }
8048     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8049     {
8050       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8051       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8052       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8053       int belt_dir = game.belt_dir[belt_nr];
8054
8055       if ((belt_dir == MV_LEFT  && left_is_free) ||
8056           (belt_dir == MV_RIGHT && right_is_free))
8057       {
8058         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8059
8060         InitMovingField(x, y, belt_dir);
8061         started_moving = TRUE;
8062
8063         Pushed[x][y] = TRUE;
8064         Pushed[nextx][y] = TRUE;
8065
8066         GfxAction[x][y] = ACTION_DEFAULT;
8067       }
8068       else
8069       {
8070         MovDir[x][y] = 0;       // if element was moving, stop it
8071       }
8072     }
8073   }
8074
8075   // not "else if" because of elements that can fall and move (EL_SPRING)
8076   if (CAN_MOVE(element) && !started_moving)
8077   {
8078     int move_pattern = element_info[element].move_pattern;
8079     int newx, newy;
8080
8081     Moving2Blocked(x, y, &newx, &newy);
8082
8083     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8084       return;
8085
8086     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8087         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8088     {
8089       WasJustMoving[x][y] = 0;
8090       CheckCollision[x][y] = 0;
8091
8092       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8093
8094       if (Tile[x][y] != element)        // element has changed
8095         return;
8096     }
8097
8098     if (!MovDelay[x][y])        // start new movement phase
8099     {
8100       // all objects that can change their move direction after each step
8101       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8102
8103       if (element != EL_YAMYAM &&
8104           element != EL_DARK_YAMYAM &&
8105           element != EL_PACMAN &&
8106           !(move_pattern & MV_ANY_DIRECTION) &&
8107           move_pattern != MV_TURNING_LEFT &&
8108           move_pattern != MV_TURNING_RIGHT &&
8109           move_pattern != MV_TURNING_LEFT_RIGHT &&
8110           move_pattern != MV_TURNING_RIGHT_LEFT &&
8111           move_pattern != MV_TURNING_RANDOM)
8112       {
8113         TurnRound(x, y);
8114
8115         if (MovDelay[x][y] && (element == EL_BUG ||
8116                                element == EL_SPACESHIP ||
8117                                element == EL_SP_SNIKSNAK ||
8118                                element == EL_SP_ELECTRON ||
8119                                element == EL_MOLE))
8120           TEST_DrawLevelField(x, y);
8121       }
8122     }
8123
8124     if (MovDelay[x][y])         // wait some time before next movement
8125     {
8126       MovDelay[x][y]--;
8127
8128       if (element == EL_ROBOT ||
8129           element == EL_YAMYAM ||
8130           element == EL_DARK_YAMYAM)
8131       {
8132         DrawLevelElementAnimationIfNeeded(x, y, element);
8133         PlayLevelSoundAction(x, y, ACTION_WAITING);
8134       }
8135       else if (element == EL_SP_ELECTRON)
8136         DrawLevelElementAnimationIfNeeded(x, y, element);
8137       else if (element == EL_DRAGON)
8138       {
8139         int i;
8140         int dir = MovDir[x][y];
8141         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8142         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8143         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8144                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8145                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8146                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8147         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8148
8149         GfxAction[x][y] = ACTION_ATTACKING;
8150
8151         if (IS_PLAYER(x, y))
8152           DrawPlayerField(x, y);
8153         else
8154           TEST_DrawLevelField(x, y);
8155
8156         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8157
8158         for (i = 1; i <= 3; i++)
8159         {
8160           int xx = x + i * dx;
8161           int yy = y + i * dy;
8162           int sx = SCREENX(xx);
8163           int sy = SCREENY(yy);
8164           int flame_graphic = graphic + (i - 1);
8165
8166           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8167             break;
8168
8169           if (MovDelay[x][y])
8170           {
8171             int flamed = MovingOrBlocked2Element(xx, yy);
8172
8173             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8174               Bang(xx, yy);
8175             else
8176               RemoveMovingField(xx, yy);
8177
8178             ChangeDelay[xx][yy] = 0;
8179
8180             Tile[xx][yy] = EL_FLAMES;
8181
8182             if (IN_SCR_FIELD(sx, sy))
8183             {
8184               TEST_DrawLevelFieldCrumbled(xx, yy);
8185               DrawGraphic(sx, sy, flame_graphic, frame);
8186             }
8187           }
8188           else
8189           {
8190             if (Tile[xx][yy] == EL_FLAMES)
8191               Tile[xx][yy] = EL_EMPTY;
8192             TEST_DrawLevelField(xx, yy);
8193           }
8194         }
8195       }
8196
8197       if (MovDelay[x][y])       // element still has to wait some time
8198       {
8199         PlayLevelSoundAction(x, y, ACTION_WAITING);
8200
8201         return;
8202       }
8203     }
8204
8205     // now make next step
8206
8207     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8208
8209     if (DONT_COLLIDE_WITH(element) &&
8210         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8211         !PLAYER_ENEMY_PROTECTED(newx, newy))
8212     {
8213       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8214
8215       return;
8216     }
8217
8218     else if (CAN_MOVE_INTO_ACID(element) &&
8219              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8220              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8221              (MovDir[x][y] == MV_DOWN ||
8222               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8223     {
8224       SplashAcid(newx, newy);
8225       Store[x][y] = EL_ACID;
8226     }
8227     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8228     {
8229       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8230           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8231           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8232           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8233       {
8234         RemoveField(x, y);
8235         TEST_DrawLevelField(x, y);
8236
8237         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8238         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8239           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8240
8241         game.friends_still_needed--;
8242         if (!game.friends_still_needed &&
8243             !game.GameOver &&
8244             game.all_players_gone)
8245           LevelSolved();
8246
8247         return;
8248       }
8249       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8250       {
8251         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8252           TEST_DrawLevelField(newx, newy);
8253         else
8254           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8255       }
8256       else if (!IS_FREE(newx, newy))
8257       {
8258         GfxAction[x][y] = ACTION_WAITING;
8259
8260         if (IS_PLAYER(x, y))
8261           DrawPlayerField(x, y);
8262         else
8263           TEST_DrawLevelField(x, y);
8264
8265         return;
8266       }
8267     }
8268     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8269     {
8270       if (IS_FOOD_PIG(Tile[newx][newy]))
8271       {
8272         if (IS_MOVING(newx, newy))
8273           RemoveMovingField(newx, newy);
8274         else
8275         {
8276           Tile[newx][newy] = EL_EMPTY;
8277           TEST_DrawLevelField(newx, newy);
8278         }
8279
8280         PlayLevelSound(x, y, SND_PIG_DIGGING);
8281       }
8282       else if (!IS_FREE(newx, newy))
8283       {
8284         if (IS_PLAYER(x, y))
8285           DrawPlayerField(x, y);
8286         else
8287           TEST_DrawLevelField(x, y);
8288
8289         return;
8290       }
8291     }
8292     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8293     {
8294       if (Store[x][y] != EL_EMPTY)
8295       {
8296         boolean can_clone = FALSE;
8297         int xx, yy;
8298
8299         // check if element to clone is still there
8300         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8301         {
8302           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8303           {
8304             can_clone = TRUE;
8305
8306             break;
8307           }
8308         }
8309
8310         // cannot clone or target field not free anymore -- do not clone
8311         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8312           Store[x][y] = EL_EMPTY;
8313       }
8314
8315       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8316       {
8317         if (IS_MV_DIAGONAL(MovDir[x][y]))
8318         {
8319           int diagonal_move_dir = MovDir[x][y];
8320           int stored = Store[x][y];
8321           int change_delay = 8;
8322           int graphic;
8323
8324           // android is moving diagonally
8325
8326           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8327
8328           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8329           GfxElement[x][y] = EL_EMC_ANDROID;
8330           GfxAction[x][y] = ACTION_SHRINKING;
8331           GfxDir[x][y] = diagonal_move_dir;
8332           ChangeDelay[x][y] = change_delay;
8333
8334           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8335                                    GfxDir[x][y]);
8336
8337           DrawLevelGraphicAnimation(x, y, graphic);
8338           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8339
8340           if (Tile[newx][newy] == EL_ACID)
8341           {
8342             SplashAcid(newx, newy);
8343
8344             return;
8345           }
8346
8347           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8348
8349           Store[newx][newy] = EL_EMC_ANDROID;
8350           GfxElement[newx][newy] = EL_EMC_ANDROID;
8351           GfxAction[newx][newy] = ACTION_GROWING;
8352           GfxDir[newx][newy] = diagonal_move_dir;
8353           ChangeDelay[newx][newy] = change_delay;
8354
8355           graphic = el_act_dir2img(GfxElement[newx][newy],
8356                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8357
8358           DrawLevelGraphicAnimation(newx, newy, graphic);
8359           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8360
8361           return;
8362         }
8363         else
8364         {
8365           Tile[newx][newy] = EL_EMPTY;
8366           TEST_DrawLevelField(newx, newy);
8367
8368           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8369         }
8370       }
8371       else if (!IS_FREE(newx, newy))
8372       {
8373         return;
8374       }
8375     }
8376     else if (IS_CUSTOM_ELEMENT(element) &&
8377              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8378     {
8379       if (!DigFieldByCE(newx, newy, element))
8380         return;
8381
8382       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8383       {
8384         RunnerVisit[x][y] = FrameCounter;
8385         PlayerVisit[x][y] /= 8;         // expire player visit path
8386       }
8387     }
8388     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8389     {
8390       if (!IS_FREE(newx, newy))
8391       {
8392         if (IS_PLAYER(x, y))
8393           DrawPlayerField(x, y);
8394         else
8395           TEST_DrawLevelField(x, y);
8396
8397         return;
8398       }
8399       else
8400       {
8401         boolean wanna_flame = !RND(10);
8402         int dx = newx - x, dy = newy - y;
8403         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8404         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8405         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8406                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8407         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8408                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8409
8410         if ((wanna_flame ||
8411              IS_CLASSIC_ENEMY(element1) ||
8412              IS_CLASSIC_ENEMY(element2)) &&
8413             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8414             element1 != EL_FLAMES && element2 != EL_FLAMES)
8415         {
8416           ResetGfxAnimation(x, y);
8417           GfxAction[x][y] = ACTION_ATTACKING;
8418
8419           if (IS_PLAYER(x, y))
8420             DrawPlayerField(x, y);
8421           else
8422             TEST_DrawLevelField(x, y);
8423
8424           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8425
8426           MovDelay[x][y] = 50;
8427
8428           Tile[newx][newy] = EL_FLAMES;
8429           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8430             Tile[newx1][newy1] = EL_FLAMES;
8431           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8432             Tile[newx2][newy2] = EL_FLAMES;
8433
8434           return;
8435         }
8436       }
8437     }
8438     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8439              Tile[newx][newy] == EL_DIAMOND)
8440     {
8441       if (IS_MOVING(newx, newy))
8442         RemoveMovingField(newx, newy);
8443       else
8444       {
8445         Tile[newx][newy] = EL_EMPTY;
8446         TEST_DrawLevelField(newx, newy);
8447       }
8448
8449       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8450     }
8451     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8452              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8453     {
8454       if (AmoebaNr[newx][newy])
8455       {
8456         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8457         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8458             Tile[newx][newy] == EL_BD_AMOEBA)
8459           AmoebaCnt[AmoebaNr[newx][newy]]--;
8460       }
8461
8462       if (IS_MOVING(newx, newy))
8463       {
8464         RemoveMovingField(newx, newy);
8465       }
8466       else
8467       {
8468         Tile[newx][newy] = EL_EMPTY;
8469         TEST_DrawLevelField(newx, newy);
8470       }
8471
8472       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8473     }
8474     else if ((element == EL_PACMAN || element == EL_MOLE)
8475              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8476     {
8477       if (AmoebaNr[newx][newy])
8478       {
8479         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8480         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8481             Tile[newx][newy] == EL_BD_AMOEBA)
8482           AmoebaCnt[AmoebaNr[newx][newy]]--;
8483       }
8484
8485       if (element == EL_MOLE)
8486       {
8487         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8488         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8489
8490         ResetGfxAnimation(x, y);
8491         GfxAction[x][y] = ACTION_DIGGING;
8492         TEST_DrawLevelField(x, y);
8493
8494         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8495
8496         return;                         // wait for shrinking amoeba
8497       }
8498       else      // element == EL_PACMAN
8499       {
8500         Tile[newx][newy] = EL_EMPTY;
8501         TEST_DrawLevelField(newx, newy);
8502         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8503       }
8504     }
8505     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8506              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8507               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8508     {
8509       // wait for shrinking amoeba to completely disappear
8510       return;
8511     }
8512     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8513     {
8514       // object was running against a wall
8515
8516       TurnRound(x, y);
8517
8518       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8519         DrawLevelElementAnimation(x, y, element);
8520
8521       if (DONT_TOUCH(element))
8522         TestIfBadThingTouchesPlayer(x, y);
8523
8524       return;
8525     }
8526
8527     InitMovingField(x, y, MovDir[x][y]);
8528
8529     PlayLevelSoundAction(x, y, ACTION_MOVING);
8530   }
8531
8532   if (MovDir[x][y])
8533     ContinueMoving(x, y);
8534 }
8535
8536 void ContinueMoving(int x, int y)
8537 {
8538   int element = Tile[x][y];
8539   struct ElementInfo *ei = &element_info[element];
8540   int direction = MovDir[x][y];
8541   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8542   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8543   int newx = x + dx, newy = y + dy;
8544   int stored = Store[x][y];
8545   int stored_new = Store[newx][newy];
8546   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8547   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8548   boolean last_line = (newy == lev_fieldy - 1);
8549   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8550
8551   if (pushed_by_player)         // special case: moving object pushed by player
8552   {
8553     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8554   }
8555   else if (use_step_delay)      // special case: moving object has step delay
8556   {
8557     if (!MovDelay[x][y])
8558       MovPos[x][y] += getElementMoveStepsize(x, y);
8559
8560     if (MovDelay[x][y])
8561       MovDelay[x][y]--;
8562     else
8563       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8564
8565     if (MovDelay[x][y])
8566     {
8567       TEST_DrawLevelField(x, y);
8568
8569       return;   // element is still waiting
8570     }
8571   }
8572   else                          // normal case: generically moving object
8573   {
8574     MovPos[x][y] += getElementMoveStepsize(x, y);
8575   }
8576
8577   if (ABS(MovPos[x][y]) < TILEX)
8578   {
8579     TEST_DrawLevelField(x, y);
8580
8581     return;     // element is still moving
8582   }
8583
8584   // element reached destination field
8585
8586   Tile[x][y] = EL_EMPTY;
8587   Tile[newx][newy] = element;
8588   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8589
8590   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8591   {
8592     element = Tile[newx][newy] = EL_ACID;
8593   }
8594   else if (element == EL_MOLE)
8595   {
8596     Tile[x][y] = EL_SAND;
8597
8598     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8599   }
8600   else if (element == EL_QUICKSAND_FILLING)
8601   {
8602     element = Tile[newx][newy] = get_next_element(element);
8603     Store[newx][newy] = Store[x][y];
8604   }
8605   else if (element == EL_QUICKSAND_EMPTYING)
8606   {
8607     Tile[x][y] = get_next_element(element);
8608     element = Tile[newx][newy] = Store[x][y];
8609   }
8610   else if (element == EL_QUICKSAND_FAST_FILLING)
8611   {
8612     element = Tile[newx][newy] = get_next_element(element);
8613     Store[newx][newy] = Store[x][y];
8614   }
8615   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8616   {
8617     Tile[x][y] = get_next_element(element);
8618     element = Tile[newx][newy] = Store[x][y];
8619   }
8620   else if (element == EL_MAGIC_WALL_FILLING)
8621   {
8622     element = Tile[newx][newy] = get_next_element(element);
8623     if (!game.magic_wall_active)
8624       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8625     Store[newx][newy] = Store[x][y];
8626   }
8627   else if (element == EL_MAGIC_WALL_EMPTYING)
8628   {
8629     Tile[x][y] = get_next_element(element);
8630     if (!game.magic_wall_active)
8631       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8632     element = Tile[newx][newy] = Store[x][y];
8633
8634     InitField(newx, newy, FALSE);
8635   }
8636   else if (element == EL_BD_MAGIC_WALL_FILLING)
8637   {
8638     element = Tile[newx][newy] = get_next_element(element);
8639     if (!game.magic_wall_active)
8640       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8641     Store[newx][newy] = Store[x][y];
8642   }
8643   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8644   {
8645     Tile[x][y] = get_next_element(element);
8646     if (!game.magic_wall_active)
8647       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8648     element = Tile[newx][newy] = Store[x][y];
8649
8650     InitField(newx, newy, FALSE);
8651   }
8652   else if (element == EL_DC_MAGIC_WALL_FILLING)
8653   {
8654     element = Tile[newx][newy] = get_next_element(element);
8655     if (!game.magic_wall_active)
8656       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8657     Store[newx][newy] = Store[x][y];
8658   }
8659   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8660   {
8661     Tile[x][y] = get_next_element(element);
8662     if (!game.magic_wall_active)
8663       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8664     element = Tile[newx][newy] = Store[x][y];
8665
8666     InitField(newx, newy, FALSE);
8667   }
8668   else if (element == EL_AMOEBA_DROPPING)
8669   {
8670     Tile[x][y] = get_next_element(element);
8671     element = Tile[newx][newy] = Store[x][y];
8672   }
8673   else if (element == EL_SOKOBAN_OBJECT)
8674   {
8675     if (Back[x][y])
8676       Tile[x][y] = Back[x][y];
8677
8678     if (Back[newx][newy])
8679       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8680
8681     Back[x][y] = Back[newx][newy] = 0;
8682   }
8683
8684   Store[x][y] = EL_EMPTY;
8685   MovPos[x][y] = 0;
8686   MovDir[x][y] = 0;
8687   MovDelay[x][y] = 0;
8688
8689   MovDelay[newx][newy] = 0;
8690
8691   if (CAN_CHANGE_OR_HAS_ACTION(element))
8692   {
8693     // copy element change control values to new field
8694     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8695     ChangePage[newx][newy]  = ChangePage[x][y];
8696     ChangeCount[newx][newy] = ChangeCount[x][y];
8697     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8698   }
8699
8700   CustomValue[newx][newy] = CustomValue[x][y];
8701
8702   ChangeDelay[x][y] = 0;
8703   ChangePage[x][y] = -1;
8704   ChangeCount[x][y] = 0;
8705   ChangeEvent[x][y] = -1;
8706
8707   CustomValue[x][y] = 0;
8708
8709   // copy animation control values to new field
8710   GfxFrame[newx][newy]  = GfxFrame[x][y];
8711   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8712   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8713   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8714
8715   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8716
8717   // some elements can leave other elements behind after moving
8718   if (ei->move_leave_element != EL_EMPTY &&
8719       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8720       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8721   {
8722     int move_leave_element = ei->move_leave_element;
8723
8724     // this makes it possible to leave the removed element again
8725     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8726       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8727
8728     Tile[x][y] = move_leave_element;
8729
8730     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8731       MovDir[x][y] = direction;
8732
8733     InitField(x, y, FALSE);
8734
8735     if (GFX_CRUMBLED(Tile[x][y]))
8736       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8737
8738     if (ELEM_IS_PLAYER(move_leave_element))
8739       RelocatePlayer(x, y, move_leave_element);
8740   }
8741
8742   // do this after checking for left-behind element
8743   ResetGfxAnimation(x, y);      // reset animation values for old field
8744
8745   if (!CAN_MOVE(element) ||
8746       (CAN_FALL(element) && direction == MV_DOWN &&
8747        (element == EL_SPRING ||
8748         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8749         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8750     GfxDir[x][y] = MovDir[newx][newy] = 0;
8751
8752   TEST_DrawLevelField(x, y);
8753   TEST_DrawLevelField(newx, newy);
8754
8755   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8756
8757   // prevent pushed element from moving on in pushed direction
8758   if (pushed_by_player && CAN_MOVE(element) &&
8759       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8760       !(element_info[element].move_pattern & direction))
8761     TurnRound(newx, newy);
8762
8763   // prevent elements on conveyor belt from moving on in last direction
8764   if (pushed_by_conveyor && CAN_FALL(element) &&
8765       direction & MV_HORIZONTAL)
8766     MovDir[newx][newy] = 0;
8767
8768   if (!pushed_by_player)
8769   {
8770     int nextx = newx + dx, nexty = newy + dy;
8771     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8772
8773     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8774
8775     if (CAN_FALL(element) && direction == MV_DOWN)
8776       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8777
8778     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8779       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8780
8781     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8782       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8783   }
8784
8785   if (DONT_TOUCH(element))      // object may be nasty to player or others
8786   {
8787     TestIfBadThingTouchesPlayer(newx, newy);
8788     TestIfBadThingTouchesFriend(newx, newy);
8789
8790     if (!IS_CUSTOM_ELEMENT(element))
8791       TestIfBadThingTouchesOtherBadThing(newx, newy);
8792   }
8793   else if (element == EL_PENGUIN)
8794     TestIfFriendTouchesBadThing(newx, newy);
8795
8796   if (DONT_GET_HIT_BY(element))
8797   {
8798     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8799   }
8800
8801   // give the player one last chance (one more frame) to move away
8802   if (CAN_FALL(element) && direction == MV_DOWN &&
8803       (last_line || (!IS_FREE(x, newy + 1) &&
8804                      (!IS_PLAYER(x, newy + 1) ||
8805                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8806     Impact(x, newy);
8807
8808   if (pushed_by_player && !game.use_change_when_pushing_bug)
8809   {
8810     int push_side = MV_DIR_OPPOSITE(direction);
8811     struct PlayerInfo *player = PLAYERINFO(x, y);
8812
8813     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8814                                player->index_bit, push_side);
8815     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8816                                         player->index_bit, push_side);
8817   }
8818
8819   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8820     MovDelay[newx][newy] = 1;
8821
8822   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8823
8824   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8825   TestIfElementHitsCustomElement(newx, newy, direction);
8826   TestIfPlayerTouchesCustomElement(newx, newy);
8827   TestIfElementTouchesCustomElement(newx, newy);
8828
8829   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8830       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8831     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8832                              MV_DIR_OPPOSITE(direction));
8833 }
8834
8835 int AmoebaNeighbourNr(int ax, int ay)
8836 {
8837   int i;
8838   int element = Tile[ax][ay];
8839   int group_nr = 0;
8840   static int xy[4][2] =
8841   {
8842     { 0, -1 },
8843     { -1, 0 },
8844     { +1, 0 },
8845     { 0, +1 }
8846   };
8847
8848   for (i = 0; i < NUM_DIRECTIONS; i++)
8849   {
8850     int x = ax + xy[i][0];
8851     int y = ay + xy[i][1];
8852
8853     if (!IN_LEV_FIELD(x, y))
8854       continue;
8855
8856     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8857       group_nr = AmoebaNr[x][y];
8858   }
8859
8860   return group_nr;
8861 }
8862
8863 static void AmoebaMerge(int ax, int ay)
8864 {
8865   int i, x, y, xx, yy;
8866   int new_group_nr = AmoebaNr[ax][ay];
8867   static int xy[4][2] =
8868   {
8869     { 0, -1 },
8870     { -1, 0 },
8871     { +1, 0 },
8872     { 0, +1 }
8873   };
8874
8875   if (new_group_nr == 0)
8876     return;
8877
8878   for (i = 0; i < NUM_DIRECTIONS; i++)
8879   {
8880     x = ax + xy[i][0];
8881     y = ay + xy[i][1];
8882
8883     if (!IN_LEV_FIELD(x, y))
8884       continue;
8885
8886     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8887          Tile[x][y] == EL_BD_AMOEBA ||
8888          Tile[x][y] == EL_AMOEBA_DEAD) &&
8889         AmoebaNr[x][y] != new_group_nr)
8890     {
8891       int old_group_nr = AmoebaNr[x][y];
8892
8893       if (old_group_nr == 0)
8894         return;
8895
8896       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8897       AmoebaCnt[old_group_nr] = 0;
8898       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8899       AmoebaCnt2[old_group_nr] = 0;
8900
8901       SCAN_PLAYFIELD(xx, yy)
8902       {
8903         if (AmoebaNr[xx][yy] == old_group_nr)
8904           AmoebaNr[xx][yy] = new_group_nr;
8905       }
8906     }
8907   }
8908 }
8909
8910 void AmoebaToDiamond(int ax, int ay)
8911 {
8912   int i, x, y;
8913
8914   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8915   {
8916     int group_nr = AmoebaNr[ax][ay];
8917
8918 #ifdef DEBUG
8919     if (group_nr == 0)
8920     {
8921       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8922       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8923
8924       return;
8925     }
8926 #endif
8927
8928     SCAN_PLAYFIELD(x, y)
8929     {
8930       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8931       {
8932         AmoebaNr[x][y] = 0;
8933         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8934       }
8935     }
8936
8937     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8938                             SND_AMOEBA_TURNING_TO_GEM :
8939                             SND_AMOEBA_TURNING_TO_ROCK));
8940     Bang(ax, ay);
8941   }
8942   else
8943   {
8944     static int xy[4][2] =
8945     {
8946       { 0, -1 },
8947       { -1, 0 },
8948       { +1, 0 },
8949       { 0, +1 }
8950     };
8951
8952     for (i = 0; i < NUM_DIRECTIONS; i++)
8953     {
8954       x = ax + xy[i][0];
8955       y = ay + xy[i][1];
8956
8957       if (!IN_LEV_FIELD(x, y))
8958         continue;
8959
8960       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8961       {
8962         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8963                               SND_AMOEBA_TURNING_TO_GEM :
8964                               SND_AMOEBA_TURNING_TO_ROCK));
8965         Bang(x, y);
8966       }
8967     }
8968   }
8969 }
8970
8971 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8972 {
8973   int x, y;
8974   int group_nr = AmoebaNr[ax][ay];
8975   boolean done = FALSE;
8976
8977 #ifdef DEBUG
8978   if (group_nr == 0)
8979   {
8980     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8981     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8982
8983     return;
8984   }
8985 #endif
8986
8987   SCAN_PLAYFIELD(x, y)
8988   {
8989     if (AmoebaNr[x][y] == group_nr &&
8990         (Tile[x][y] == EL_AMOEBA_DEAD ||
8991          Tile[x][y] == EL_BD_AMOEBA ||
8992          Tile[x][y] == EL_AMOEBA_GROWING))
8993     {
8994       AmoebaNr[x][y] = 0;
8995       Tile[x][y] = new_element;
8996       InitField(x, y, FALSE);
8997       TEST_DrawLevelField(x, y);
8998       done = TRUE;
8999     }
9000   }
9001
9002   if (done)
9003     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9004                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9005                             SND_BD_AMOEBA_TURNING_TO_GEM));
9006 }
9007
9008 static void AmoebaGrowing(int x, int y)
9009 {
9010   static unsigned int sound_delay = 0;
9011   static unsigned int sound_delay_value = 0;
9012
9013   if (!MovDelay[x][y])          // start new growing cycle
9014   {
9015     MovDelay[x][y] = 7;
9016
9017     if (DelayReached(&sound_delay, sound_delay_value))
9018     {
9019       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9020       sound_delay_value = 30;
9021     }
9022   }
9023
9024   if (MovDelay[x][y])           // wait some time before growing bigger
9025   {
9026     MovDelay[x][y]--;
9027     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9028     {
9029       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9030                                            6 - MovDelay[x][y]);
9031
9032       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9033     }
9034
9035     if (!MovDelay[x][y])
9036     {
9037       Tile[x][y] = Store[x][y];
9038       Store[x][y] = 0;
9039       TEST_DrawLevelField(x, y);
9040     }
9041   }
9042 }
9043
9044 static void AmoebaShrinking(int x, int y)
9045 {
9046   static unsigned int sound_delay = 0;
9047   static unsigned int sound_delay_value = 0;
9048
9049   if (!MovDelay[x][y])          // start new shrinking cycle
9050   {
9051     MovDelay[x][y] = 7;
9052
9053     if (DelayReached(&sound_delay, sound_delay_value))
9054       sound_delay_value = 30;
9055   }
9056
9057   if (MovDelay[x][y])           // wait some time before shrinking
9058   {
9059     MovDelay[x][y]--;
9060     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9061     {
9062       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9063                                            6 - MovDelay[x][y]);
9064
9065       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9066     }
9067
9068     if (!MovDelay[x][y])
9069     {
9070       Tile[x][y] = EL_EMPTY;
9071       TEST_DrawLevelField(x, y);
9072
9073       // don't let mole enter this field in this cycle;
9074       // (give priority to objects falling to this field from above)
9075       Stop[x][y] = TRUE;
9076     }
9077   }
9078 }
9079
9080 static void AmoebaReproduce(int ax, int ay)
9081 {
9082   int i;
9083   int element = Tile[ax][ay];
9084   int graphic = el2img(element);
9085   int newax = ax, neway = ay;
9086   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9087   static int xy[4][2] =
9088   {
9089     { 0, -1 },
9090     { -1, 0 },
9091     { +1, 0 },
9092     { 0, +1 }
9093   };
9094
9095   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9096   {
9097     Tile[ax][ay] = EL_AMOEBA_DEAD;
9098     TEST_DrawLevelField(ax, ay);
9099     return;
9100   }
9101
9102   if (IS_ANIMATED(graphic))
9103     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9104
9105   if (!MovDelay[ax][ay])        // start making new amoeba field
9106     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9107
9108   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9109   {
9110     MovDelay[ax][ay]--;
9111     if (MovDelay[ax][ay])
9112       return;
9113   }
9114
9115   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9116   {
9117     int start = RND(4);
9118     int x = ax + xy[start][0];
9119     int y = ay + xy[start][1];
9120
9121     if (!IN_LEV_FIELD(x, y))
9122       return;
9123
9124     if (IS_FREE(x, y) ||
9125         CAN_GROW_INTO(Tile[x][y]) ||
9126         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9127         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9128     {
9129       newax = x;
9130       neway = y;
9131     }
9132
9133     if (newax == ax && neway == ay)
9134       return;
9135   }
9136   else                          // normal or "filled" (BD style) amoeba
9137   {
9138     int start = RND(4);
9139     boolean waiting_for_player = FALSE;
9140
9141     for (i = 0; i < NUM_DIRECTIONS; i++)
9142     {
9143       int j = (start + i) % 4;
9144       int x = ax + xy[j][0];
9145       int y = ay + xy[j][1];
9146
9147       if (!IN_LEV_FIELD(x, y))
9148         continue;
9149
9150       if (IS_FREE(x, y) ||
9151           CAN_GROW_INTO(Tile[x][y]) ||
9152           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9153           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9154       {
9155         newax = x;
9156         neway = y;
9157         break;
9158       }
9159       else if (IS_PLAYER(x, y))
9160         waiting_for_player = TRUE;
9161     }
9162
9163     if (newax == ax && neway == ay)             // amoeba cannot grow
9164     {
9165       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9166       {
9167         Tile[ax][ay] = EL_AMOEBA_DEAD;
9168         TEST_DrawLevelField(ax, ay);
9169         AmoebaCnt[AmoebaNr[ax][ay]]--;
9170
9171         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9172         {
9173           if (element == EL_AMOEBA_FULL)
9174             AmoebaToDiamond(ax, ay);
9175           else if (element == EL_BD_AMOEBA)
9176             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9177         }
9178       }
9179       return;
9180     }
9181     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9182     {
9183       // amoeba gets larger by growing in some direction
9184
9185       int new_group_nr = AmoebaNr[ax][ay];
9186
9187 #ifdef DEBUG
9188   if (new_group_nr == 0)
9189   {
9190     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9191           newax, neway);
9192     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9193
9194     return;
9195   }
9196 #endif
9197
9198       AmoebaNr[newax][neway] = new_group_nr;
9199       AmoebaCnt[new_group_nr]++;
9200       AmoebaCnt2[new_group_nr]++;
9201
9202       // if amoeba touches other amoeba(s) after growing, unify them
9203       AmoebaMerge(newax, neway);
9204
9205       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9206       {
9207         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9208         return;
9209       }
9210     }
9211   }
9212
9213   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9214       (neway == lev_fieldy - 1 && newax != ax))
9215   {
9216     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9217     Store[newax][neway] = element;
9218   }
9219   else if (neway == ay || element == EL_EMC_DRIPPER)
9220   {
9221     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9222
9223     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9224   }
9225   else
9226   {
9227     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9228     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9229     Store[ax][ay] = EL_AMOEBA_DROP;
9230     ContinueMoving(ax, ay);
9231     return;
9232   }
9233
9234   TEST_DrawLevelField(newax, neway);
9235 }
9236
9237 static void Life(int ax, int ay)
9238 {
9239   int x1, y1, x2, y2;
9240   int life_time = 40;
9241   int element = Tile[ax][ay];
9242   int graphic = el2img(element);
9243   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9244                          level.biomaze);
9245   boolean changed = FALSE;
9246
9247   if (IS_ANIMATED(graphic))
9248     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9249
9250   if (Stop[ax][ay])
9251     return;
9252
9253   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9254     MovDelay[ax][ay] = life_time;
9255
9256   if (MovDelay[ax][ay])         // wait some time before next cycle
9257   {
9258     MovDelay[ax][ay]--;
9259     if (MovDelay[ax][ay])
9260       return;
9261   }
9262
9263   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9264   {
9265     int xx = ax+x1, yy = ay+y1;
9266     int old_element = Tile[xx][yy];
9267     int num_neighbours = 0;
9268
9269     if (!IN_LEV_FIELD(xx, yy))
9270       continue;
9271
9272     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9273     {
9274       int x = xx+x2, y = yy+y2;
9275
9276       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9277         continue;
9278
9279       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9280       boolean is_neighbour = FALSE;
9281
9282       if (level.use_life_bugs)
9283         is_neighbour =
9284           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9285            (IS_FREE(x, y)                             &&  Stop[x][y]));
9286       else
9287         is_neighbour =
9288           (Last[x][y] == element || is_player_cell);
9289
9290       if (is_neighbour)
9291         num_neighbours++;
9292     }
9293
9294     boolean is_free = FALSE;
9295
9296     if (level.use_life_bugs)
9297       is_free = (IS_FREE(xx, yy));
9298     else
9299       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9300
9301     if (xx == ax && yy == ay)           // field in the middle
9302     {
9303       if (num_neighbours < life_parameter[0] ||
9304           num_neighbours > life_parameter[1])
9305       {
9306         Tile[xx][yy] = EL_EMPTY;
9307         if (Tile[xx][yy] != old_element)
9308           TEST_DrawLevelField(xx, yy);
9309         Stop[xx][yy] = TRUE;
9310         changed = TRUE;
9311       }
9312     }
9313     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9314     {                                   // free border field
9315       if (num_neighbours >= life_parameter[2] &&
9316           num_neighbours <= life_parameter[3])
9317       {
9318         Tile[xx][yy] = element;
9319         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9320         if (Tile[xx][yy] != old_element)
9321           TEST_DrawLevelField(xx, yy);
9322         Stop[xx][yy] = TRUE;
9323         changed = TRUE;
9324       }
9325     }
9326   }
9327
9328   if (changed)
9329     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9330                    SND_GAME_OF_LIFE_GROWING);
9331 }
9332
9333 static void InitRobotWheel(int x, int y)
9334 {
9335   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9336 }
9337
9338 static void RunRobotWheel(int x, int y)
9339 {
9340   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9341 }
9342
9343 static void StopRobotWheel(int x, int y)
9344 {
9345   if (game.robot_wheel_x == x &&
9346       game.robot_wheel_y == y)
9347   {
9348     game.robot_wheel_x = -1;
9349     game.robot_wheel_y = -1;
9350     game.robot_wheel_active = FALSE;
9351   }
9352 }
9353
9354 static void InitTimegateWheel(int x, int y)
9355 {
9356   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9357 }
9358
9359 static void RunTimegateWheel(int x, int y)
9360 {
9361   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9362 }
9363
9364 static void InitMagicBallDelay(int x, int y)
9365 {
9366   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9367 }
9368
9369 static void ActivateMagicBall(int bx, int by)
9370 {
9371   int x, y;
9372
9373   if (level.ball_random)
9374   {
9375     int pos_border = RND(8);    // select one of the eight border elements
9376     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9377     int xx = pos_content % 3;
9378     int yy = pos_content / 3;
9379
9380     x = bx - 1 + xx;
9381     y = by - 1 + yy;
9382
9383     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9384       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9385   }
9386   else
9387   {
9388     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9389     {
9390       int xx = x - bx + 1;
9391       int yy = y - by + 1;
9392
9393       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9394         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9395     }
9396   }
9397
9398   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9399 }
9400
9401 static void CheckExit(int x, int y)
9402 {
9403   if (game.gems_still_needed > 0 ||
9404       game.sokoban_fields_still_needed > 0 ||
9405       game.sokoban_objects_still_needed > 0 ||
9406       game.lights_still_needed > 0)
9407   {
9408     int element = Tile[x][y];
9409     int graphic = el2img(element);
9410
9411     if (IS_ANIMATED(graphic))
9412       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9413
9414     return;
9415   }
9416
9417   // do not re-open exit door closed after last player
9418   if (game.all_players_gone)
9419     return;
9420
9421   Tile[x][y] = EL_EXIT_OPENING;
9422
9423   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9424 }
9425
9426 static void CheckExitEM(int x, int y)
9427 {
9428   if (game.gems_still_needed > 0 ||
9429       game.sokoban_fields_still_needed > 0 ||
9430       game.sokoban_objects_still_needed > 0 ||
9431       game.lights_still_needed > 0)
9432   {
9433     int element = Tile[x][y];
9434     int graphic = el2img(element);
9435
9436     if (IS_ANIMATED(graphic))
9437       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9438
9439     return;
9440   }
9441
9442   // do not re-open exit door closed after last player
9443   if (game.all_players_gone)
9444     return;
9445
9446   Tile[x][y] = EL_EM_EXIT_OPENING;
9447
9448   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9449 }
9450
9451 static void CheckExitSteel(int x, int y)
9452 {
9453   if (game.gems_still_needed > 0 ||
9454       game.sokoban_fields_still_needed > 0 ||
9455       game.sokoban_objects_still_needed > 0 ||
9456       game.lights_still_needed > 0)
9457   {
9458     int element = Tile[x][y];
9459     int graphic = el2img(element);
9460
9461     if (IS_ANIMATED(graphic))
9462       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9463
9464     return;
9465   }
9466
9467   // do not re-open exit door closed after last player
9468   if (game.all_players_gone)
9469     return;
9470
9471   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9472
9473   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9474 }
9475
9476 static void CheckExitSteelEM(int x, int y)
9477 {
9478   if (game.gems_still_needed > 0 ||
9479       game.sokoban_fields_still_needed > 0 ||
9480       game.sokoban_objects_still_needed > 0 ||
9481       game.lights_still_needed > 0)
9482   {
9483     int element = Tile[x][y];
9484     int graphic = el2img(element);
9485
9486     if (IS_ANIMATED(graphic))
9487       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9488
9489     return;
9490   }
9491
9492   // do not re-open exit door closed after last player
9493   if (game.all_players_gone)
9494     return;
9495
9496   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9497
9498   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9499 }
9500
9501 static void CheckExitSP(int x, int y)
9502 {
9503   if (game.gems_still_needed > 0)
9504   {
9505     int element = Tile[x][y];
9506     int graphic = el2img(element);
9507
9508     if (IS_ANIMATED(graphic))
9509       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9510
9511     return;
9512   }
9513
9514   // do not re-open exit door closed after last player
9515   if (game.all_players_gone)
9516     return;
9517
9518   Tile[x][y] = EL_SP_EXIT_OPENING;
9519
9520   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9521 }
9522
9523 static void CloseAllOpenTimegates(void)
9524 {
9525   int x, y;
9526
9527   SCAN_PLAYFIELD(x, y)
9528   {
9529     int element = Tile[x][y];
9530
9531     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9532     {
9533       Tile[x][y] = EL_TIMEGATE_CLOSING;
9534
9535       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9536     }
9537   }
9538 }
9539
9540 static void DrawTwinkleOnField(int x, int y)
9541 {
9542   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9543     return;
9544
9545   if (Tile[x][y] == EL_BD_DIAMOND)
9546     return;
9547
9548   if (MovDelay[x][y] == 0)      // next animation frame
9549     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9550
9551   if (MovDelay[x][y] != 0)      // wait some time before next frame
9552   {
9553     MovDelay[x][y]--;
9554
9555     DrawLevelElementAnimation(x, y, Tile[x][y]);
9556
9557     if (MovDelay[x][y] != 0)
9558     {
9559       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9560                                            10 - MovDelay[x][y]);
9561
9562       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9563     }
9564   }
9565 }
9566
9567 static void MauerWaechst(int x, int y)
9568 {
9569   int delay = 6;
9570
9571   if (!MovDelay[x][y])          // next animation frame
9572     MovDelay[x][y] = 3 * delay;
9573
9574   if (MovDelay[x][y])           // wait some time before next frame
9575   {
9576     MovDelay[x][y]--;
9577
9578     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9579     {
9580       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9581       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9582
9583       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9584     }
9585
9586     if (!MovDelay[x][y])
9587     {
9588       if (MovDir[x][y] == MV_LEFT)
9589       {
9590         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9591           TEST_DrawLevelField(x - 1, y);
9592       }
9593       else if (MovDir[x][y] == MV_RIGHT)
9594       {
9595         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9596           TEST_DrawLevelField(x + 1, y);
9597       }
9598       else if (MovDir[x][y] == MV_UP)
9599       {
9600         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9601           TEST_DrawLevelField(x, y - 1);
9602       }
9603       else
9604       {
9605         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9606           TEST_DrawLevelField(x, y + 1);
9607       }
9608
9609       Tile[x][y] = Store[x][y];
9610       Store[x][y] = 0;
9611       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9612       TEST_DrawLevelField(x, y);
9613     }
9614   }
9615 }
9616
9617 static void MauerAbleger(int ax, int ay)
9618 {
9619   int element = Tile[ax][ay];
9620   int graphic = el2img(element);
9621   boolean oben_frei = FALSE, unten_frei = FALSE;
9622   boolean links_frei = FALSE, rechts_frei = FALSE;
9623   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9624   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9625   boolean new_wall = FALSE;
9626
9627   if (IS_ANIMATED(graphic))
9628     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9629
9630   if (!MovDelay[ax][ay])        // start building new wall
9631     MovDelay[ax][ay] = 6;
9632
9633   if (MovDelay[ax][ay])         // wait some time before building new wall
9634   {
9635     MovDelay[ax][ay]--;
9636     if (MovDelay[ax][ay])
9637       return;
9638   }
9639
9640   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9641     oben_frei = TRUE;
9642   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9643     unten_frei = TRUE;
9644   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9645     links_frei = TRUE;
9646   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9647     rechts_frei = TRUE;
9648
9649   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9650       element == EL_EXPANDABLE_WALL_ANY)
9651   {
9652     if (oben_frei)
9653     {
9654       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9655       Store[ax][ay-1] = element;
9656       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9657       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9658         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9659                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9660       new_wall = TRUE;
9661     }
9662     if (unten_frei)
9663     {
9664       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9665       Store[ax][ay+1] = element;
9666       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9667       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9668         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9669                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9670       new_wall = TRUE;
9671     }
9672   }
9673
9674   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9675       element == EL_EXPANDABLE_WALL_ANY ||
9676       element == EL_EXPANDABLE_WALL ||
9677       element == EL_BD_EXPANDABLE_WALL)
9678   {
9679     if (links_frei)
9680     {
9681       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9682       Store[ax-1][ay] = element;
9683       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9684       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9685         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9686                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9687       new_wall = TRUE;
9688     }
9689
9690     if (rechts_frei)
9691     {
9692       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9693       Store[ax+1][ay] = element;
9694       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9695       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9696         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9697                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9698       new_wall = TRUE;
9699     }
9700   }
9701
9702   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9703     TEST_DrawLevelField(ax, ay);
9704
9705   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9706     oben_massiv = TRUE;
9707   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9708     unten_massiv = TRUE;
9709   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9710     links_massiv = TRUE;
9711   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9712     rechts_massiv = TRUE;
9713
9714   if (((oben_massiv && unten_massiv) ||
9715        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9716        element == EL_EXPANDABLE_WALL) &&
9717       ((links_massiv && rechts_massiv) ||
9718        element == EL_EXPANDABLE_WALL_VERTICAL))
9719     Tile[ax][ay] = EL_WALL;
9720
9721   if (new_wall)
9722     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9723 }
9724
9725 static void MauerAblegerStahl(int ax, int ay)
9726 {
9727   int element = Tile[ax][ay];
9728   int graphic = el2img(element);
9729   boolean oben_frei = FALSE, unten_frei = FALSE;
9730   boolean links_frei = FALSE, rechts_frei = FALSE;
9731   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9732   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9733   boolean new_wall = FALSE;
9734
9735   if (IS_ANIMATED(graphic))
9736     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9737
9738   if (!MovDelay[ax][ay])        // start building new wall
9739     MovDelay[ax][ay] = 6;
9740
9741   if (MovDelay[ax][ay])         // wait some time before building new wall
9742   {
9743     MovDelay[ax][ay]--;
9744     if (MovDelay[ax][ay])
9745       return;
9746   }
9747
9748   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9749     oben_frei = TRUE;
9750   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9751     unten_frei = TRUE;
9752   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9753     links_frei = TRUE;
9754   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9755     rechts_frei = TRUE;
9756
9757   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9758       element == EL_EXPANDABLE_STEELWALL_ANY)
9759   {
9760     if (oben_frei)
9761     {
9762       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9763       Store[ax][ay-1] = element;
9764       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9765       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9766         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9767                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9768       new_wall = TRUE;
9769     }
9770     if (unten_frei)
9771     {
9772       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9773       Store[ax][ay+1] = element;
9774       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9775       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9776         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9777                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9778       new_wall = TRUE;
9779     }
9780   }
9781
9782   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9783       element == EL_EXPANDABLE_STEELWALL_ANY)
9784   {
9785     if (links_frei)
9786     {
9787       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9788       Store[ax-1][ay] = element;
9789       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9790       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9791         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9792                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9793       new_wall = TRUE;
9794     }
9795
9796     if (rechts_frei)
9797     {
9798       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9799       Store[ax+1][ay] = element;
9800       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9801       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9802         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9803                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9804       new_wall = TRUE;
9805     }
9806   }
9807
9808   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9809     oben_massiv = TRUE;
9810   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9811     unten_massiv = TRUE;
9812   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9813     links_massiv = TRUE;
9814   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9815     rechts_massiv = TRUE;
9816
9817   if (((oben_massiv && unten_massiv) ||
9818        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9819       ((links_massiv && rechts_massiv) ||
9820        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9821     Tile[ax][ay] = EL_STEELWALL;
9822
9823   if (new_wall)
9824     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9825 }
9826
9827 static void CheckForDragon(int x, int y)
9828 {
9829   int i, j;
9830   boolean dragon_found = FALSE;
9831   static int xy[4][2] =
9832   {
9833     { 0, -1 },
9834     { -1, 0 },
9835     { +1, 0 },
9836     { 0, +1 }
9837   };
9838
9839   for (i = 0; i < NUM_DIRECTIONS; i++)
9840   {
9841     for (j = 0; j < 4; j++)
9842     {
9843       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9844
9845       if (IN_LEV_FIELD(xx, yy) &&
9846           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9847       {
9848         if (Tile[xx][yy] == EL_DRAGON)
9849           dragon_found = TRUE;
9850       }
9851       else
9852         break;
9853     }
9854   }
9855
9856   if (!dragon_found)
9857   {
9858     for (i = 0; i < NUM_DIRECTIONS; i++)
9859     {
9860       for (j = 0; j < 3; j++)
9861       {
9862         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9863   
9864         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9865         {
9866           Tile[xx][yy] = EL_EMPTY;
9867           TEST_DrawLevelField(xx, yy);
9868         }
9869         else
9870           break;
9871       }
9872     }
9873   }
9874 }
9875
9876 static void InitBuggyBase(int x, int y)
9877 {
9878   int element = Tile[x][y];
9879   int activating_delay = FRAMES_PER_SECOND / 4;
9880
9881   ChangeDelay[x][y] =
9882     (element == EL_SP_BUGGY_BASE ?
9883      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9884      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9885      activating_delay :
9886      element == EL_SP_BUGGY_BASE_ACTIVE ?
9887      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9888 }
9889
9890 static void WarnBuggyBase(int x, int y)
9891 {
9892   int i;
9893   static int xy[4][2] =
9894   {
9895     { 0, -1 },
9896     { -1, 0 },
9897     { +1, 0 },
9898     { 0, +1 }
9899   };
9900
9901   for (i = 0; i < NUM_DIRECTIONS; i++)
9902   {
9903     int xx = x + xy[i][0];
9904     int yy = y + xy[i][1];
9905
9906     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9907     {
9908       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9909
9910       break;
9911     }
9912   }
9913 }
9914
9915 static void InitTrap(int x, int y)
9916 {
9917   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9918 }
9919
9920 static void ActivateTrap(int x, int y)
9921 {
9922   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9923 }
9924
9925 static void ChangeActiveTrap(int x, int y)
9926 {
9927   int graphic = IMG_TRAP_ACTIVE;
9928
9929   // if new animation frame was drawn, correct crumbled sand border
9930   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9931     TEST_DrawLevelFieldCrumbled(x, y);
9932 }
9933
9934 static int getSpecialActionElement(int element, int number, int base_element)
9935 {
9936   return (element != EL_EMPTY ? element :
9937           number != -1 ? base_element + number - 1 :
9938           EL_EMPTY);
9939 }
9940
9941 static int getModifiedActionNumber(int value_old, int operator, int operand,
9942                                    int value_min, int value_max)
9943 {
9944   int value_new = (operator == CA_MODE_SET      ? operand :
9945                    operator == CA_MODE_ADD      ? value_old + operand :
9946                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9947                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9948                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9949                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9950                    value_old);
9951
9952   return (value_new < value_min ? value_min :
9953           value_new > value_max ? value_max :
9954           value_new);
9955 }
9956
9957 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9958 {
9959   struct ElementInfo *ei = &element_info[element];
9960   struct ElementChangeInfo *change = &ei->change_page[page];
9961   int target_element = change->target_element;
9962   int action_type = change->action_type;
9963   int action_mode = change->action_mode;
9964   int action_arg = change->action_arg;
9965   int action_element = change->action_element;
9966   int i;
9967
9968   if (!change->has_action)
9969     return;
9970
9971   // ---------- determine action paramater values -----------------------------
9972
9973   int level_time_value =
9974     (level.time > 0 ? TimeLeft :
9975      TimePlayed);
9976
9977   int action_arg_element_raw =
9978     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9979      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9980      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9981      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9982      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9983      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9984      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9985      EL_EMPTY);
9986   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9987
9988   int action_arg_direction =
9989     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9990      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9991      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9992      change->actual_trigger_side :
9993      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9994      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9995      MV_NONE);
9996
9997   int action_arg_number_min =
9998     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9999      CA_ARG_MIN);
10000
10001   int action_arg_number_max =
10002     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10003      action_type == CA_SET_LEVEL_GEMS ? 999 :
10004      action_type == CA_SET_LEVEL_TIME ? 9999 :
10005      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10006      action_type == CA_SET_CE_VALUE ? 9999 :
10007      action_type == CA_SET_CE_SCORE ? 9999 :
10008      CA_ARG_MAX);
10009
10010   int action_arg_number_reset =
10011     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10012      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10013      action_type == CA_SET_LEVEL_TIME ? level.time :
10014      action_type == CA_SET_LEVEL_SCORE ? 0 :
10015      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10016      action_type == CA_SET_CE_SCORE ? 0 :
10017      0);
10018
10019   int action_arg_number =
10020     (action_arg <= CA_ARG_MAX ? action_arg :
10021      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10022      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10023      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10024      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10025      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10026      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10027      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10028      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10029      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10030      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10031      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10032      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10033      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10034      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10035      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10036      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10037      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10038      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10039      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10040      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10041      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10042      -1);
10043
10044   int action_arg_number_old =
10045     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10046      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10047      action_type == CA_SET_LEVEL_SCORE ? game.score :
10048      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10049      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10050      0);
10051
10052   int action_arg_number_new =
10053     getModifiedActionNumber(action_arg_number_old,
10054                             action_mode, action_arg_number,
10055                             action_arg_number_min, action_arg_number_max);
10056
10057   int trigger_player_bits =
10058     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10059      change->actual_trigger_player_bits : change->trigger_player);
10060
10061   int action_arg_player_bits =
10062     (action_arg >= CA_ARG_PLAYER_1 &&
10063      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10064      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10065      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10066      PLAYER_BITS_ANY);
10067
10068   // ---------- execute action  -----------------------------------------------
10069
10070   switch (action_type)
10071   {
10072     case CA_NO_ACTION:
10073     {
10074       return;
10075     }
10076
10077     // ---------- level actions  ----------------------------------------------
10078
10079     case CA_RESTART_LEVEL:
10080     {
10081       game.restart_level = TRUE;
10082
10083       break;
10084     }
10085
10086     case CA_SHOW_ENVELOPE:
10087     {
10088       int element = getSpecialActionElement(action_arg_element,
10089                                             action_arg_number, EL_ENVELOPE_1);
10090
10091       if (IS_ENVELOPE(element))
10092         local_player->show_envelope = element;
10093
10094       break;
10095     }
10096
10097     case CA_SET_LEVEL_TIME:
10098     {
10099       if (level.time > 0)       // only modify limited time value
10100       {
10101         TimeLeft = action_arg_number_new;
10102
10103         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10104
10105         DisplayGameControlValues();
10106
10107         if (!TimeLeft && setup.time_limit)
10108           for (i = 0; i < MAX_PLAYERS; i++)
10109             KillPlayer(&stored_player[i]);
10110       }
10111
10112       break;
10113     }
10114
10115     case CA_SET_LEVEL_SCORE:
10116     {
10117       game.score = action_arg_number_new;
10118
10119       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10120
10121       DisplayGameControlValues();
10122
10123       break;
10124     }
10125
10126     case CA_SET_LEVEL_GEMS:
10127     {
10128       game.gems_still_needed = action_arg_number_new;
10129
10130       game.snapshot.collected_item = TRUE;
10131
10132       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10133
10134       DisplayGameControlValues();
10135
10136       break;
10137     }
10138
10139     case CA_SET_LEVEL_WIND:
10140     {
10141       game.wind_direction = action_arg_direction;
10142
10143       break;
10144     }
10145
10146     case CA_SET_LEVEL_RANDOM_SEED:
10147     {
10148       // ensure that setting a new random seed while playing is predictable
10149       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10150
10151       break;
10152     }
10153
10154     // ---------- player actions  ---------------------------------------------
10155
10156     case CA_MOVE_PLAYER:
10157     case CA_MOVE_PLAYER_NEW:
10158     {
10159       // automatically move to the next field in specified direction
10160       for (i = 0; i < MAX_PLAYERS; i++)
10161         if (trigger_player_bits & (1 << i))
10162           if (action_type == CA_MOVE_PLAYER ||
10163               stored_player[i].MovPos == 0)
10164             stored_player[i].programmed_action = action_arg_direction;
10165
10166       break;
10167     }
10168
10169     case CA_EXIT_PLAYER:
10170     {
10171       for (i = 0; i < MAX_PLAYERS; i++)
10172         if (action_arg_player_bits & (1 << i))
10173           ExitPlayer(&stored_player[i]);
10174
10175       if (game.players_still_needed == 0)
10176         LevelSolved();
10177
10178       break;
10179     }
10180
10181     case CA_KILL_PLAYER:
10182     {
10183       for (i = 0; i < MAX_PLAYERS; i++)
10184         if (action_arg_player_bits & (1 << i))
10185           KillPlayer(&stored_player[i]);
10186
10187       break;
10188     }
10189
10190     case CA_SET_PLAYER_KEYS:
10191     {
10192       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10193       int element = getSpecialActionElement(action_arg_element,
10194                                             action_arg_number, EL_KEY_1);
10195
10196       if (IS_KEY(element))
10197       {
10198         for (i = 0; i < MAX_PLAYERS; i++)
10199         {
10200           if (trigger_player_bits & (1 << i))
10201           {
10202             stored_player[i].key[KEY_NR(element)] = key_state;
10203
10204             DrawGameDoorValues();
10205           }
10206         }
10207       }
10208
10209       break;
10210     }
10211
10212     case CA_SET_PLAYER_SPEED:
10213     {
10214       for (i = 0; i < MAX_PLAYERS; i++)
10215       {
10216         if (trigger_player_bits & (1 << i))
10217         {
10218           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10219
10220           if (action_arg == CA_ARG_SPEED_FASTER &&
10221               stored_player[i].cannot_move)
10222           {
10223             action_arg_number = STEPSIZE_VERY_SLOW;
10224           }
10225           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10226                    action_arg == CA_ARG_SPEED_FASTER)
10227           {
10228             action_arg_number = 2;
10229             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10230                            CA_MODE_MULTIPLY);
10231           }
10232           else if (action_arg == CA_ARG_NUMBER_RESET)
10233           {
10234             action_arg_number = level.initial_player_stepsize[i];
10235           }
10236
10237           move_stepsize =
10238             getModifiedActionNumber(move_stepsize,
10239                                     action_mode,
10240                                     action_arg_number,
10241                                     action_arg_number_min,
10242                                     action_arg_number_max);
10243
10244           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10245         }
10246       }
10247
10248       break;
10249     }
10250
10251     case CA_SET_PLAYER_SHIELD:
10252     {
10253       for (i = 0; i < MAX_PLAYERS; i++)
10254       {
10255         if (trigger_player_bits & (1 << i))
10256         {
10257           if (action_arg == CA_ARG_SHIELD_OFF)
10258           {
10259             stored_player[i].shield_normal_time_left = 0;
10260             stored_player[i].shield_deadly_time_left = 0;
10261           }
10262           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10263           {
10264             stored_player[i].shield_normal_time_left = 999999;
10265           }
10266           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10267           {
10268             stored_player[i].shield_normal_time_left = 999999;
10269             stored_player[i].shield_deadly_time_left = 999999;
10270           }
10271         }
10272       }
10273
10274       break;
10275     }
10276
10277     case CA_SET_PLAYER_GRAVITY:
10278     {
10279       for (i = 0; i < MAX_PLAYERS; i++)
10280       {
10281         if (trigger_player_bits & (1 << i))
10282         {
10283           stored_player[i].gravity =
10284             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10285              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10286              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10287              stored_player[i].gravity);
10288         }
10289       }
10290
10291       break;
10292     }
10293
10294     case CA_SET_PLAYER_ARTWORK:
10295     {
10296       for (i = 0; i < MAX_PLAYERS; i++)
10297       {
10298         if (trigger_player_bits & (1 << i))
10299         {
10300           int artwork_element = action_arg_element;
10301
10302           if (action_arg == CA_ARG_ELEMENT_RESET)
10303             artwork_element =
10304               (level.use_artwork_element[i] ? level.artwork_element[i] :
10305                stored_player[i].element_nr);
10306
10307           if (stored_player[i].artwork_element != artwork_element)
10308             stored_player[i].Frame = 0;
10309
10310           stored_player[i].artwork_element = artwork_element;
10311
10312           SetPlayerWaiting(&stored_player[i], FALSE);
10313
10314           // set number of special actions for bored and sleeping animation
10315           stored_player[i].num_special_action_bored =
10316             get_num_special_action(artwork_element,
10317                                    ACTION_BORING_1, ACTION_BORING_LAST);
10318           stored_player[i].num_special_action_sleeping =
10319             get_num_special_action(artwork_element,
10320                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10321         }
10322       }
10323
10324       break;
10325     }
10326
10327     case CA_SET_PLAYER_INVENTORY:
10328     {
10329       for (i = 0; i < MAX_PLAYERS; i++)
10330       {
10331         struct PlayerInfo *player = &stored_player[i];
10332         int j, k;
10333
10334         if (trigger_player_bits & (1 << i))
10335         {
10336           int inventory_element = action_arg_element;
10337
10338           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10339               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10340               action_arg == CA_ARG_ELEMENT_ACTION)
10341           {
10342             int element = inventory_element;
10343             int collect_count = element_info[element].collect_count_initial;
10344
10345             if (!IS_CUSTOM_ELEMENT(element))
10346               collect_count = 1;
10347
10348             if (collect_count == 0)
10349               player->inventory_infinite_element = element;
10350             else
10351               for (k = 0; k < collect_count; k++)
10352                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10353                   player->inventory_element[player->inventory_size++] =
10354                     element;
10355           }
10356           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10357                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10358                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10359           {
10360             if (player->inventory_infinite_element != EL_UNDEFINED &&
10361                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10362                                      action_arg_element_raw))
10363               player->inventory_infinite_element = EL_UNDEFINED;
10364
10365             for (k = 0, j = 0; j < player->inventory_size; j++)
10366             {
10367               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10368                                         action_arg_element_raw))
10369                 player->inventory_element[k++] = player->inventory_element[j];
10370             }
10371
10372             player->inventory_size = k;
10373           }
10374           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10375           {
10376             if (player->inventory_size > 0)
10377             {
10378               for (j = 0; j < player->inventory_size - 1; j++)
10379                 player->inventory_element[j] = player->inventory_element[j + 1];
10380
10381               player->inventory_size--;
10382             }
10383           }
10384           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10385           {
10386             if (player->inventory_size > 0)
10387               player->inventory_size--;
10388           }
10389           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10390           {
10391             player->inventory_infinite_element = EL_UNDEFINED;
10392             player->inventory_size = 0;
10393           }
10394           else if (action_arg == CA_ARG_INVENTORY_RESET)
10395           {
10396             player->inventory_infinite_element = EL_UNDEFINED;
10397             player->inventory_size = 0;
10398
10399             if (level.use_initial_inventory[i])
10400             {
10401               for (j = 0; j < level.initial_inventory_size[i]; j++)
10402               {
10403                 int element = level.initial_inventory_content[i][j];
10404                 int collect_count = element_info[element].collect_count_initial;
10405
10406                 if (!IS_CUSTOM_ELEMENT(element))
10407                   collect_count = 1;
10408
10409                 if (collect_count == 0)
10410                   player->inventory_infinite_element = element;
10411                 else
10412                   for (k = 0; k < collect_count; k++)
10413                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10414                       player->inventory_element[player->inventory_size++] =
10415                         element;
10416               }
10417             }
10418           }
10419         }
10420       }
10421
10422       break;
10423     }
10424
10425     // ---------- CE actions  -------------------------------------------------
10426
10427     case CA_SET_CE_VALUE:
10428     {
10429       int last_ce_value = CustomValue[x][y];
10430
10431       CustomValue[x][y] = action_arg_number_new;
10432
10433       if (CustomValue[x][y] != last_ce_value)
10434       {
10435         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10436         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10437
10438         if (CustomValue[x][y] == 0)
10439         {
10440           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10441           ChangeCount[x][y] = 0;        // allow at least one more change
10442
10443           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10444           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10445         }
10446       }
10447
10448       break;
10449     }
10450
10451     case CA_SET_CE_SCORE:
10452     {
10453       int last_ce_score = ei->collect_score;
10454
10455       ei->collect_score = action_arg_number_new;
10456
10457       if (ei->collect_score != last_ce_score)
10458       {
10459         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10460         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10461
10462         if (ei->collect_score == 0)
10463         {
10464           int xx, yy;
10465
10466           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10467           ChangeCount[x][y] = 0;        // allow at least one more change
10468
10469           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10470           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10471
10472           /*
10473             This is a very special case that seems to be a mixture between
10474             CheckElementChange() and CheckTriggeredElementChange(): while
10475             the first one only affects single elements that are triggered
10476             directly, the second one affects multiple elements in the playfield
10477             that are triggered indirectly by another element. This is a third
10478             case: Changing the CE score always affects multiple identical CEs,
10479             so every affected CE must be checked, not only the single CE for
10480             which the CE score was changed in the first place (as every instance
10481             of that CE shares the same CE score, and therefore also can change)!
10482           */
10483           SCAN_PLAYFIELD(xx, yy)
10484           {
10485             if (Tile[xx][yy] == element)
10486               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10487                                  CE_SCORE_GETS_ZERO);
10488           }
10489         }
10490       }
10491
10492       break;
10493     }
10494
10495     case CA_SET_CE_ARTWORK:
10496     {
10497       int artwork_element = action_arg_element;
10498       boolean reset_frame = FALSE;
10499       int xx, yy;
10500
10501       if (action_arg == CA_ARG_ELEMENT_RESET)
10502         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10503                            element);
10504
10505       if (ei->gfx_element != artwork_element)
10506         reset_frame = TRUE;
10507
10508       ei->gfx_element = artwork_element;
10509
10510       SCAN_PLAYFIELD(xx, yy)
10511       {
10512         if (Tile[xx][yy] == element)
10513         {
10514           if (reset_frame)
10515           {
10516             ResetGfxAnimation(xx, yy);
10517             ResetRandomAnimationValue(xx, yy);
10518           }
10519
10520           TEST_DrawLevelField(xx, yy);
10521         }
10522       }
10523
10524       break;
10525     }
10526
10527     // ---------- engine actions  ---------------------------------------------
10528
10529     case CA_SET_ENGINE_SCAN_MODE:
10530     {
10531       InitPlayfieldScanMode(action_arg);
10532
10533       break;
10534     }
10535
10536     default:
10537       break;
10538   }
10539 }
10540
10541 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10542 {
10543   int old_element = Tile[x][y];
10544   int new_element = GetElementFromGroupElement(element);
10545   int previous_move_direction = MovDir[x][y];
10546   int last_ce_value = CustomValue[x][y];
10547   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10548   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10549   boolean add_player_onto_element = (new_element_is_player &&
10550                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10551                                      IS_WALKABLE(old_element));
10552
10553   if (!add_player_onto_element)
10554   {
10555     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10556       RemoveMovingField(x, y);
10557     else
10558       RemoveField(x, y);
10559
10560     Tile[x][y] = new_element;
10561
10562     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10563       MovDir[x][y] = previous_move_direction;
10564
10565     if (element_info[new_element].use_last_ce_value)
10566       CustomValue[x][y] = last_ce_value;
10567
10568     InitField_WithBug1(x, y, FALSE);
10569
10570     new_element = Tile[x][y];   // element may have changed
10571
10572     ResetGfxAnimation(x, y);
10573     ResetRandomAnimationValue(x, y);
10574
10575     TEST_DrawLevelField(x, y);
10576
10577     if (GFX_CRUMBLED(new_element))
10578       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10579   }
10580
10581   // check if element under the player changes from accessible to unaccessible
10582   // (needed for special case of dropping element which then changes)
10583   // (must be checked after creating new element for walkable group elements)
10584   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10585       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10586   {
10587     Bang(x, y);
10588
10589     return;
10590   }
10591
10592   // "ChangeCount" not set yet to allow "entered by player" change one time
10593   if (new_element_is_player)
10594     RelocatePlayer(x, y, new_element);
10595
10596   if (is_change)
10597     ChangeCount[x][y]++;        // count number of changes in the same frame
10598
10599   TestIfBadThingTouchesPlayer(x, y);
10600   TestIfPlayerTouchesCustomElement(x, y);
10601   TestIfElementTouchesCustomElement(x, y);
10602 }
10603
10604 static void CreateField(int x, int y, int element)
10605 {
10606   CreateFieldExt(x, y, element, FALSE);
10607 }
10608
10609 static void CreateElementFromChange(int x, int y, int element)
10610 {
10611   element = GET_VALID_RUNTIME_ELEMENT(element);
10612
10613   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10614   {
10615     int old_element = Tile[x][y];
10616
10617     // prevent changed element from moving in same engine frame
10618     // unless both old and new element can either fall or move
10619     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10620         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10621       Stop[x][y] = TRUE;
10622   }
10623
10624   CreateFieldExt(x, y, element, TRUE);
10625 }
10626
10627 static boolean ChangeElement(int x, int y, int element, int page)
10628 {
10629   struct ElementInfo *ei = &element_info[element];
10630   struct ElementChangeInfo *change = &ei->change_page[page];
10631   int ce_value = CustomValue[x][y];
10632   int ce_score = ei->collect_score;
10633   int target_element;
10634   int old_element = Tile[x][y];
10635
10636   // always use default change event to prevent running into a loop
10637   if (ChangeEvent[x][y] == -1)
10638     ChangeEvent[x][y] = CE_DELAY;
10639
10640   if (ChangeEvent[x][y] == CE_DELAY)
10641   {
10642     // reset actual trigger element, trigger player and action element
10643     change->actual_trigger_element = EL_EMPTY;
10644     change->actual_trigger_player = EL_EMPTY;
10645     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10646     change->actual_trigger_side = CH_SIDE_NONE;
10647     change->actual_trigger_ce_value = 0;
10648     change->actual_trigger_ce_score = 0;
10649   }
10650
10651   // do not change elements more than a specified maximum number of changes
10652   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10653     return FALSE;
10654
10655   ChangeCount[x][y]++;          // count number of changes in the same frame
10656
10657   if (change->explode)
10658   {
10659     Bang(x, y);
10660
10661     return TRUE;
10662   }
10663
10664   if (change->use_target_content)
10665   {
10666     boolean complete_replace = TRUE;
10667     boolean can_replace[3][3];
10668     int xx, yy;
10669
10670     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10671     {
10672       boolean is_empty;
10673       boolean is_walkable;
10674       boolean is_diggable;
10675       boolean is_collectible;
10676       boolean is_removable;
10677       boolean is_destructible;
10678       int ex = x + xx - 1;
10679       int ey = y + yy - 1;
10680       int content_element = change->target_content.e[xx][yy];
10681       int e;
10682
10683       can_replace[xx][yy] = TRUE;
10684
10685       if (ex == x && ey == y)   // do not check changing element itself
10686         continue;
10687
10688       if (content_element == EL_EMPTY_SPACE)
10689       {
10690         can_replace[xx][yy] = FALSE;    // do not replace border with space
10691
10692         continue;
10693       }
10694
10695       if (!IN_LEV_FIELD(ex, ey))
10696       {
10697         can_replace[xx][yy] = FALSE;
10698         complete_replace = FALSE;
10699
10700         continue;
10701       }
10702
10703       e = Tile[ex][ey];
10704
10705       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10706         e = MovingOrBlocked2Element(ex, ey);
10707
10708       is_empty = (IS_FREE(ex, ey) ||
10709                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10710
10711       is_walkable     = (is_empty || IS_WALKABLE(e));
10712       is_diggable     = (is_empty || IS_DIGGABLE(e));
10713       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10714       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10715       is_removable    = (is_diggable || is_collectible);
10716
10717       can_replace[xx][yy] =
10718         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10719           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10720           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10721           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10722           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10723           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10724          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10725
10726       if (!can_replace[xx][yy])
10727         complete_replace = FALSE;
10728     }
10729
10730     if (!change->only_if_complete || complete_replace)
10731     {
10732       boolean something_has_changed = FALSE;
10733
10734       if (change->only_if_complete && change->use_random_replace &&
10735           RND(100) < change->random_percentage)
10736         return FALSE;
10737
10738       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10739       {
10740         int ex = x + xx - 1;
10741         int ey = y + yy - 1;
10742         int content_element;
10743
10744         if (can_replace[xx][yy] && (!change->use_random_replace ||
10745                                     RND(100) < change->random_percentage))
10746         {
10747           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10748             RemoveMovingField(ex, ey);
10749
10750           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10751
10752           content_element = change->target_content.e[xx][yy];
10753           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10754                                               ce_value, ce_score);
10755
10756           CreateElementFromChange(ex, ey, target_element);
10757
10758           something_has_changed = TRUE;
10759
10760           // for symmetry reasons, freeze newly created border elements
10761           if (ex != x || ey != y)
10762             Stop[ex][ey] = TRUE;        // no more moving in this frame
10763         }
10764       }
10765
10766       if (something_has_changed)
10767       {
10768         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10769         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10770       }
10771     }
10772   }
10773   else
10774   {
10775     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10776                                         ce_value, ce_score);
10777
10778     if (element == EL_DIAGONAL_GROWING ||
10779         element == EL_DIAGONAL_SHRINKING)
10780     {
10781       target_element = Store[x][y];
10782
10783       Store[x][y] = EL_EMPTY;
10784     }
10785
10786     CreateElementFromChange(x, y, target_element);
10787
10788     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10789     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10790   }
10791
10792   // this uses direct change before indirect change
10793   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10794
10795   return TRUE;
10796 }
10797
10798 static void HandleElementChange(int x, int y, int page)
10799 {
10800   int element = MovingOrBlocked2Element(x, y);
10801   struct ElementInfo *ei = &element_info[element];
10802   struct ElementChangeInfo *change = &ei->change_page[page];
10803   boolean handle_action_before_change = FALSE;
10804
10805 #ifdef DEBUG
10806   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10807       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10808   {
10809     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10810           x, y, element, element_info[element].token_name);
10811     Debug("game:playing:HandleElementChange", "This should never happen!");
10812   }
10813 #endif
10814
10815   // this can happen with classic bombs on walkable, changing elements
10816   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10817   {
10818     return;
10819   }
10820
10821   if (ChangeDelay[x][y] == 0)           // initialize element change
10822   {
10823     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10824
10825     if (change->can_change)
10826     {
10827       // !!! not clear why graphic animation should be reset at all here !!!
10828       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10829       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10830
10831       /*
10832         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10833
10834         When using an animation frame delay of 1 (this only happens with
10835         "sp_zonk.moving.left/right" in the classic graphics), the default
10836         (non-moving) animation shows wrong animation frames (while the
10837         moving animation, like "sp_zonk.moving.left/right", is correct,
10838         so this graphical bug never shows up with the classic graphics).
10839         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10840         be drawn instead of the correct frames 0,1,2,3. This is caused by
10841         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10842         an element change: First when the change delay ("ChangeDelay[][]")
10843         counter has reached zero after decrementing, then a second time in
10844         the next frame (after "GfxFrame[][]" was already incremented) when
10845         "ChangeDelay[][]" is reset to the initial delay value again.
10846
10847         This causes frame 0 to be drawn twice, while the last frame won't
10848         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10849
10850         As some animations may already be cleverly designed around this bug
10851         (at least the "Snake Bite" snake tail animation does this), it cannot
10852         simply be fixed here without breaking such existing animations.
10853         Unfortunately, it cannot easily be detected if a graphics set was
10854         designed "before" or "after" the bug was fixed. As a workaround,
10855         a new graphics set option "game.graphics_engine_version" was added
10856         to be able to specify the game's major release version for which the
10857         graphics set was designed, which can then be used to decide if the
10858         bugfix should be used (version 4 and above) or not (version 3 or
10859         below, or if no version was specified at all, as with old sets).
10860
10861         (The wrong/fixed animation frames can be tested with the test level set
10862         "test_gfxframe" and level "000", which contains a specially prepared
10863         custom element at level position (x/y) == (11/9) which uses the zonk
10864         animation mentioned above. Using "game.graphics_engine_version: 4"
10865         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10866         This can also be seen from the debug output for this test element.)
10867       */
10868
10869       // when a custom element is about to change (for example by change delay),
10870       // do not reset graphic animation when the custom element is moving
10871       if (game.graphics_engine_version < 4 &&
10872           !IS_MOVING(x, y))
10873       {
10874         ResetGfxAnimation(x, y);
10875         ResetRandomAnimationValue(x, y);
10876       }
10877
10878       if (change->pre_change_function)
10879         change->pre_change_function(x, y);
10880     }
10881   }
10882
10883   ChangeDelay[x][y]--;
10884
10885   if (ChangeDelay[x][y] != 0)           // continue element change
10886   {
10887     if (change->can_change)
10888     {
10889       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10890
10891       if (IS_ANIMATED(graphic))
10892         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10893
10894       if (change->change_function)
10895         change->change_function(x, y);
10896     }
10897   }
10898   else                                  // finish element change
10899   {
10900     if (ChangePage[x][y] != -1)         // remember page from delayed change
10901     {
10902       page = ChangePage[x][y];
10903       ChangePage[x][y] = -1;
10904
10905       change = &ei->change_page[page];
10906     }
10907
10908     if (IS_MOVING(x, y))                // never change a running system ;-)
10909     {
10910       ChangeDelay[x][y] = 1;            // try change after next move step
10911       ChangePage[x][y] = page;          // remember page to use for change
10912
10913       return;
10914     }
10915
10916     // special case: set new level random seed before changing element
10917     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10918       handle_action_before_change = TRUE;
10919
10920     if (change->has_action && handle_action_before_change)
10921       ExecuteCustomElementAction(x, y, element, page);
10922
10923     if (change->can_change)
10924     {
10925       if (ChangeElement(x, y, element, page))
10926       {
10927         if (change->post_change_function)
10928           change->post_change_function(x, y);
10929       }
10930     }
10931
10932     if (change->has_action && !handle_action_before_change)
10933       ExecuteCustomElementAction(x, y, element, page);
10934   }
10935 }
10936
10937 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10938                                               int trigger_element,
10939                                               int trigger_event,
10940                                               int trigger_player,
10941                                               int trigger_side,
10942                                               int trigger_page)
10943 {
10944   boolean change_done_any = FALSE;
10945   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10946   int i;
10947
10948   if (!(trigger_events[trigger_element][trigger_event]))
10949     return FALSE;
10950
10951   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10952
10953   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10954   {
10955     int element = EL_CUSTOM_START + i;
10956     boolean change_done = FALSE;
10957     int p;
10958
10959     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10960         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10961       continue;
10962
10963     for (p = 0; p < element_info[element].num_change_pages; p++)
10964     {
10965       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10966
10967       if (change->can_change_or_has_action &&
10968           change->has_event[trigger_event] &&
10969           change->trigger_side & trigger_side &&
10970           change->trigger_player & trigger_player &&
10971           change->trigger_page & trigger_page_bits &&
10972           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10973       {
10974         change->actual_trigger_element = trigger_element;
10975         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10976         change->actual_trigger_player_bits = trigger_player;
10977         change->actual_trigger_side = trigger_side;
10978         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10979         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10980
10981         if ((change->can_change && !change_done) || change->has_action)
10982         {
10983           int x, y;
10984
10985           SCAN_PLAYFIELD(x, y)
10986           {
10987             if (Tile[x][y] == element)
10988             {
10989               if (change->can_change && !change_done)
10990               {
10991                 // if element already changed in this frame, not only prevent
10992                 // another element change (checked in ChangeElement()), but
10993                 // also prevent additional element actions for this element
10994
10995                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10996                     !level.use_action_after_change_bug)
10997                   continue;
10998
10999                 ChangeDelay[x][y] = 1;
11000                 ChangeEvent[x][y] = trigger_event;
11001
11002                 HandleElementChange(x, y, p);
11003               }
11004               else if (change->has_action)
11005               {
11006                 // if element already changed in this frame, not only prevent
11007                 // another element change (checked in ChangeElement()), but
11008                 // also prevent additional element actions for this element
11009
11010                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11011                     !level.use_action_after_change_bug)
11012                   continue;
11013
11014                 ExecuteCustomElementAction(x, y, element, p);
11015                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11016               }
11017             }
11018           }
11019
11020           if (change->can_change)
11021           {
11022             change_done = TRUE;
11023             change_done_any = TRUE;
11024           }
11025         }
11026       }
11027     }
11028   }
11029
11030   RECURSION_LOOP_DETECTION_END();
11031
11032   return change_done_any;
11033 }
11034
11035 static boolean CheckElementChangeExt(int x, int y,
11036                                      int element,
11037                                      int trigger_element,
11038                                      int trigger_event,
11039                                      int trigger_player,
11040                                      int trigger_side)
11041 {
11042   boolean change_done = FALSE;
11043   int p;
11044
11045   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11046       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11047     return FALSE;
11048
11049   if (Tile[x][y] == EL_BLOCKED)
11050   {
11051     Blocked2Moving(x, y, &x, &y);
11052     element = Tile[x][y];
11053   }
11054
11055   // check if element has already changed or is about to change after moving
11056   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11057        Tile[x][y] != element) ||
11058
11059       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11060        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11061         ChangePage[x][y] != -1)))
11062     return FALSE;
11063
11064   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11065
11066   for (p = 0; p < element_info[element].num_change_pages; p++)
11067   {
11068     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11069
11070     /* check trigger element for all events where the element that is checked
11071        for changing interacts with a directly adjacent element -- this is
11072        different to element changes that affect other elements to change on the
11073        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11074     boolean check_trigger_element =
11075       (trigger_event == CE_TOUCHING_X ||
11076        trigger_event == CE_HITTING_X ||
11077        trigger_event == CE_HIT_BY_X ||
11078        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11079
11080     if (change->can_change_or_has_action &&
11081         change->has_event[trigger_event] &&
11082         change->trigger_side & trigger_side &&
11083         change->trigger_player & trigger_player &&
11084         (!check_trigger_element ||
11085          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11086     {
11087       change->actual_trigger_element = trigger_element;
11088       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11089       change->actual_trigger_player_bits = trigger_player;
11090       change->actual_trigger_side = trigger_side;
11091       change->actual_trigger_ce_value = CustomValue[x][y];
11092       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11093
11094       // special case: trigger element not at (x,y) position for some events
11095       if (check_trigger_element)
11096       {
11097         static struct
11098         {
11099           int dx, dy;
11100         } move_xy[] =
11101           {
11102             {  0,  0 },
11103             { -1,  0 },
11104             { +1,  0 },
11105             {  0,  0 },
11106             {  0, -1 },
11107             {  0,  0 }, { 0, 0 }, { 0, 0 },
11108             {  0, +1 }
11109           };
11110
11111         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11112         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11113
11114         change->actual_trigger_ce_value = CustomValue[xx][yy];
11115         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11116       }
11117
11118       if (change->can_change && !change_done)
11119       {
11120         ChangeDelay[x][y] = 1;
11121         ChangeEvent[x][y] = trigger_event;
11122
11123         HandleElementChange(x, y, p);
11124
11125         change_done = TRUE;
11126       }
11127       else if (change->has_action)
11128       {
11129         ExecuteCustomElementAction(x, y, element, p);
11130         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11131       }
11132     }
11133   }
11134
11135   RECURSION_LOOP_DETECTION_END();
11136
11137   return change_done;
11138 }
11139
11140 static void PlayPlayerSound(struct PlayerInfo *player)
11141 {
11142   int jx = player->jx, jy = player->jy;
11143   int sound_element = player->artwork_element;
11144   int last_action = player->last_action_waiting;
11145   int action = player->action_waiting;
11146
11147   if (player->is_waiting)
11148   {
11149     if (action != last_action)
11150       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11151     else
11152       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11153   }
11154   else
11155   {
11156     if (action != last_action)
11157       StopSound(element_info[sound_element].sound[last_action]);
11158
11159     if (last_action == ACTION_SLEEPING)
11160       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11161   }
11162 }
11163
11164 static void PlayAllPlayersSound(void)
11165 {
11166   int i;
11167
11168   for (i = 0; i < MAX_PLAYERS; i++)
11169     if (stored_player[i].active)
11170       PlayPlayerSound(&stored_player[i]);
11171 }
11172
11173 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11174 {
11175   boolean last_waiting = player->is_waiting;
11176   int move_dir = player->MovDir;
11177
11178   player->dir_waiting = move_dir;
11179   player->last_action_waiting = player->action_waiting;
11180
11181   if (is_waiting)
11182   {
11183     if (!last_waiting)          // not waiting -> waiting
11184     {
11185       player->is_waiting = TRUE;
11186
11187       player->frame_counter_bored =
11188         FrameCounter +
11189         game.player_boring_delay_fixed +
11190         GetSimpleRandom(game.player_boring_delay_random);
11191       player->frame_counter_sleeping =
11192         FrameCounter +
11193         game.player_sleeping_delay_fixed +
11194         GetSimpleRandom(game.player_sleeping_delay_random);
11195
11196       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11197     }
11198
11199     if (game.player_sleeping_delay_fixed +
11200         game.player_sleeping_delay_random > 0 &&
11201         player->anim_delay_counter == 0 &&
11202         player->post_delay_counter == 0 &&
11203         FrameCounter >= player->frame_counter_sleeping)
11204       player->is_sleeping = TRUE;
11205     else if (game.player_boring_delay_fixed +
11206              game.player_boring_delay_random > 0 &&
11207              FrameCounter >= player->frame_counter_bored)
11208       player->is_bored = TRUE;
11209
11210     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11211                               player->is_bored ? ACTION_BORING :
11212                               ACTION_WAITING);
11213
11214     if (player->is_sleeping && player->use_murphy)
11215     {
11216       // special case for sleeping Murphy when leaning against non-free tile
11217
11218       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11219           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11220            !IS_MOVING(player->jx - 1, player->jy)))
11221         move_dir = MV_LEFT;
11222       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11223                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11224                 !IS_MOVING(player->jx + 1, player->jy)))
11225         move_dir = MV_RIGHT;
11226       else
11227         player->is_sleeping = FALSE;
11228
11229       player->dir_waiting = move_dir;
11230     }
11231
11232     if (player->is_sleeping)
11233     {
11234       if (player->num_special_action_sleeping > 0)
11235       {
11236         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11237         {
11238           int last_special_action = player->special_action_sleeping;
11239           int num_special_action = player->num_special_action_sleeping;
11240           int special_action =
11241             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11242              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11243              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11244              last_special_action + 1 : ACTION_SLEEPING);
11245           int special_graphic =
11246             el_act_dir2img(player->artwork_element, special_action, move_dir);
11247
11248           player->anim_delay_counter =
11249             graphic_info[special_graphic].anim_delay_fixed +
11250             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11251           player->post_delay_counter =
11252             graphic_info[special_graphic].post_delay_fixed +
11253             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11254
11255           player->special_action_sleeping = special_action;
11256         }
11257
11258         if (player->anim_delay_counter > 0)
11259         {
11260           player->action_waiting = player->special_action_sleeping;
11261           player->anim_delay_counter--;
11262         }
11263         else if (player->post_delay_counter > 0)
11264         {
11265           player->post_delay_counter--;
11266         }
11267       }
11268     }
11269     else if (player->is_bored)
11270     {
11271       if (player->num_special_action_bored > 0)
11272       {
11273         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11274         {
11275           int special_action =
11276             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11277           int special_graphic =
11278             el_act_dir2img(player->artwork_element, special_action, move_dir);
11279
11280           player->anim_delay_counter =
11281             graphic_info[special_graphic].anim_delay_fixed +
11282             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11283           player->post_delay_counter =
11284             graphic_info[special_graphic].post_delay_fixed +
11285             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11286
11287           player->special_action_bored = special_action;
11288         }
11289
11290         if (player->anim_delay_counter > 0)
11291         {
11292           player->action_waiting = player->special_action_bored;
11293           player->anim_delay_counter--;
11294         }
11295         else if (player->post_delay_counter > 0)
11296         {
11297           player->post_delay_counter--;
11298         }
11299       }
11300     }
11301   }
11302   else if (last_waiting)        // waiting -> not waiting
11303   {
11304     player->is_waiting = FALSE;
11305     player->is_bored = FALSE;
11306     player->is_sleeping = FALSE;
11307
11308     player->frame_counter_bored = -1;
11309     player->frame_counter_sleeping = -1;
11310
11311     player->anim_delay_counter = 0;
11312     player->post_delay_counter = 0;
11313
11314     player->dir_waiting = player->MovDir;
11315     player->action_waiting = ACTION_DEFAULT;
11316
11317     player->special_action_bored = ACTION_DEFAULT;
11318     player->special_action_sleeping = ACTION_DEFAULT;
11319   }
11320 }
11321
11322 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11323 {
11324   if ((!player->is_moving  && player->was_moving) ||
11325       (player->MovPos == 0 && player->was_moving) ||
11326       (player->is_snapping && !player->was_snapping) ||
11327       (player->is_dropping && !player->was_dropping))
11328   {
11329     if (!CheckSaveEngineSnapshotToList())
11330       return;
11331
11332     player->was_moving = FALSE;
11333     player->was_snapping = TRUE;
11334     player->was_dropping = TRUE;
11335   }
11336   else
11337   {
11338     if (player->is_moving)
11339       player->was_moving = TRUE;
11340
11341     if (!player->is_snapping)
11342       player->was_snapping = FALSE;
11343
11344     if (!player->is_dropping)
11345       player->was_dropping = FALSE;
11346   }
11347
11348   static struct MouseActionInfo mouse_action_last = { 0 };
11349   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11350   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11351
11352   if (new_released)
11353     CheckSaveEngineSnapshotToList();
11354
11355   mouse_action_last = mouse_action;
11356 }
11357
11358 static void CheckSingleStepMode(struct PlayerInfo *player)
11359 {
11360   if (tape.single_step && tape.recording && !tape.pausing)
11361   {
11362     // as it is called "single step mode", just return to pause mode when the
11363     // player stopped moving after one tile (or never starts moving at all)
11364     // (reverse logic needed here in case single step mode used in team mode)
11365     if (player->is_moving ||
11366         player->is_pushing ||
11367         player->is_dropping_pressed ||
11368         player->effective_mouse_action.button)
11369       game.enter_single_step_mode = FALSE;
11370   }
11371
11372   CheckSaveEngineSnapshot(player);
11373 }
11374
11375 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11376 {
11377   int left      = player_action & JOY_LEFT;
11378   int right     = player_action & JOY_RIGHT;
11379   int up        = player_action & JOY_UP;
11380   int down      = player_action & JOY_DOWN;
11381   int button1   = player_action & JOY_BUTTON_1;
11382   int button2   = player_action & JOY_BUTTON_2;
11383   int dx        = (left ? -1 : right ? 1 : 0);
11384   int dy        = (up   ? -1 : down  ? 1 : 0);
11385
11386   if (!player->active || tape.pausing)
11387     return 0;
11388
11389   if (player_action)
11390   {
11391     if (button1)
11392       SnapField(player, dx, dy);
11393     else
11394     {
11395       if (button2)
11396         DropElement(player);
11397
11398       MovePlayer(player, dx, dy);
11399     }
11400
11401     CheckSingleStepMode(player);
11402
11403     SetPlayerWaiting(player, FALSE);
11404
11405     return player_action;
11406   }
11407   else
11408   {
11409     // no actions for this player (no input at player's configured device)
11410
11411     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11412     SnapField(player, 0, 0);
11413     CheckGravityMovementWhenNotMoving(player);
11414
11415     if (player->MovPos == 0)
11416       SetPlayerWaiting(player, TRUE);
11417
11418     if (player->MovPos == 0)    // needed for tape.playing
11419       player->is_moving = FALSE;
11420
11421     player->is_dropping = FALSE;
11422     player->is_dropping_pressed = FALSE;
11423     player->drop_pressed_delay = 0;
11424
11425     CheckSingleStepMode(player);
11426
11427     return 0;
11428   }
11429 }
11430
11431 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11432                                          byte *tape_action)
11433 {
11434   if (!tape.use_mouse_actions)
11435     return;
11436
11437   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11438   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11439   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11440 }
11441
11442 static void SetTapeActionFromMouseAction(byte *tape_action,
11443                                          struct MouseActionInfo *mouse_action)
11444 {
11445   if (!tape.use_mouse_actions)
11446     return;
11447
11448   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11449   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11450   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11451 }
11452
11453 static void CheckLevelSolved(void)
11454 {
11455   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11456   {
11457     if (game_em.level_solved &&
11458         !game_em.game_over)                             // game won
11459     {
11460       LevelSolved();
11461
11462       game_em.game_over = TRUE;
11463
11464       game.all_players_gone = TRUE;
11465     }
11466
11467     if (game_em.game_over)                              // game lost
11468       game.all_players_gone = TRUE;
11469   }
11470   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11471   {
11472     if (game_sp.level_solved &&
11473         !game_sp.game_over)                             // game won
11474     {
11475       LevelSolved();
11476
11477       game_sp.game_over = TRUE;
11478
11479       game.all_players_gone = TRUE;
11480     }
11481
11482     if (game_sp.game_over)                              // game lost
11483       game.all_players_gone = TRUE;
11484   }
11485   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11486   {
11487     if (game_mm.level_solved &&
11488         !game_mm.game_over)                             // game won
11489     {
11490       LevelSolved();
11491
11492       game_mm.game_over = TRUE;
11493
11494       game.all_players_gone = TRUE;
11495     }
11496
11497     if (game_mm.game_over)                              // game lost
11498       game.all_players_gone = TRUE;
11499   }
11500 }
11501
11502 static void CheckLevelTime(void)
11503 {
11504   int i;
11505
11506   if (TimeFrames >= FRAMES_PER_SECOND)
11507   {
11508     TimeFrames = 0;
11509     TapeTime++;
11510
11511     for (i = 0; i < MAX_PLAYERS; i++)
11512     {
11513       struct PlayerInfo *player = &stored_player[i];
11514
11515       if (SHIELD_ON(player))
11516       {
11517         player->shield_normal_time_left--;
11518
11519         if (player->shield_deadly_time_left > 0)
11520           player->shield_deadly_time_left--;
11521       }
11522     }
11523
11524     if (!game.LevelSolved && !level.use_step_counter)
11525     {
11526       TimePlayed++;
11527
11528       if (TimeLeft > 0)
11529       {
11530         TimeLeft--;
11531
11532         if (TimeLeft <= 10 && setup.time_limit)
11533           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11534
11535         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11536            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11537
11538         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11539
11540         if (!TimeLeft && setup.time_limit)
11541         {
11542           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11543             game_em.lev->killed_out_of_time = TRUE;
11544           else
11545             for (i = 0; i < MAX_PLAYERS; i++)
11546               KillPlayer(&stored_player[i]);
11547         }
11548       }
11549       else if (game.no_time_limit && !game.all_players_gone)
11550       {
11551         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11552       }
11553
11554       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11555     }
11556
11557     if (tape.recording || tape.playing)
11558       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11559   }
11560
11561   if (tape.recording || tape.playing)
11562     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11563
11564   UpdateAndDisplayGameControlValues();
11565 }
11566
11567 void AdvanceFrameAndPlayerCounters(int player_nr)
11568 {
11569   int i;
11570
11571   // advance frame counters (global frame counter and time frame counter)
11572   FrameCounter++;
11573   TimeFrames++;
11574
11575   // advance player counters (counters for move delay, move animation etc.)
11576   for (i = 0; i < MAX_PLAYERS; i++)
11577   {
11578     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11579     int move_delay_value = stored_player[i].move_delay_value;
11580     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11581
11582     if (!advance_player_counters)       // not all players may be affected
11583       continue;
11584
11585     if (move_frames == 0)       // less than one move per game frame
11586     {
11587       int stepsize = TILEX / move_delay_value;
11588       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11589       int count = (stored_player[i].is_moving ?
11590                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11591
11592       if (count % delay == 0)
11593         move_frames = 1;
11594     }
11595
11596     stored_player[i].Frame += move_frames;
11597
11598     if (stored_player[i].MovPos != 0)
11599       stored_player[i].StepFrame += move_frames;
11600
11601     if (stored_player[i].move_delay > 0)
11602       stored_player[i].move_delay--;
11603
11604     // due to bugs in previous versions, counter must count up, not down
11605     if (stored_player[i].push_delay != -1)
11606       stored_player[i].push_delay++;
11607
11608     if (stored_player[i].drop_delay > 0)
11609       stored_player[i].drop_delay--;
11610
11611     if (stored_player[i].is_dropping_pressed)
11612       stored_player[i].drop_pressed_delay++;
11613   }
11614 }
11615
11616 void StartGameActions(boolean init_network_game, boolean record_tape,
11617                       int random_seed)
11618 {
11619   unsigned int new_random_seed = InitRND(random_seed);
11620
11621   if (record_tape)
11622     TapeStartRecording(new_random_seed);
11623
11624   if (init_network_game)
11625   {
11626     SendToServer_LevelFile();
11627     SendToServer_StartPlaying();
11628
11629     return;
11630   }
11631
11632   InitGame();
11633 }
11634
11635 static void GameActionsExt(void)
11636 {
11637 #if 0
11638   static unsigned int game_frame_delay = 0;
11639 #endif
11640   unsigned int game_frame_delay_value;
11641   byte *recorded_player_action;
11642   byte summarized_player_action = 0;
11643   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11644   int i;
11645
11646   // detect endless loops, caused by custom element programming
11647   if (recursion_loop_detected && recursion_loop_depth == 0)
11648   {
11649     char *message = getStringCat3("Internal Error! Element ",
11650                                   EL_NAME(recursion_loop_element),
11651                                   " caused endless loop! Quit the game?");
11652
11653     Warn("element '%s' caused endless loop in game engine",
11654          EL_NAME(recursion_loop_element));
11655
11656     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11657
11658     recursion_loop_detected = FALSE;    // if game should be continued
11659
11660     free(message);
11661
11662     return;
11663   }
11664
11665   if (game.restart_level)
11666     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11667
11668   CheckLevelSolved();
11669
11670   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11671     GameWon();
11672
11673   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11674     TapeStop();
11675
11676   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11677     return;
11678
11679   game_frame_delay_value =
11680     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11681
11682   if (tape.playing && tape.warp_forward && !tape.pausing)
11683     game_frame_delay_value = 0;
11684
11685   SetVideoFrameDelay(game_frame_delay_value);
11686
11687   // (de)activate virtual buttons depending on current game status
11688   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11689   {
11690     if (game.all_players_gone)  // if no players there to be controlled anymore
11691       SetOverlayActive(FALSE);
11692     else if (!tape.playing)     // if game continues after tape stopped playing
11693       SetOverlayActive(TRUE);
11694   }
11695
11696 #if 0
11697 #if 0
11698   // ---------- main game synchronization point ----------
11699
11700   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11701
11702   Debug("game:playing:skip", "skip == %d", skip);
11703
11704 #else
11705   // ---------- main game synchronization point ----------
11706
11707   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11708 #endif
11709 #endif
11710
11711   if (network_playing && !network_player_action_received)
11712   {
11713     // try to get network player actions in time
11714
11715     // last chance to get network player actions without main loop delay
11716     HandleNetworking();
11717
11718     // game was quit by network peer
11719     if (game_status != GAME_MODE_PLAYING)
11720       return;
11721
11722     // check if network player actions still missing and game still running
11723     if (!network_player_action_received && !checkGameEnded())
11724       return;           // failed to get network player actions in time
11725
11726     // do not yet reset "network_player_action_received" (for tape.pausing)
11727   }
11728
11729   if (tape.pausing)
11730     return;
11731
11732   // at this point we know that we really continue executing the game
11733
11734   network_player_action_received = FALSE;
11735
11736   // when playing tape, read previously recorded player input from tape data
11737   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11738
11739   local_player->effective_mouse_action = local_player->mouse_action;
11740
11741   if (recorded_player_action != NULL)
11742     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11743                                  recorded_player_action);
11744
11745   // TapePlayAction() may return NULL when toggling to "pause before death"
11746   if (tape.pausing)
11747     return;
11748
11749   if (tape.set_centered_player)
11750   {
11751     game.centered_player_nr_next = tape.centered_player_nr_next;
11752     game.set_centered_player = TRUE;
11753   }
11754
11755   for (i = 0; i < MAX_PLAYERS; i++)
11756   {
11757     summarized_player_action |= stored_player[i].action;
11758
11759     if (!network_playing && (game.team_mode || tape.playing))
11760       stored_player[i].effective_action = stored_player[i].action;
11761   }
11762
11763   if (network_playing && !checkGameEnded())
11764     SendToServer_MovePlayer(summarized_player_action);
11765
11766   // summarize all actions at local players mapped input device position
11767   // (this allows using different input devices in single player mode)
11768   if (!network.enabled && !game.team_mode)
11769     stored_player[map_player_action[local_player->index_nr]].effective_action =
11770       summarized_player_action;
11771
11772   // summarize all actions at centered player in local team mode
11773   if (tape.recording &&
11774       setup.team_mode && !network.enabled &&
11775       setup.input_on_focus &&
11776       game.centered_player_nr != -1)
11777   {
11778     for (i = 0; i < MAX_PLAYERS; i++)
11779       stored_player[map_player_action[i]].effective_action =
11780         (i == game.centered_player_nr ? summarized_player_action : 0);
11781   }
11782
11783   if (recorded_player_action != NULL)
11784     for (i = 0; i < MAX_PLAYERS; i++)
11785       stored_player[i].effective_action = recorded_player_action[i];
11786
11787   for (i = 0; i < MAX_PLAYERS; i++)
11788   {
11789     tape_action[i] = stored_player[i].effective_action;
11790
11791     /* (this may happen in the RND game engine if a player was not present on
11792        the playfield on level start, but appeared later from a custom element */
11793     if (setup.team_mode &&
11794         tape.recording &&
11795         tape_action[i] &&
11796         !tape.player_participates[i])
11797       tape.player_participates[i] = TRUE;
11798   }
11799
11800   SetTapeActionFromMouseAction(tape_action,
11801                                &local_player->effective_mouse_action);
11802
11803   // only record actions from input devices, but not programmed actions
11804   if (tape.recording)
11805     TapeRecordAction(tape_action);
11806
11807   // remember if game was played (especially after tape stopped playing)
11808   if (!tape.playing && summarized_player_action)
11809     game.GamePlayed = TRUE;
11810
11811 #if USE_NEW_PLAYER_ASSIGNMENTS
11812   // !!! also map player actions in single player mode !!!
11813   // if (game.team_mode)
11814   if (1)
11815   {
11816     byte mapped_action[MAX_PLAYERS];
11817
11818 #if DEBUG_PLAYER_ACTIONS
11819     for (i = 0; i < MAX_PLAYERS; i++)
11820       DebugContinued("", "%d, ", stored_player[i].effective_action);
11821 #endif
11822
11823     for (i = 0; i < MAX_PLAYERS; i++)
11824       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11825
11826     for (i = 0; i < MAX_PLAYERS; i++)
11827       stored_player[i].effective_action = mapped_action[i];
11828
11829 #if DEBUG_PLAYER_ACTIONS
11830     DebugContinued("", "=> ");
11831     for (i = 0; i < MAX_PLAYERS; i++)
11832       DebugContinued("", "%d, ", stored_player[i].effective_action);
11833     DebugContinued("game:playing:player", "\n");
11834 #endif
11835   }
11836 #if DEBUG_PLAYER_ACTIONS
11837   else
11838   {
11839     for (i = 0; i < MAX_PLAYERS; i++)
11840       DebugContinued("", "%d, ", stored_player[i].effective_action);
11841     DebugContinued("game:playing:player", "\n");
11842   }
11843 #endif
11844 #endif
11845
11846   for (i = 0; i < MAX_PLAYERS; i++)
11847   {
11848     // allow engine snapshot in case of changed movement attempt
11849     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11850         (stored_player[i].effective_action & KEY_MOTION))
11851       game.snapshot.changed_action = TRUE;
11852
11853     // allow engine snapshot in case of snapping/dropping attempt
11854     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11855         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11856       game.snapshot.changed_action = TRUE;
11857
11858     game.snapshot.last_action[i] = stored_player[i].effective_action;
11859   }
11860
11861   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11862   {
11863     GameActions_EM_Main();
11864   }
11865   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11866   {
11867     GameActions_SP_Main();
11868   }
11869   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11870   {
11871     GameActions_MM_Main();
11872   }
11873   else
11874   {
11875     GameActions_RND_Main();
11876   }
11877
11878   BlitScreenToBitmap(backbuffer);
11879
11880   CheckLevelSolved();
11881   CheckLevelTime();
11882
11883   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11884
11885   if (global.show_frames_per_second)
11886   {
11887     static unsigned int fps_counter = 0;
11888     static int fps_frames = 0;
11889     unsigned int fps_delay_ms = Counter() - fps_counter;
11890
11891     fps_frames++;
11892
11893     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11894     {
11895       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11896
11897       fps_frames = 0;
11898       fps_counter = Counter();
11899
11900       // always draw FPS to screen after FPS value was updated
11901       redraw_mask |= REDRAW_FPS;
11902     }
11903
11904     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11905     if (GetDrawDeactivationMask() == REDRAW_NONE)
11906       redraw_mask |= REDRAW_FPS;
11907   }
11908 }
11909
11910 static void GameActions_CheckSaveEngineSnapshot(void)
11911 {
11912   if (!game.snapshot.save_snapshot)
11913     return;
11914
11915   // clear flag for saving snapshot _before_ saving snapshot
11916   game.snapshot.save_snapshot = FALSE;
11917
11918   SaveEngineSnapshotToList();
11919 }
11920
11921 void GameActions(void)
11922 {
11923   GameActionsExt();
11924
11925   GameActions_CheckSaveEngineSnapshot();
11926 }
11927
11928 void GameActions_EM_Main(void)
11929 {
11930   byte effective_action[MAX_PLAYERS];
11931   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11932   int i;
11933
11934   for (i = 0; i < MAX_PLAYERS; i++)
11935     effective_action[i] = stored_player[i].effective_action;
11936
11937   GameActions_EM(effective_action, warp_mode);
11938 }
11939
11940 void GameActions_SP_Main(void)
11941 {
11942   byte effective_action[MAX_PLAYERS];
11943   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11944   int i;
11945
11946   for (i = 0; i < MAX_PLAYERS; i++)
11947     effective_action[i] = stored_player[i].effective_action;
11948
11949   GameActions_SP(effective_action, warp_mode);
11950
11951   for (i = 0; i < MAX_PLAYERS; i++)
11952   {
11953     if (stored_player[i].force_dropping)
11954       stored_player[i].action |= KEY_BUTTON_DROP;
11955
11956     stored_player[i].force_dropping = FALSE;
11957   }
11958 }
11959
11960 void GameActions_MM_Main(void)
11961 {
11962   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11963
11964   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11965 }
11966
11967 void GameActions_RND_Main(void)
11968 {
11969   GameActions_RND();
11970 }
11971
11972 void GameActions_RND(void)
11973 {
11974   static struct MouseActionInfo mouse_action_last = { 0 };
11975   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11976   int magic_wall_x = 0, magic_wall_y = 0;
11977   int i, x, y, element, graphic, last_gfx_frame;
11978
11979   InitPlayfieldScanModeVars();
11980
11981   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11982   {
11983     SCAN_PLAYFIELD(x, y)
11984     {
11985       ChangeCount[x][y] = 0;
11986       ChangeEvent[x][y] = -1;
11987     }
11988   }
11989
11990   if (game.set_centered_player)
11991   {
11992     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11993
11994     // switching to "all players" only possible if all players fit to screen
11995     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11996     {
11997       game.centered_player_nr_next = game.centered_player_nr;
11998       game.set_centered_player = FALSE;
11999     }
12000
12001     // do not switch focus to non-existing (or non-active) player
12002     if (game.centered_player_nr_next >= 0 &&
12003         !stored_player[game.centered_player_nr_next].active)
12004     {
12005       game.centered_player_nr_next = game.centered_player_nr;
12006       game.set_centered_player = FALSE;
12007     }
12008   }
12009
12010   if (game.set_centered_player &&
12011       ScreenMovPos == 0)        // screen currently aligned at tile position
12012   {
12013     int sx, sy;
12014
12015     if (game.centered_player_nr_next == -1)
12016     {
12017       setScreenCenteredToAllPlayers(&sx, &sy);
12018     }
12019     else
12020     {
12021       sx = stored_player[game.centered_player_nr_next].jx;
12022       sy = stored_player[game.centered_player_nr_next].jy;
12023     }
12024
12025     game.centered_player_nr = game.centered_player_nr_next;
12026     game.set_centered_player = FALSE;
12027
12028     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12029     DrawGameDoorValues();
12030   }
12031
12032   // check single step mode (set flag and clear again if any player is active)
12033   game.enter_single_step_mode =
12034     (tape.single_step && tape.recording && !tape.pausing);
12035
12036   for (i = 0; i < MAX_PLAYERS; i++)
12037   {
12038     int actual_player_action = stored_player[i].effective_action;
12039
12040 #if 1
12041     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12042        - rnd_equinox_tetrachloride 048
12043        - rnd_equinox_tetrachloride_ii 096
12044        - rnd_emanuel_schmieg 002
12045        - doctor_sloan_ww 001, 020
12046     */
12047     if (stored_player[i].MovPos == 0)
12048       CheckGravityMovement(&stored_player[i]);
12049 #endif
12050
12051     // overwrite programmed action with tape action
12052     if (stored_player[i].programmed_action)
12053       actual_player_action = stored_player[i].programmed_action;
12054
12055     PlayerActions(&stored_player[i], actual_player_action);
12056
12057     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12058   }
12059
12060   // single step pause mode may already have been toggled by "ScrollPlayer()"
12061   if (game.enter_single_step_mode && !tape.pausing)
12062     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12063
12064   ScrollScreen(NULL, SCROLL_GO_ON);
12065
12066   /* for backwards compatibility, the following code emulates a fixed bug that
12067      occured when pushing elements (causing elements that just made their last
12068      pushing step to already (if possible) make their first falling step in the
12069      same game frame, which is bad); this code is also needed to use the famous
12070      "spring push bug" which is used in older levels and might be wanted to be
12071      used also in newer levels, but in this case the buggy pushing code is only
12072      affecting the "spring" element and no other elements */
12073
12074   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12075   {
12076     for (i = 0; i < MAX_PLAYERS; i++)
12077     {
12078       struct PlayerInfo *player = &stored_player[i];
12079       int x = player->jx;
12080       int y = player->jy;
12081
12082       if (player->active && player->is_pushing && player->is_moving &&
12083           IS_MOVING(x, y) &&
12084           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12085            Tile[x][y] == EL_SPRING))
12086       {
12087         ContinueMoving(x, y);
12088
12089         // continue moving after pushing (this is actually a bug)
12090         if (!IS_MOVING(x, y))
12091           Stop[x][y] = FALSE;
12092       }
12093     }
12094   }
12095
12096   SCAN_PLAYFIELD(x, y)
12097   {
12098     Last[x][y] = Tile[x][y];
12099
12100     ChangeCount[x][y] = 0;
12101     ChangeEvent[x][y] = -1;
12102
12103     // this must be handled before main playfield loop
12104     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12105     {
12106       MovDelay[x][y]--;
12107       if (MovDelay[x][y] <= 0)
12108         RemoveField(x, y);
12109     }
12110
12111     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12112     {
12113       MovDelay[x][y]--;
12114       if (MovDelay[x][y] <= 0)
12115       {
12116         int element = Store[x][y];
12117         int move_direction = MovDir[x][y];
12118         int player_index_bit = Store2[x][y];
12119
12120         Store[x][y] = 0;
12121         Store2[x][y] = 0;
12122
12123         RemoveField(x, y);
12124         TEST_DrawLevelField(x, y);
12125
12126         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12127       }
12128     }
12129
12130 #if DEBUG
12131     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12132     {
12133       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12134             x, y);
12135       Debug("game:playing:GameActions_RND", "This should never happen!");
12136
12137       ChangePage[x][y] = -1;
12138     }
12139 #endif
12140
12141     Stop[x][y] = FALSE;
12142     if (WasJustMoving[x][y] > 0)
12143       WasJustMoving[x][y]--;
12144     if (WasJustFalling[x][y] > 0)
12145       WasJustFalling[x][y]--;
12146     if (CheckCollision[x][y] > 0)
12147       CheckCollision[x][y]--;
12148     if (CheckImpact[x][y] > 0)
12149       CheckImpact[x][y]--;
12150
12151     GfxFrame[x][y]++;
12152
12153     /* reset finished pushing action (not done in ContinueMoving() to allow
12154        continuous pushing animation for elements with zero push delay) */
12155     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12156     {
12157       ResetGfxAnimation(x, y);
12158       TEST_DrawLevelField(x, y);
12159     }
12160
12161 #if DEBUG
12162     if (IS_BLOCKED(x, y))
12163     {
12164       int oldx, oldy;
12165
12166       Blocked2Moving(x, y, &oldx, &oldy);
12167       if (!IS_MOVING(oldx, oldy))
12168       {
12169         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12170         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12171         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12172         Debug("game:playing:GameActions_RND", "This should never happen!");
12173       }
12174     }
12175 #endif
12176   }
12177
12178   if (mouse_action.button)
12179   {
12180     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12181     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12182
12183     x = mouse_action.lx;
12184     y = mouse_action.ly;
12185     element = Tile[x][y];
12186
12187     if (new_button)
12188     {
12189       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12190       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12191                                          ch_button);
12192     }
12193
12194     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12195     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12196                                        ch_button);
12197   }
12198
12199   SCAN_PLAYFIELD(x, y)
12200   {
12201     element = Tile[x][y];
12202     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12203     last_gfx_frame = GfxFrame[x][y];
12204
12205     ResetGfxFrame(x, y);
12206
12207     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12208       DrawLevelGraphicAnimation(x, y, graphic);
12209
12210     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12211         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12212       ResetRandomAnimationValue(x, y);
12213
12214     SetRandomAnimationValue(x, y);
12215
12216     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12217
12218     if (IS_INACTIVE(element))
12219     {
12220       if (IS_ANIMATED(graphic))
12221         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12222
12223       continue;
12224     }
12225
12226     // this may take place after moving, so 'element' may have changed
12227     if (IS_CHANGING(x, y) &&
12228         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12229     {
12230       int page = element_info[element].event_page_nr[CE_DELAY];
12231
12232       HandleElementChange(x, y, page);
12233
12234       element = Tile[x][y];
12235       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12236     }
12237
12238     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12239     {
12240       StartMoving(x, y);
12241
12242       element = Tile[x][y];
12243       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12244
12245       if (IS_ANIMATED(graphic) &&
12246           !IS_MOVING(x, y) &&
12247           !Stop[x][y])
12248         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12249
12250       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12251         TEST_DrawTwinkleOnField(x, y);
12252     }
12253     else if (element == EL_ACID)
12254     {
12255       if (!Stop[x][y])
12256         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12257     }
12258     else if ((element == EL_EXIT_OPEN ||
12259               element == EL_EM_EXIT_OPEN ||
12260               element == EL_SP_EXIT_OPEN ||
12261               element == EL_STEEL_EXIT_OPEN ||
12262               element == EL_EM_STEEL_EXIT_OPEN ||
12263               element == EL_SP_TERMINAL ||
12264               element == EL_SP_TERMINAL_ACTIVE ||
12265               element == EL_EXTRA_TIME ||
12266               element == EL_SHIELD_NORMAL ||
12267               element == EL_SHIELD_DEADLY) &&
12268              IS_ANIMATED(graphic))
12269       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12270     else if (IS_MOVING(x, y))
12271       ContinueMoving(x, y);
12272     else if (IS_ACTIVE_BOMB(element))
12273       CheckDynamite(x, y);
12274     else if (element == EL_AMOEBA_GROWING)
12275       AmoebaGrowing(x, y);
12276     else if (element == EL_AMOEBA_SHRINKING)
12277       AmoebaShrinking(x, y);
12278
12279 #if !USE_NEW_AMOEBA_CODE
12280     else if (IS_AMOEBALIVE(element))
12281       AmoebaReproduce(x, y);
12282 #endif
12283
12284     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12285       Life(x, y);
12286     else if (element == EL_EXIT_CLOSED)
12287       CheckExit(x, y);
12288     else if (element == EL_EM_EXIT_CLOSED)
12289       CheckExitEM(x, y);
12290     else if (element == EL_STEEL_EXIT_CLOSED)
12291       CheckExitSteel(x, y);
12292     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12293       CheckExitSteelEM(x, y);
12294     else if (element == EL_SP_EXIT_CLOSED)
12295       CheckExitSP(x, y);
12296     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12297              element == EL_EXPANDABLE_STEELWALL_GROWING)
12298       MauerWaechst(x, y);
12299     else if (element == EL_EXPANDABLE_WALL ||
12300              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12301              element == EL_EXPANDABLE_WALL_VERTICAL ||
12302              element == EL_EXPANDABLE_WALL_ANY ||
12303              element == EL_BD_EXPANDABLE_WALL)
12304       MauerAbleger(x, y);
12305     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12306              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12307              element == EL_EXPANDABLE_STEELWALL_ANY)
12308       MauerAblegerStahl(x, y);
12309     else if (element == EL_FLAMES)
12310       CheckForDragon(x, y);
12311     else if (element == EL_EXPLOSION)
12312       ; // drawing of correct explosion animation is handled separately
12313     else if (element == EL_ELEMENT_SNAPPING ||
12314              element == EL_DIAGONAL_SHRINKING ||
12315              element == EL_DIAGONAL_GROWING)
12316     {
12317       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12318
12319       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12320     }
12321     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12322       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12323
12324     if (IS_BELT_ACTIVE(element))
12325       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12326
12327     if (game.magic_wall_active)
12328     {
12329       int jx = local_player->jx, jy = local_player->jy;
12330
12331       // play the element sound at the position nearest to the player
12332       if ((element == EL_MAGIC_WALL_FULL ||
12333            element == EL_MAGIC_WALL_ACTIVE ||
12334            element == EL_MAGIC_WALL_EMPTYING ||
12335            element == EL_BD_MAGIC_WALL_FULL ||
12336            element == EL_BD_MAGIC_WALL_ACTIVE ||
12337            element == EL_BD_MAGIC_WALL_EMPTYING ||
12338            element == EL_DC_MAGIC_WALL_FULL ||
12339            element == EL_DC_MAGIC_WALL_ACTIVE ||
12340            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12341           ABS(x - jx) + ABS(y - jy) <
12342           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12343       {
12344         magic_wall_x = x;
12345         magic_wall_y = y;
12346       }
12347     }
12348   }
12349
12350 #if USE_NEW_AMOEBA_CODE
12351   // new experimental amoeba growth stuff
12352   if (!(FrameCounter % 8))
12353   {
12354     static unsigned int random = 1684108901;
12355
12356     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12357     {
12358       x = RND(lev_fieldx);
12359       y = RND(lev_fieldy);
12360       element = Tile[x][y];
12361
12362       if (!IS_PLAYER(x,y) &&
12363           (element == EL_EMPTY ||
12364            CAN_GROW_INTO(element) ||
12365            element == EL_QUICKSAND_EMPTY ||
12366            element == EL_QUICKSAND_FAST_EMPTY ||
12367            element == EL_ACID_SPLASH_LEFT ||
12368            element == EL_ACID_SPLASH_RIGHT))
12369       {
12370         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12371             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12372             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12373             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12374           Tile[x][y] = EL_AMOEBA_DROP;
12375       }
12376
12377       random = random * 129 + 1;
12378     }
12379   }
12380 #endif
12381
12382   game.explosions_delayed = FALSE;
12383
12384   SCAN_PLAYFIELD(x, y)
12385   {
12386     element = Tile[x][y];
12387
12388     if (ExplodeField[x][y])
12389       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12390     else if (element == EL_EXPLOSION)
12391       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12392
12393     ExplodeField[x][y] = EX_TYPE_NONE;
12394   }
12395
12396   game.explosions_delayed = TRUE;
12397
12398   if (game.magic_wall_active)
12399   {
12400     if (!(game.magic_wall_time_left % 4))
12401     {
12402       int element = Tile[magic_wall_x][magic_wall_y];
12403
12404       if (element == EL_BD_MAGIC_WALL_FULL ||
12405           element == EL_BD_MAGIC_WALL_ACTIVE ||
12406           element == EL_BD_MAGIC_WALL_EMPTYING)
12407         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12408       else if (element == EL_DC_MAGIC_WALL_FULL ||
12409                element == EL_DC_MAGIC_WALL_ACTIVE ||
12410                element == EL_DC_MAGIC_WALL_EMPTYING)
12411         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12412       else
12413         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12414     }
12415
12416     if (game.magic_wall_time_left > 0)
12417     {
12418       game.magic_wall_time_left--;
12419
12420       if (!game.magic_wall_time_left)
12421       {
12422         SCAN_PLAYFIELD(x, y)
12423         {
12424           element = Tile[x][y];
12425
12426           if (element == EL_MAGIC_WALL_ACTIVE ||
12427               element == EL_MAGIC_WALL_FULL)
12428           {
12429             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12430             TEST_DrawLevelField(x, y);
12431           }
12432           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12433                    element == EL_BD_MAGIC_WALL_FULL)
12434           {
12435             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12436             TEST_DrawLevelField(x, y);
12437           }
12438           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12439                    element == EL_DC_MAGIC_WALL_FULL)
12440           {
12441             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12442             TEST_DrawLevelField(x, y);
12443           }
12444         }
12445
12446         game.magic_wall_active = FALSE;
12447       }
12448     }
12449   }
12450
12451   if (game.light_time_left > 0)
12452   {
12453     game.light_time_left--;
12454
12455     if (game.light_time_left == 0)
12456       RedrawAllLightSwitchesAndInvisibleElements();
12457   }
12458
12459   if (game.timegate_time_left > 0)
12460   {
12461     game.timegate_time_left--;
12462
12463     if (game.timegate_time_left == 0)
12464       CloseAllOpenTimegates();
12465   }
12466
12467   if (game.lenses_time_left > 0)
12468   {
12469     game.lenses_time_left--;
12470
12471     if (game.lenses_time_left == 0)
12472       RedrawAllInvisibleElementsForLenses();
12473   }
12474
12475   if (game.magnify_time_left > 0)
12476   {
12477     game.magnify_time_left--;
12478
12479     if (game.magnify_time_left == 0)
12480       RedrawAllInvisibleElementsForMagnifier();
12481   }
12482
12483   for (i = 0; i < MAX_PLAYERS; i++)
12484   {
12485     struct PlayerInfo *player = &stored_player[i];
12486
12487     if (SHIELD_ON(player))
12488     {
12489       if (player->shield_deadly_time_left)
12490         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12491       else if (player->shield_normal_time_left)
12492         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12493     }
12494   }
12495
12496 #if USE_DELAYED_GFX_REDRAW
12497   SCAN_PLAYFIELD(x, y)
12498   {
12499     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12500     {
12501       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12502          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12503
12504       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12505         DrawLevelField(x, y);
12506
12507       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12508         DrawLevelFieldCrumbled(x, y);
12509
12510       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12511         DrawLevelFieldCrumbledNeighbours(x, y);
12512
12513       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12514         DrawTwinkleOnField(x, y);
12515     }
12516
12517     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12518   }
12519 #endif
12520
12521   DrawAllPlayers();
12522   PlayAllPlayersSound();
12523
12524   for (i = 0; i < MAX_PLAYERS; i++)
12525   {
12526     struct PlayerInfo *player = &stored_player[i];
12527
12528     if (player->show_envelope != 0 && (!player->active ||
12529                                        player->MovPos == 0))
12530     {
12531       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12532
12533       player->show_envelope = 0;
12534     }
12535   }
12536
12537   // use random number generator in every frame to make it less predictable
12538   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12539     RND(1);
12540
12541   mouse_action_last = mouse_action;
12542 }
12543
12544 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12545 {
12546   int min_x = x, min_y = y, max_x = x, max_y = y;
12547   int scr_fieldx = getScreenFieldSizeX();
12548   int scr_fieldy = getScreenFieldSizeY();
12549   int i;
12550
12551   for (i = 0; i < MAX_PLAYERS; i++)
12552   {
12553     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12554
12555     if (!stored_player[i].active || &stored_player[i] == player)
12556       continue;
12557
12558     min_x = MIN(min_x, jx);
12559     min_y = MIN(min_y, jy);
12560     max_x = MAX(max_x, jx);
12561     max_y = MAX(max_y, jy);
12562   }
12563
12564   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12565 }
12566
12567 static boolean AllPlayersInVisibleScreen(void)
12568 {
12569   int i;
12570
12571   for (i = 0; i < MAX_PLAYERS; i++)
12572   {
12573     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12574
12575     if (!stored_player[i].active)
12576       continue;
12577
12578     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12579       return FALSE;
12580   }
12581
12582   return TRUE;
12583 }
12584
12585 void ScrollLevel(int dx, int dy)
12586 {
12587   int scroll_offset = 2 * TILEX_VAR;
12588   int x, y;
12589
12590   BlitBitmap(drawto_field, drawto_field,
12591              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12592              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12593              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12594              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12595              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12596              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12597
12598   if (dx != 0)
12599   {
12600     x = (dx == 1 ? BX1 : BX2);
12601     for (y = BY1; y <= BY2; y++)
12602       DrawScreenField(x, y);
12603   }
12604
12605   if (dy != 0)
12606   {
12607     y = (dy == 1 ? BY1 : BY2);
12608     for (x = BX1; x <= BX2; x++)
12609       DrawScreenField(x, y);
12610   }
12611
12612   redraw_mask |= REDRAW_FIELD;
12613 }
12614
12615 static boolean canFallDown(struct PlayerInfo *player)
12616 {
12617   int jx = player->jx, jy = player->jy;
12618
12619   return (IN_LEV_FIELD(jx, jy + 1) &&
12620           (IS_FREE(jx, jy + 1) ||
12621            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12622           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12623           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12624 }
12625
12626 static boolean canPassField(int x, int y, int move_dir)
12627 {
12628   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12629   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12630   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12631   int nextx = x + dx;
12632   int nexty = y + dy;
12633   int element = Tile[x][y];
12634
12635   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12636           !CAN_MOVE(element) &&
12637           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12638           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12639           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12640 }
12641
12642 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12643 {
12644   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12645   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12646   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12647   int newx = x + dx;
12648   int newy = y + dy;
12649
12650   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12651           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12652           (IS_DIGGABLE(Tile[newx][newy]) ||
12653            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12654            canPassField(newx, newy, move_dir)));
12655 }
12656
12657 static void CheckGravityMovement(struct PlayerInfo *player)
12658 {
12659   if (player->gravity && !player->programmed_action)
12660   {
12661     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12662     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12663     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12664     int jx = player->jx, jy = player->jy;
12665     boolean player_is_moving_to_valid_field =
12666       (!player_is_snapping &&
12667        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12668         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12669     boolean player_can_fall_down = canFallDown(player);
12670
12671     if (player_can_fall_down &&
12672         !player_is_moving_to_valid_field)
12673       player->programmed_action = MV_DOWN;
12674   }
12675 }
12676
12677 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12678 {
12679   return CheckGravityMovement(player);
12680
12681   if (player->gravity && !player->programmed_action)
12682   {
12683     int jx = player->jx, jy = player->jy;
12684     boolean field_under_player_is_free =
12685       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12686     boolean player_is_standing_on_valid_field =
12687       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12688        (IS_WALKABLE(Tile[jx][jy]) &&
12689         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12690
12691     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12692       player->programmed_action = MV_DOWN;
12693   }
12694 }
12695
12696 /*
12697   MovePlayerOneStep()
12698   -----------------------------------------------------------------------------
12699   dx, dy:               direction (non-diagonal) to try to move the player to
12700   real_dx, real_dy:     direction as read from input device (can be diagonal)
12701 */
12702
12703 boolean MovePlayerOneStep(struct PlayerInfo *player,
12704                           int dx, int dy, int real_dx, int real_dy)
12705 {
12706   int jx = player->jx, jy = player->jy;
12707   int new_jx = jx + dx, new_jy = jy + dy;
12708   int can_move;
12709   boolean player_can_move = !player->cannot_move;
12710
12711   if (!player->active || (!dx && !dy))
12712     return MP_NO_ACTION;
12713
12714   player->MovDir = (dx < 0 ? MV_LEFT :
12715                     dx > 0 ? MV_RIGHT :
12716                     dy < 0 ? MV_UP :
12717                     dy > 0 ? MV_DOWN :  MV_NONE);
12718
12719   if (!IN_LEV_FIELD(new_jx, new_jy))
12720     return MP_NO_ACTION;
12721
12722   if (!player_can_move)
12723   {
12724     if (player->MovPos == 0)
12725     {
12726       player->is_moving = FALSE;
12727       player->is_digging = FALSE;
12728       player->is_collecting = FALSE;
12729       player->is_snapping = FALSE;
12730       player->is_pushing = FALSE;
12731     }
12732   }
12733
12734   if (!network.enabled && game.centered_player_nr == -1 &&
12735       !AllPlayersInSight(player, new_jx, new_jy))
12736     return MP_NO_ACTION;
12737
12738   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12739   if (can_move != MP_MOVING)
12740     return can_move;
12741
12742   // check if DigField() has caused relocation of the player
12743   if (player->jx != jx || player->jy != jy)
12744     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12745
12746   StorePlayer[jx][jy] = 0;
12747   player->last_jx = jx;
12748   player->last_jy = jy;
12749   player->jx = new_jx;
12750   player->jy = new_jy;
12751   StorePlayer[new_jx][new_jy] = player->element_nr;
12752
12753   if (player->move_delay_value_next != -1)
12754   {
12755     player->move_delay_value = player->move_delay_value_next;
12756     player->move_delay_value_next = -1;
12757   }
12758
12759   player->MovPos =
12760     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12761
12762   player->step_counter++;
12763
12764   PlayerVisit[jx][jy] = FrameCounter;
12765
12766   player->is_moving = TRUE;
12767
12768 #if 1
12769   // should better be called in MovePlayer(), but this breaks some tapes
12770   ScrollPlayer(player, SCROLL_INIT);
12771 #endif
12772
12773   return MP_MOVING;
12774 }
12775
12776 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12777 {
12778   int jx = player->jx, jy = player->jy;
12779   int old_jx = jx, old_jy = jy;
12780   int moved = MP_NO_ACTION;
12781
12782   if (!player->active)
12783     return FALSE;
12784
12785   if (!dx && !dy)
12786   {
12787     if (player->MovPos == 0)
12788     {
12789       player->is_moving = FALSE;
12790       player->is_digging = FALSE;
12791       player->is_collecting = FALSE;
12792       player->is_snapping = FALSE;
12793       player->is_pushing = FALSE;
12794     }
12795
12796     return FALSE;
12797   }
12798
12799   if (player->move_delay > 0)
12800     return FALSE;
12801
12802   player->move_delay = -1;              // set to "uninitialized" value
12803
12804   // store if player is automatically moved to next field
12805   player->is_auto_moving = (player->programmed_action != MV_NONE);
12806
12807   // remove the last programmed player action
12808   player->programmed_action = 0;
12809
12810   if (player->MovPos)
12811   {
12812     // should only happen if pre-1.2 tape recordings are played
12813     // this is only for backward compatibility
12814
12815     int original_move_delay_value = player->move_delay_value;
12816
12817 #if DEBUG
12818     Debug("game:playing:MovePlayer",
12819           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12820           tape.counter);
12821 #endif
12822
12823     // scroll remaining steps with finest movement resolution
12824     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12825
12826     while (player->MovPos)
12827     {
12828       ScrollPlayer(player, SCROLL_GO_ON);
12829       ScrollScreen(NULL, SCROLL_GO_ON);
12830
12831       AdvanceFrameAndPlayerCounters(player->index_nr);
12832
12833       DrawAllPlayers();
12834       BackToFront_WithFrameDelay(0);
12835     }
12836
12837     player->move_delay_value = original_move_delay_value;
12838   }
12839
12840   player->is_active = FALSE;
12841
12842   if (player->last_move_dir & MV_HORIZONTAL)
12843   {
12844     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12845       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12846   }
12847   else
12848   {
12849     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12850       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12851   }
12852
12853   if (!moved && !player->is_active)
12854   {
12855     player->is_moving = FALSE;
12856     player->is_digging = FALSE;
12857     player->is_collecting = FALSE;
12858     player->is_snapping = FALSE;
12859     player->is_pushing = FALSE;
12860   }
12861
12862   jx = player->jx;
12863   jy = player->jy;
12864
12865   if (moved & MP_MOVING && !ScreenMovPos &&
12866       (player->index_nr == game.centered_player_nr ||
12867        game.centered_player_nr == -1))
12868   {
12869     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12870
12871     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12872     {
12873       // actual player has left the screen -- scroll in that direction
12874       if (jx != old_jx)         // player has moved horizontally
12875         scroll_x += (jx - old_jx);
12876       else                      // player has moved vertically
12877         scroll_y += (jy - old_jy);
12878     }
12879     else
12880     {
12881       int offset_raw = game.scroll_delay_value;
12882
12883       if (jx != old_jx)         // player has moved horizontally
12884       {
12885         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12886         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12887         int new_scroll_x = jx - MIDPOSX + offset_x;
12888
12889         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12890             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12891           scroll_x = new_scroll_x;
12892
12893         // don't scroll over playfield boundaries
12894         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12895
12896         // don't scroll more than one field at a time
12897         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12898
12899         // don't scroll against the player's moving direction
12900         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12901             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12902           scroll_x = old_scroll_x;
12903       }
12904       else                      // player has moved vertically
12905       {
12906         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12907         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12908         int new_scroll_y = jy - MIDPOSY + offset_y;
12909
12910         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12911             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12912           scroll_y = new_scroll_y;
12913
12914         // don't scroll over playfield boundaries
12915         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12916
12917         // don't scroll more than one field at a time
12918         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12919
12920         // don't scroll against the player's moving direction
12921         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12922             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12923           scroll_y = old_scroll_y;
12924       }
12925     }
12926
12927     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12928     {
12929       if (!network.enabled && game.centered_player_nr == -1 &&
12930           !AllPlayersInVisibleScreen())
12931       {
12932         scroll_x = old_scroll_x;
12933         scroll_y = old_scroll_y;
12934       }
12935       else
12936       {
12937         ScrollScreen(player, SCROLL_INIT);
12938         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12939       }
12940     }
12941   }
12942
12943   player->StepFrame = 0;
12944
12945   if (moved & MP_MOVING)
12946   {
12947     if (old_jx != jx && old_jy == jy)
12948       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12949     else if (old_jx == jx && old_jy != jy)
12950       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12951
12952     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12953
12954     player->last_move_dir = player->MovDir;
12955     player->is_moving = TRUE;
12956     player->is_snapping = FALSE;
12957     player->is_switching = FALSE;
12958     player->is_dropping = FALSE;
12959     player->is_dropping_pressed = FALSE;
12960     player->drop_pressed_delay = 0;
12961
12962 #if 0
12963     // should better be called here than above, but this breaks some tapes
12964     ScrollPlayer(player, SCROLL_INIT);
12965 #endif
12966   }
12967   else
12968   {
12969     CheckGravityMovementWhenNotMoving(player);
12970
12971     player->is_moving = FALSE;
12972
12973     /* at this point, the player is allowed to move, but cannot move right now
12974        (e.g. because of something blocking the way) -- ensure that the player
12975        is also allowed to move in the next frame (in old versions before 3.1.1,
12976        the player was forced to wait again for eight frames before next try) */
12977
12978     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12979       player->move_delay = 0;   // allow direct movement in the next frame
12980   }
12981
12982   if (player->move_delay == -1)         // not yet initialized by DigField()
12983     player->move_delay = player->move_delay_value;
12984
12985   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12986   {
12987     TestIfPlayerTouchesBadThing(jx, jy);
12988     TestIfPlayerTouchesCustomElement(jx, jy);
12989   }
12990
12991   if (!player->active)
12992     RemovePlayer(player);
12993
12994   return moved;
12995 }
12996
12997 void ScrollPlayer(struct PlayerInfo *player, int mode)
12998 {
12999   int jx = player->jx, jy = player->jy;
13000   int last_jx = player->last_jx, last_jy = player->last_jy;
13001   int move_stepsize = TILEX / player->move_delay_value;
13002
13003   if (!player->active)
13004     return;
13005
13006   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13007     return;
13008
13009   if (mode == SCROLL_INIT)
13010   {
13011     player->actual_frame_counter = FrameCounter;
13012     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13013
13014     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13015         Tile[last_jx][last_jy] == EL_EMPTY)
13016     {
13017       int last_field_block_delay = 0;   // start with no blocking at all
13018       int block_delay_adjustment = player->block_delay_adjustment;
13019
13020       // if player blocks last field, add delay for exactly one move
13021       if (player->block_last_field)
13022       {
13023         last_field_block_delay += player->move_delay_value;
13024
13025         // when blocking enabled, prevent moving up despite gravity
13026         if (player->gravity && player->MovDir == MV_UP)
13027           block_delay_adjustment = -1;
13028       }
13029
13030       // add block delay adjustment (also possible when not blocking)
13031       last_field_block_delay += block_delay_adjustment;
13032
13033       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13034       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13035     }
13036
13037     if (player->MovPos != 0)    // player has not yet reached destination
13038       return;
13039   }
13040   else if (!FrameReached(&player->actual_frame_counter, 1))
13041     return;
13042
13043   if (player->MovPos != 0)
13044   {
13045     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13046     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13047
13048     // before DrawPlayer() to draw correct player graphic for this case
13049     if (player->MovPos == 0)
13050       CheckGravityMovement(player);
13051   }
13052
13053   if (player->MovPos == 0)      // player reached destination field
13054   {
13055     if (player->move_delay_reset_counter > 0)
13056     {
13057       player->move_delay_reset_counter--;
13058
13059       if (player->move_delay_reset_counter == 0)
13060       {
13061         // continue with normal speed after quickly moving through gate
13062         HALVE_PLAYER_SPEED(player);
13063
13064         // be able to make the next move without delay
13065         player->move_delay = 0;
13066       }
13067     }
13068
13069     player->last_jx = jx;
13070     player->last_jy = jy;
13071
13072     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13073         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13074         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13075         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13076         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13077         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13078         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13079         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13080     {
13081       ExitPlayer(player);
13082
13083       if (game.players_still_needed == 0 &&
13084           (game.friends_still_needed == 0 ||
13085            IS_SP_ELEMENT(Tile[jx][jy])))
13086         LevelSolved();
13087     }
13088
13089     // this breaks one level: "machine", level 000
13090     {
13091       int move_direction = player->MovDir;
13092       int enter_side = MV_DIR_OPPOSITE(move_direction);
13093       int leave_side = move_direction;
13094       int old_jx = last_jx;
13095       int old_jy = last_jy;
13096       int old_element = Tile[old_jx][old_jy];
13097       int new_element = Tile[jx][jy];
13098
13099       if (IS_CUSTOM_ELEMENT(old_element))
13100         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13101                                    CE_LEFT_BY_PLAYER,
13102                                    player->index_bit, leave_side);
13103
13104       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13105                                           CE_PLAYER_LEAVES_X,
13106                                           player->index_bit, leave_side);
13107
13108       if (IS_CUSTOM_ELEMENT(new_element))
13109         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13110                                    player->index_bit, enter_side);
13111
13112       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13113                                           CE_PLAYER_ENTERS_X,
13114                                           player->index_bit, enter_side);
13115
13116       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13117                                         CE_MOVE_OF_X, move_direction);
13118     }
13119
13120     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13121     {
13122       TestIfPlayerTouchesBadThing(jx, jy);
13123       TestIfPlayerTouchesCustomElement(jx, jy);
13124
13125       /* needed because pushed element has not yet reached its destination,
13126          so it would trigger a change event at its previous field location */
13127       if (!player->is_pushing)
13128         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13129
13130       if (level.finish_dig_collect &&
13131           (player->is_digging || player->is_collecting))
13132       {
13133         int last_element = player->last_removed_element;
13134         int move_direction = player->MovDir;
13135         int enter_side = MV_DIR_OPPOSITE(move_direction);
13136         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13137                             CE_PLAYER_COLLECTS_X);
13138
13139         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13140                                             player->index_bit, enter_side);
13141
13142         player->last_removed_element = EL_UNDEFINED;
13143       }
13144
13145       if (!player->active)
13146         RemovePlayer(player);
13147     }
13148
13149     if (level.use_step_counter)
13150     {
13151       int i;
13152
13153       TimePlayed++;
13154
13155       if (TimeLeft > 0)
13156       {
13157         TimeLeft--;
13158
13159         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13160           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13161
13162         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13163
13164         DisplayGameControlValues();
13165
13166         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13167           for (i = 0; i < MAX_PLAYERS; i++)
13168             KillPlayer(&stored_player[i]);
13169       }
13170       else if (game.no_time_limit && !game.all_players_gone)
13171       {
13172         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13173
13174         DisplayGameControlValues();
13175       }
13176     }
13177
13178     if (tape.single_step && tape.recording && !tape.pausing &&
13179         !player->programmed_action)
13180       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13181
13182     if (!player->programmed_action)
13183       CheckSaveEngineSnapshot(player);
13184   }
13185 }
13186
13187 void ScrollScreen(struct PlayerInfo *player, int mode)
13188 {
13189   static unsigned int screen_frame_counter = 0;
13190
13191   if (mode == SCROLL_INIT)
13192   {
13193     // set scrolling step size according to actual player's moving speed
13194     ScrollStepSize = TILEX / player->move_delay_value;
13195
13196     screen_frame_counter = FrameCounter;
13197     ScreenMovDir = player->MovDir;
13198     ScreenMovPos = player->MovPos;
13199     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13200     return;
13201   }
13202   else if (!FrameReached(&screen_frame_counter, 1))
13203     return;
13204
13205   if (ScreenMovPos)
13206   {
13207     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13208     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13209     redraw_mask |= REDRAW_FIELD;
13210   }
13211   else
13212     ScreenMovDir = MV_NONE;
13213 }
13214
13215 void TestIfPlayerTouchesCustomElement(int x, int y)
13216 {
13217   static int xy[4][2] =
13218   {
13219     { 0, -1 },
13220     { -1, 0 },
13221     { +1, 0 },
13222     { 0, +1 }
13223   };
13224   static int trigger_sides[4][2] =
13225   {
13226     // center side       border side
13227     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13228     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13229     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13230     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13231   };
13232   static int touch_dir[4] =
13233   {
13234     MV_LEFT | MV_RIGHT,
13235     MV_UP   | MV_DOWN,
13236     MV_UP   | MV_DOWN,
13237     MV_LEFT | MV_RIGHT
13238   };
13239   int center_element = Tile[x][y];      // should always be non-moving!
13240   int i;
13241
13242   for (i = 0; i < NUM_DIRECTIONS; i++)
13243   {
13244     int xx = x + xy[i][0];
13245     int yy = y + xy[i][1];
13246     int center_side = trigger_sides[i][0];
13247     int border_side = trigger_sides[i][1];
13248     int border_element;
13249
13250     if (!IN_LEV_FIELD(xx, yy))
13251       continue;
13252
13253     if (IS_PLAYER(x, y))                // player found at center element
13254     {
13255       struct PlayerInfo *player = PLAYERINFO(x, y);
13256
13257       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13258         border_element = Tile[xx][yy];          // may be moving!
13259       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13260         border_element = Tile[xx][yy];
13261       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13262         border_element = MovingOrBlocked2Element(xx, yy);
13263       else
13264         continue;               // center and border element do not touch
13265
13266       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13267                                  player->index_bit, border_side);
13268       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13269                                           CE_PLAYER_TOUCHES_X,
13270                                           player->index_bit, border_side);
13271
13272       {
13273         /* use player element that is initially defined in the level playfield,
13274            not the player element that corresponds to the runtime player number
13275            (example: a level that contains EL_PLAYER_3 as the only player would
13276            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13277         int player_element = PLAYERINFO(x, y)->initial_element;
13278
13279         CheckElementChangeBySide(xx, yy, border_element, player_element,
13280                                  CE_TOUCHING_X, border_side);
13281       }
13282     }
13283     else if (IS_PLAYER(xx, yy))         // player found at border element
13284     {
13285       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13286
13287       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13288       {
13289         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13290           continue;             // center and border element do not touch
13291       }
13292
13293       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13294                                  player->index_bit, center_side);
13295       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13296                                           CE_PLAYER_TOUCHES_X,
13297                                           player->index_bit, center_side);
13298
13299       {
13300         /* use player element that is initially defined in the level playfield,
13301            not the player element that corresponds to the runtime player number
13302            (example: a level that contains EL_PLAYER_3 as the only player would
13303            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13304         int player_element = PLAYERINFO(xx, yy)->initial_element;
13305
13306         CheckElementChangeBySide(x, y, center_element, player_element,
13307                                  CE_TOUCHING_X, center_side);
13308       }
13309
13310       break;
13311     }
13312   }
13313 }
13314
13315 void TestIfElementTouchesCustomElement(int x, int y)
13316 {
13317   static int xy[4][2] =
13318   {
13319     { 0, -1 },
13320     { -1, 0 },
13321     { +1, 0 },
13322     { 0, +1 }
13323   };
13324   static int trigger_sides[4][2] =
13325   {
13326     // center side      border side
13327     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13328     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13329     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13330     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13331   };
13332   static int touch_dir[4] =
13333   {
13334     MV_LEFT | MV_RIGHT,
13335     MV_UP   | MV_DOWN,
13336     MV_UP   | MV_DOWN,
13337     MV_LEFT | MV_RIGHT
13338   };
13339   boolean change_center_element = FALSE;
13340   int center_element = Tile[x][y];      // should always be non-moving!
13341   int border_element_old[NUM_DIRECTIONS];
13342   int i;
13343
13344   for (i = 0; i < NUM_DIRECTIONS; i++)
13345   {
13346     int xx = x + xy[i][0];
13347     int yy = y + xy[i][1];
13348     int border_element;
13349
13350     border_element_old[i] = -1;
13351
13352     if (!IN_LEV_FIELD(xx, yy))
13353       continue;
13354
13355     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13356       border_element = Tile[xx][yy];    // may be moving!
13357     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13358       border_element = Tile[xx][yy];
13359     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13360       border_element = MovingOrBlocked2Element(xx, yy);
13361     else
13362       continue;                 // center and border element do not touch
13363
13364     border_element_old[i] = border_element;
13365   }
13366
13367   for (i = 0; i < NUM_DIRECTIONS; i++)
13368   {
13369     int xx = x + xy[i][0];
13370     int yy = y + xy[i][1];
13371     int center_side = trigger_sides[i][0];
13372     int border_element = border_element_old[i];
13373
13374     if (border_element == -1)
13375       continue;
13376
13377     // check for change of border element
13378     CheckElementChangeBySide(xx, yy, border_element, center_element,
13379                              CE_TOUCHING_X, center_side);
13380
13381     // (center element cannot be player, so we dont have to check this here)
13382   }
13383
13384   for (i = 0; i < NUM_DIRECTIONS; i++)
13385   {
13386     int xx = x + xy[i][0];
13387     int yy = y + xy[i][1];
13388     int border_side = trigger_sides[i][1];
13389     int border_element = border_element_old[i];
13390
13391     if (border_element == -1)
13392       continue;
13393
13394     // check for change of center element (but change it only once)
13395     if (!change_center_element)
13396       change_center_element =
13397         CheckElementChangeBySide(x, y, center_element, border_element,
13398                                  CE_TOUCHING_X, border_side);
13399
13400     if (IS_PLAYER(xx, yy))
13401     {
13402       /* use player element that is initially defined in the level playfield,
13403          not the player element that corresponds to the runtime player number
13404          (example: a level that contains EL_PLAYER_3 as the only player would
13405          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13406       int player_element = PLAYERINFO(xx, yy)->initial_element;
13407
13408       CheckElementChangeBySide(x, y, center_element, player_element,
13409                                CE_TOUCHING_X, border_side);
13410     }
13411   }
13412 }
13413
13414 void TestIfElementHitsCustomElement(int x, int y, int direction)
13415 {
13416   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13417   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13418   int hitx = x + dx, hity = y + dy;
13419   int hitting_element = Tile[x][y];
13420   int touched_element;
13421
13422   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13423     return;
13424
13425   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13426                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13427
13428   if (IN_LEV_FIELD(hitx, hity))
13429   {
13430     int opposite_direction = MV_DIR_OPPOSITE(direction);
13431     int hitting_side = direction;
13432     int touched_side = opposite_direction;
13433     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13434                           MovDir[hitx][hity] != direction ||
13435                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13436
13437     object_hit = TRUE;
13438
13439     if (object_hit)
13440     {
13441       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13442                                CE_HITTING_X, touched_side);
13443
13444       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13445                                CE_HIT_BY_X, hitting_side);
13446
13447       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13448                                CE_HIT_BY_SOMETHING, opposite_direction);
13449
13450       if (IS_PLAYER(hitx, hity))
13451       {
13452         /* use player element that is initially defined in the level playfield,
13453            not the player element that corresponds to the runtime player number
13454            (example: a level that contains EL_PLAYER_3 as the only player would
13455            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13456         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13457
13458         CheckElementChangeBySide(x, y, hitting_element, player_element,
13459                                  CE_HITTING_X, touched_side);
13460       }
13461     }
13462   }
13463
13464   // "hitting something" is also true when hitting the playfield border
13465   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13466                            CE_HITTING_SOMETHING, direction);
13467 }
13468
13469 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13470 {
13471   int i, kill_x = -1, kill_y = -1;
13472
13473   int bad_element = -1;
13474   static int test_xy[4][2] =
13475   {
13476     { 0, -1 },
13477     { -1, 0 },
13478     { +1, 0 },
13479     { 0, +1 }
13480   };
13481   static int test_dir[4] =
13482   {
13483     MV_UP,
13484     MV_LEFT,
13485     MV_RIGHT,
13486     MV_DOWN
13487   };
13488
13489   for (i = 0; i < NUM_DIRECTIONS; i++)
13490   {
13491     int test_x, test_y, test_move_dir, test_element;
13492
13493     test_x = good_x + test_xy[i][0];
13494     test_y = good_y + test_xy[i][1];
13495
13496     if (!IN_LEV_FIELD(test_x, test_y))
13497       continue;
13498
13499     test_move_dir =
13500       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13501
13502     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13503
13504     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13505        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13506     */
13507     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13508         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13509     {
13510       kill_x = test_x;
13511       kill_y = test_y;
13512       bad_element = test_element;
13513
13514       break;
13515     }
13516   }
13517
13518   if (kill_x != -1 || kill_y != -1)
13519   {
13520     if (IS_PLAYER(good_x, good_y))
13521     {
13522       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13523
13524       if (player->shield_deadly_time_left > 0 &&
13525           !IS_INDESTRUCTIBLE(bad_element))
13526         Bang(kill_x, kill_y);
13527       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13528         KillPlayer(player);
13529     }
13530     else
13531       Bang(good_x, good_y);
13532   }
13533 }
13534
13535 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13536 {
13537   int i, kill_x = -1, kill_y = -1;
13538   int bad_element = Tile[bad_x][bad_y];
13539   static int test_xy[4][2] =
13540   {
13541     { 0, -1 },
13542     { -1, 0 },
13543     { +1, 0 },
13544     { 0, +1 }
13545   };
13546   static int touch_dir[4] =
13547   {
13548     MV_LEFT | MV_RIGHT,
13549     MV_UP   | MV_DOWN,
13550     MV_UP   | MV_DOWN,
13551     MV_LEFT | MV_RIGHT
13552   };
13553   static int test_dir[4] =
13554   {
13555     MV_UP,
13556     MV_LEFT,
13557     MV_RIGHT,
13558     MV_DOWN
13559   };
13560
13561   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13562     return;
13563
13564   for (i = 0; i < NUM_DIRECTIONS; i++)
13565   {
13566     int test_x, test_y, test_move_dir, test_element;
13567
13568     test_x = bad_x + test_xy[i][0];
13569     test_y = bad_y + test_xy[i][1];
13570
13571     if (!IN_LEV_FIELD(test_x, test_y))
13572       continue;
13573
13574     test_move_dir =
13575       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13576
13577     test_element = Tile[test_x][test_y];
13578
13579     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13580        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13581     */
13582     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13583         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13584     {
13585       // good thing is player or penguin that does not move away
13586       if (IS_PLAYER(test_x, test_y))
13587       {
13588         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13589
13590         if (bad_element == EL_ROBOT && player->is_moving)
13591           continue;     // robot does not kill player if he is moving
13592
13593         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13594         {
13595           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13596             continue;           // center and border element do not touch
13597         }
13598
13599         kill_x = test_x;
13600         kill_y = test_y;
13601
13602         break;
13603       }
13604       else if (test_element == EL_PENGUIN)
13605       {
13606         kill_x = test_x;
13607         kill_y = test_y;
13608
13609         break;
13610       }
13611     }
13612   }
13613
13614   if (kill_x != -1 || kill_y != -1)
13615   {
13616     if (IS_PLAYER(kill_x, kill_y))
13617     {
13618       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13619
13620       if (player->shield_deadly_time_left > 0 &&
13621           !IS_INDESTRUCTIBLE(bad_element))
13622         Bang(bad_x, bad_y);
13623       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13624         KillPlayer(player);
13625     }
13626     else
13627       Bang(kill_x, kill_y);
13628   }
13629 }
13630
13631 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13632 {
13633   int bad_element = Tile[bad_x][bad_y];
13634   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13635   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13636   int test_x = bad_x + dx, test_y = bad_y + dy;
13637   int test_move_dir, test_element;
13638   int kill_x = -1, kill_y = -1;
13639
13640   if (!IN_LEV_FIELD(test_x, test_y))
13641     return;
13642
13643   test_move_dir =
13644     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13645
13646   test_element = Tile[test_x][test_y];
13647
13648   if (test_move_dir != bad_move_dir)
13649   {
13650     // good thing can be player or penguin that does not move away
13651     if (IS_PLAYER(test_x, test_y))
13652     {
13653       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13654
13655       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13656          player as being hit when he is moving towards the bad thing, because
13657          the "get hit by" condition would be lost after the player stops) */
13658       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13659         return;         // player moves away from bad thing
13660
13661       kill_x = test_x;
13662       kill_y = test_y;
13663     }
13664     else if (test_element == EL_PENGUIN)
13665     {
13666       kill_x = test_x;
13667       kill_y = test_y;
13668     }
13669   }
13670
13671   if (kill_x != -1 || kill_y != -1)
13672   {
13673     if (IS_PLAYER(kill_x, kill_y))
13674     {
13675       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13676
13677       if (player->shield_deadly_time_left > 0 &&
13678           !IS_INDESTRUCTIBLE(bad_element))
13679         Bang(bad_x, bad_y);
13680       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13681         KillPlayer(player);
13682     }
13683     else
13684       Bang(kill_x, kill_y);
13685   }
13686 }
13687
13688 void TestIfPlayerTouchesBadThing(int x, int y)
13689 {
13690   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13691 }
13692
13693 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13694 {
13695   TestIfGoodThingHitsBadThing(x, y, move_dir);
13696 }
13697
13698 void TestIfBadThingTouchesPlayer(int x, int y)
13699 {
13700   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13701 }
13702
13703 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13704 {
13705   TestIfBadThingHitsGoodThing(x, y, move_dir);
13706 }
13707
13708 void TestIfFriendTouchesBadThing(int x, int y)
13709 {
13710   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13711 }
13712
13713 void TestIfBadThingTouchesFriend(int x, int y)
13714 {
13715   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13716 }
13717
13718 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13719 {
13720   int i, kill_x = bad_x, kill_y = bad_y;
13721   static int xy[4][2] =
13722   {
13723     { 0, -1 },
13724     { -1, 0 },
13725     { +1, 0 },
13726     { 0, +1 }
13727   };
13728
13729   for (i = 0; i < NUM_DIRECTIONS; i++)
13730   {
13731     int x, y, element;
13732
13733     x = bad_x + xy[i][0];
13734     y = bad_y + xy[i][1];
13735     if (!IN_LEV_FIELD(x, y))
13736       continue;
13737
13738     element = Tile[x][y];
13739     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13740         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13741     {
13742       kill_x = x;
13743       kill_y = y;
13744       break;
13745     }
13746   }
13747
13748   if (kill_x != bad_x || kill_y != bad_y)
13749     Bang(bad_x, bad_y);
13750 }
13751
13752 void KillPlayer(struct PlayerInfo *player)
13753 {
13754   int jx = player->jx, jy = player->jy;
13755
13756   if (!player->active)
13757     return;
13758
13759 #if 0
13760   Debug("game:playing:KillPlayer",
13761         "0: killed == %d, active == %d, reanimated == %d",
13762         player->killed, player->active, player->reanimated);
13763 #endif
13764
13765   /* the following code was introduced to prevent an infinite loop when calling
13766      -> Bang()
13767      -> CheckTriggeredElementChangeExt()
13768      -> ExecuteCustomElementAction()
13769      -> KillPlayer()
13770      -> (infinitely repeating the above sequence of function calls)
13771      which occurs when killing the player while having a CE with the setting
13772      "kill player X when explosion of <player X>"; the solution using a new
13773      field "player->killed" was chosen for backwards compatibility, although
13774      clever use of the fields "player->active" etc. would probably also work */
13775 #if 1
13776   if (player->killed)
13777     return;
13778 #endif
13779
13780   player->killed = TRUE;
13781
13782   // remove accessible field at the player's position
13783   Tile[jx][jy] = EL_EMPTY;
13784
13785   // deactivate shield (else Bang()/Explode() would not work right)
13786   player->shield_normal_time_left = 0;
13787   player->shield_deadly_time_left = 0;
13788
13789 #if 0
13790   Debug("game:playing:KillPlayer",
13791         "1: killed == %d, active == %d, reanimated == %d",
13792         player->killed, player->active, player->reanimated);
13793 #endif
13794
13795   Bang(jx, jy);
13796
13797 #if 0
13798   Debug("game:playing:KillPlayer",
13799         "2: killed == %d, active == %d, reanimated == %d",
13800         player->killed, player->active, player->reanimated);
13801 #endif
13802
13803   if (player->reanimated)       // killed player may have been reanimated
13804     player->killed = player->reanimated = FALSE;
13805   else
13806     BuryPlayer(player);
13807 }
13808
13809 static void KillPlayerUnlessEnemyProtected(int x, int y)
13810 {
13811   if (!PLAYER_ENEMY_PROTECTED(x, y))
13812     KillPlayer(PLAYERINFO(x, y));
13813 }
13814
13815 static void KillPlayerUnlessExplosionProtected(int x, int y)
13816 {
13817   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13818     KillPlayer(PLAYERINFO(x, y));
13819 }
13820
13821 void BuryPlayer(struct PlayerInfo *player)
13822 {
13823   int jx = player->jx, jy = player->jy;
13824
13825   if (!player->active)
13826     return;
13827
13828   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13829   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13830
13831   RemovePlayer(player);
13832
13833   player->buried = TRUE;
13834
13835   if (game.all_players_gone)
13836     game.GameOver = TRUE;
13837 }
13838
13839 void RemovePlayer(struct PlayerInfo *player)
13840 {
13841   int jx = player->jx, jy = player->jy;
13842   int i, found = FALSE;
13843
13844   player->present = FALSE;
13845   player->active = FALSE;
13846
13847   // required for some CE actions (even if the player is not active anymore)
13848   player->MovPos = 0;
13849
13850   if (!ExplodeField[jx][jy])
13851     StorePlayer[jx][jy] = 0;
13852
13853   if (player->is_moving)
13854     TEST_DrawLevelField(player->last_jx, player->last_jy);
13855
13856   for (i = 0; i < MAX_PLAYERS; i++)
13857     if (stored_player[i].active)
13858       found = TRUE;
13859
13860   if (!found)
13861   {
13862     game.all_players_gone = TRUE;
13863     game.GameOver = TRUE;
13864   }
13865
13866   game.exit_x = game.robot_wheel_x = jx;
13867   game.exit_y = game.robot_wheel_y = jy;
13868 }
13869
13870 void ExitPlayer(struct PlayerInfo *player)
13871 {
13872   DrawPlayer(player);   // needed here only to cleanup last field
13873   RemovePlayer(player);
13874
13875   if (game.players_still_needed > 0)
13876     game.players_still_needed--;
13877 }
13878
13879 static void SetFieldForSnapping(int x, int y, int element, int direction,
13880                                 int player_index_bit)
13881 {
13882   struct ElementInfo *ei = &element_info[element];
13883   int direction_bit = MV_DIR_TO_BIT(direction);
13884   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13885   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13886                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13887
13888   Tile[x][y] = EL_ELEMENT_SNAPPING;
13889   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13890   MovDir[x][y] = direction;
13891   Store[x][y] = element;
13892   Store2[x][y] = player_index_bit;
13893
13894   ResetGfxAnimation(x, y);
13895
13896   GfxElement[x][y] = element;
13897   GfxAction[x][y] = action;
13898   GfxDir[x][y] = direction;
13899   GfxFrame[x][y] = -1;
13900 }
13901
13902 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13903                                    int player_index_bit)
13904 {
13905   TestIfElementTouchesCustomElement(x, y);      // for empty space
13906
13907   if (level.finish_dig_collect)
13908   {
13909     int dig_side = MV_DIR_OPPOSITE(direction);
13910
13911     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13912                                         player_index_bit, dig_side);
13913   }
13914 }
13915
13916 /*
13917   =============================================================================
13918   checkDiagonalPushing()
13919   -----------------------------------------------------------------------------
13920   check if diagonal input device direction results in pushing of object
13921   (by checking if the alternative direction is walkable, diggable, ...)
13922   =============================================================================
13923 */
13924
13925 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13926                                     int x, int y, int real_dx, int real_dy)
13927 {
13928   int jx, jy, dx, dy, xx, yy;
13929
13930   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13931     return TRUE;
13932
13933   // diagonal direction: check alternative direction
13934   jx = player->jx;
13935   jy = player->jy;
13936   dx = x - jx;
13937   dy = y - jy;
13938   xx = jx + (dx == 0 ? real_dx : 0);
13939   yy = jy + (dy == 0 ? real_dy : 0);
13940
13941   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13942 }
13943
13944 /*
13945   =============================================================================
13946   DigField()
13947   -----------------------------------------------------------------------------
13948   x, y:                 field next to player (non-diagonal) to try to dig to
13949   real_dx, real_dy:     direction as read from input device (can be diagonal)
13950   =============================================================================
13951 */
13952
13953 static int DigField(struct PlayerInfo *player,
13954                     int oldx, int oldy, int x, int y,
13955                     int real_dx, int real_dy, int mode)
13956 {
13957   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13958   boolean player_was_pushing = player->is_pushing;
13959   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13960   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13961   int jx = oldx, jy = oldy;
13962   int dx = x - jx, dy = y - jy;
13963   int nextx = x + dx, nexty = y + dy;
13964   int move_direction = (dx == -1 ? MV_LEFT  :
13965                         dx == +1 ? MV_RIGHT :
13966                         dy == -1 ? MV_UP    :
13967                         dy == +1 ? MV_DOWN  : MV_NONE);
13968   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13969   int dig_side = MV_DIR_OPPOSITE(move_direction);
13970   int old_element = Tile[jx][jy];
13971   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13972   int collect_count;
13973
13974   if (is_player)                // function can also be called by EL_PENGUIN
13975   {
13976     if (player->MovPos == 0)
13977     {
13978       player->is_digging = FALSE;
13979       player->is_collecting = FALSE;
13980     }
13981
13982     if (player->MovPos == 0)    // last pushing move finished
13983       player->is_pushing = FALSE;
13984
13985     if (mode == DF_NO_PUSH)     // player just stopped pushing
13986     {
13987       player->is_switching = FALSE;
13988       player->push_delay = -1;
13989
13990       return MP_NO_ACTION;
13991     }
13992   }
13993
13994   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13995     old_element = Back[jx][jy];
13996
13997   // in case of element dropped at player position, check background
13998   else if (Back[jx][jy] != EL_EMPTY &&
13999            game.engine_version >= VERSION_IDENT(2,2,0,0))
14000     old_element = Back[jx][jy];
14001
14002   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14003     return MP_NO_ACTION;        // field has no opening in this direction
14004
14005   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14006     return MP_NO_ACTION;        // field has no opening in this direction
14007
14008   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14009   {
14010     SplashAcid(x, y);
14011
14012     Tile[jx][jy] = player->artwork_element;
14013     InitMovingField(jx, jy, MV_DOWN);
14014     Store[jx][jy] = EL_ACID;
14015     ContinueMoving(jx, jy);
14016     BuryPlayer(player);
14017
14018     return MP_DONT_RUN_INTO;
14019   }
14020
14021   if (player_can_move && DONT_RUN_INTO(element))
14022   {
14023     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14024
14025     return MP_DONT_RUN_INTO;
14026   }
14027
14028   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14029     return MP_NO_ACTION;
14030
14031   collect_count = element_info[element].collect_count_initial;
14032
14033   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14034     return MP_NO_ACTION;
14035
14036   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14037     player_can_move = player_can_move_or_snap;
14038
14039   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14040       game.engine_version >= VERSION_IDENT(2,2,0,0))
14041   {
14042     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14043                                player->index_bit, dig_side);
14044     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14045                                         player->index_bit, dig_side);
14046
14047     if (element == EL_DC_LANDMINE)
14048       Bang(x, y);
14049
14050     if (Tile[x][y] != element)          // field changed by snapping
14051       return MP_ACTION;
14052
14053     return MP_NO_ACTION;
14054   }
14055
14056   if (player->gravity && is_player && !player->is_auto_moving &&
14057       canFallDown(player) && move_direction != MV_DOWN &&
14058       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14059     return MP_NO_ACTION;        // player cannot walk here due to gravity
14060
14061   if (player_can_move &&
14062       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14063   {
14064     int sound_element = SND_ELEMENT(element);
14065     int sound_action = ACTION_WALKING;
14066
14067     if (IS_RND_GATE(element))
14068     {
14069       if (!player->key[RND_GATE_NR(element)])
14070         return MP_NO_ACTION;
14071     }
14072     else if (IS_RND_GATE_GRAY(element))
14073     {
14074       if (!player->key[RND_GATE_GRAY_NR(element)])
14075         return MP_NO_ACTION;
14076     }
14077     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14078     {
14079       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14080         return MP_NO_ACTION;
14081     }
14082     else if (element == EL_EXIT_OPEN ||
14083              element == EL_EM_EXIT_OPEN ||
14084              element == EL_EM_EXIT_OPENING ||
14085              element == EL_STEEL_EXIT_OPEN ||
14086              element == EL_EM_STEEL_EXIT_OPEN ||
14087              element == EL_EM_STEEL_EXIT_OPENING ||
14088              element == EL_SP_EXIT_OPEN ||
14089              element == EL_SP_EXIT_OPENING)
14090     {
14091       sound_action = ACTION_PASSING;    // player is passing exit
14092     }
14093     else if (element == EL_EMPTY)
14094     {
14095       sound_action = ACTION_MOVING;             // nothing to walk on
14096     }
14097
14098     // play sound from background or player, whatever is available
14099     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14100       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14101     else
14102       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14103   }
14104   else if (player_can_move &&
14105            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14106   {
14107     if (!ACCESS_FROM(element, opposite_direction))
14108       return MP_NO_ACTION;      // field not accessible from this direction
14109
14110     if (CAN_MOVE(element))      // only fixed elements can be passed!
14111       return MP_NO_ACTION;
14112
14113     if (IS_EM_GATE(element))
14114     {
14115       if (!player->key[EM_GATE_NR(element)])
14116         return MP_NO_ACTION;
14117     }
14118     else if (IS_EM_GATE_GRAY(element))
14119     {
14120       if (!player->key[EM_GATE_GRAY_NR(element)])
14121         return MP_NO_ACTION;
14122     }
14123     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14124     {
14125       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14126         return MP_NO_ACTION;
14127     }
14128     else if (IS_EMC_GATE(element))
14129     {
14130       if (!player->key[EMC_GATE_NR(element)])
14131         return MP_NO_ACTION;
14132     }
14133     else if (IS_EMC_GATE_GRAY(element))
14134     {
14135       if (!player->key[EMC_GATE_GRAY_NR(element)])
14136         return MP_NO_ACTION;
14137     }
14138     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14139     {
14140       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14141         return MP_NO_ACTION;
14142     }
14143     else if (element == EL_DC_GATE_WHITE ||
14144              element == EL_DC_GATE_WHITE_GRAY ||
14145              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14146     {
14147       if (player->num_white_keys == 0)
14148         return MP_NO_ACTION;
14149
14150       player->num_white_keys--;
14151     }
14152     else if (IS_SP_PORT(element))
14153     {
14154       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14155           element == EL_SP_GRAVITY_PORT_RIGHT ||
14156           element == EL_SP_GRAVITY_PORT_UP ||
14157           element == EL_SP_GRAVITY_PORT_DOWN)
14158         player->gravity = !player->gravity;
14159       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14160                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14161                element == EL_SP_GRAVITY_ON_PORT_UP ||
14162                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14163         player->gravity = TRUE;
14164       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14165                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14166                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14167                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14168         player->gravity = FALSE;
14169     }
14170
14171     // automatically move to the next field with double speed
14172     player->programmed_action = move_direction;
14173
14174     if (player->move_delay_reset_counter == 0)
14175     {
14176       player->move_delay_reset_counter = 2;     // two double speed steps
14177
14178       DOUBLE_PLAYER_SPEED(player);
14179     }
14180
14181     PlayLevelSoundAction(x, y, ACTION_PASSING);
14182   }
14183   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14184   {
14185     RemoveField(x, y);
14186
14187     if (mode != DF_SNAP)
14188     {
14189       GfxElement[x][y] = GFX_ELEMENT(element);
14190       player->is_digging = TRUE;
14191     }
14192
14193     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14194
14195     // use old behaviour for old levels (digging)
14196     if (!level.finish_dig_collect)
14197     {
14198       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14199                                           player->index_bit, dig_side);
14200
14201       // if digging triggered player relocation, finish digging tile
14202       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14203         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14204     }
14205
14206     if (mode == DF_SNAP)
14207     {
14208       if (level.block_snap_field)
14209         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14210       else
14211         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14212
14213       // use old behaviour for old levels (snapping)
14214       if (!level.finish_dig_collect)
14215         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14216                                             player->index_bit, dig_side);
14217     }
14218   }
14219   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14220   {
14221     RemoveField(x, y);
14222
14223     if (is_player && mode != DF_SNAP)
14224     {
14225       GfxElement[x][y] = element;
14226       player->is_collecting = TRUE;
14227     }
14228
14229     if (element == EL_SPEED_PILL)
14230     {
14231       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14232     }
14233     else if (element == EL_EXTRA_TIME && level.time > 0)
14234     {
14235       TimeLeft += level.extra_time;
14236
14237       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14238
14239       DisplayGameControlValues();
14240     }
14241     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14242     {
14243       player->shield_normal_time_left += level.shield_normal_time;
14244       if (element == EL_SHIELD_DEADLY)
14245         player->shield_deadly_time_left += level.shield_deadly_time;
14246     }
14247     else if (element == EL_DYNAMITE ||
14248              element == EL_EM_DYNAMITE ||
14249              element == EL_SP_DISK_RED)
14250     {
14251       if (player->inventory_size < MAX_INVENTORY_SIZE)
14252         player->inventory_element[player->inventory_size++] = element;
14253
14254       DrawGameDoorValues();
14255     }
14256     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14257     {
14258       player->dynabomb_count++;
14259       player->dynabombs_left++;
14260     }
14261     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14262     {
14263       player->dynabomb_size++;
14264     }
14265     else if (element == EL_DYNABOMB_INCREASE_POWER)
14266     {
14267       player->dynabomb_xl = TRUE;
14268     }
14269     else if (IS_KEY(element))
14270     {
14271       player->key[KEY_NR(element)] = TRUE;
14272
14273       DrawGameDoorValues();
14274     }
14275     else if (element == EL_DC_KEY_WHITE)
14276     {
14277       player->num_white_keys++;
14278
14279       // display white keys?
14280       // DrawGameDoorValues();
14281     }
14282     else if (IS_ENVELOPE(element))
14283     {
14284       player->show_envelope = element;
14285     }
14286     else if (element == EL_EMC_LENSES)
14287     {
14288       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14289
14290       RedrawAllInvisibleElementsForLenses();
14291     }
14292     else if (element == EL_EMC_MAGNIFIER)
14293     {
14294       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14295
14296       RedrawAllInvisibleElementsForMagnifier();
14297     }
14298     else if (IS_DROPPABLE(element) ||
14299              IS_THROWABLE(element))     // can be collected and dropped
14300     {
14301       int i;
14302
14303       if (collect_count == 0)
14304         player->inventory_infinite_element = element;
14305       else
14306         for (i = 0; i < collect_count; i++)
14307           if (player->inventory_size < MAX_INVENTORY_SIZE)
14308             player->inventory_element[player->inventory_size++] = element;
14309
14310       DrawGameDoorValues();
14311     }
14312     else if (collect_count > 0)
14313     {
14314       game.gems_still_needed -= collect_count;
14315       if (game.gems_still_needed < 0)
14316         game.gems_still_needed = 0;
14317
14318       game.snapshot.collected_item = TRUE;
14319
14320       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14321
14322       DisplayGameControlValues();
14323     }
14324
14325     RaiseScoreElement(element);
14326     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14327
14328     // use old behaviour for old levels (collecting)
14329     if (!level.finish_dig_collect && is_player)
14330     {
14331       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14332                                           player->index_bit, dig_side);
14333
14334       // if collecting triggered player relocation, finish collecting tile
14335       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14336         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14337     }
14338
14339     if (mode == DF_SNAP)
14340     {
14341       if (level.block_snap_field)
14342         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14343       else
14344         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14345
14346       // use old behaviour for old levels (snapping)
14347       if (!level.finish_dig_collect)
14348         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14349                                             player->index_bit, dig_side);
14350     }
14351   }
14352   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14353   {
14354     if (mode == DF_SNAP && element != EL_BD_ROCK)
14355       return MP_NO_ACTION;
14356
14357     if (CAN_FALL(element) && dy)
14358       return MP_NO_ACTION;
14359
14360     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14361         !(element == EL_SPRING && level.use_spring_bug))
14362       return MP_NO_ACTION;
14363
14364     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14365         ((move_direction & MV_VERTICAL &&
14366           ((element_info[element].move_pattern & MV_LEFT &&
14367             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14368            (element_info[element].move_pattern & MV_RIGHT &&
14369             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14370          (move_direction & MV_HORIZONTAL &&
14371           ((element_info[element].move_pattern & MV_UP &&
14372             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14373            (element_info[element].move_pattern & MV_DOWN &&
14374             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14375       return MP_NO_ACTION;
14376
14377     // do not push elements already moving away faster than player
14378     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14379         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14380       return MP_NO_ACTION;
14381
14382     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14383     {
14384       if (player->push_delay_value == -1 || !player_was_pushing)
14385         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14386     }
14387     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14388     {
14389       if (player->push_delay_value == -1)
14390         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14391     }
14392     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14393     {
14394       if (!player->is_pushing)
14395         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14396     }
14397
14398     player->is_pushing = TRUE;
14399     player->is_active = TRUE;
14400
14401     if (!(IN_LEV_FIELD(nextx, nexty) &&
14402           (IS_FREE(nextx, nexty) ||
14403            (IS_SB_ELEMENT(element) &&
14404             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14405            (IS_CUSTOM_ELEMENT(element) &&
14406             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14407       return MP_NO_ACTION;
14408
14409     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14410       return MP_NO_ACTION;
14411
14412     if (player->push_delay == -1)       // new pushing; restart delay
14413       player->push_delay = 0;
14414
14415     if (player->push_delay < player->push_delay_value &&
14416         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14417         element != EL_SPRING && element != EL_BALLOON)
14418     {
14419       // make sure that there is no move delay before next try to push
14420       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14421         player->move_delay = 0;
14422
14423       return MP_NO_ACTION;
14424     }
14425
14426     if (IS_CUSTOM_ELEMENT(element) &&
14427         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14428     {
14429       if (!DigFieldByCE(nextx, nexty, element))
14430         return MP_NO_ACTION;
14431     }
14432
14433     if (IS_SB_ELEMENT(element))
14434     {
14435       boolean sokoban_task_solved = FALSE;
14436
14437       if (element == EL_SOKOBAN_FIELD_FULL)
14438       {
14439         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14440
14441         IncrementSokobanFieldsNeeded();
14442         IncrementSokobanObjectsNeeded();
14443       }
14444
14445       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14446       {
14447         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14448
14449         DecrementSokobanFieldsNeeded();
14450         DecrementSokobanObjectsNeeded();
14451
14452         // sokoban object was pushed from empty field to sokoban field
14453         if (Back[x][y] == EL_EMPTY)
14454           sokoban_task_solved = TRUE;
14455       }
14456
14457       Tile[x][y] = EL_SOKOBAN_OBJECT;
14458
14459       if (Back[x][y] == Back[nextx][nexty])
14460         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14461       else if (Back[x][y] != 0)
14462         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14463                                     ACTION_EMPTYING);
14464       else
14465         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14466                                     ACTION_FILLING);
14467
14468       if (sokoban_task_solved &&
14469           game.sokoban_fields_still_needed == 0 &&
14470           game.sokoban_objects_still_needed == 0 &&
14471           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14472       {
14473         game.players_still_needed = 0;
14474
14475         LevelSolved();
14476
14477         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14478       }
14479     }
14480     else
14481       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14482
14483     InitMovingField(x, y, move_direction);
14484     GfxAction[x][y] = ACTION_PUSHING;
14485
14486     if (mode == DF_SNAP)
14487       ContinueMoving(x, y);
14488     else
14489       MovPos[x][y] = (dx != 0 ? dx : dy);
14490
14491     Pushed[x][y] = TRUE;
14492     Pushed[nextx][nexty] = TRUE;
14493
14494     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14495       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14496     else
14497       player->push_delay_value = -1;    // get new value later
14498
14499     // check for element change _after_ element has been pushed
14500     if (game.use_change_when_pushing_bug)
14501     {
14502       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14503                                  player->index_bit, dig_side);
14504       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14505                                           player->index_bit, dig_side);
14506     }
14507   }
14508   else if (IS_SWITCHABLE(element))
14509   {
14510     if (PLAYER_SWITCHING(player, x, y))
14511     {
14512       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14513                                           player->index_bit, dig_side);
14514
14515       return MP_ACTION;
14516     }
14517
14518     player->is_switching = TRUE;
14519     player->switch_x = x;
14520     player->switch_y = y;
14521
14522     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14523
14524     if (element == EL_ROBOT_WHEEL)
14525     {
14526       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14527
14528       game.robot_wheel_x = x;
14529       game.robot_wheel_y = y;
14530       game.robot_wheel_active = TRUE;
14531
14532       TEST_DrawLevelField(x, y);
14533     }
14534     else if (element == EL_SP_TERMINAL)
14535     {
14536       int xx, yy;
14537
14538       SCAN_PLAYFIELD(xx, yy)
14539       {
14540         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14541         {
14542           Bang(xx, yy);
14543         }
14544         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14545         {
14546           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14547
14548           ResetGfxAnimation(xx, yy);
14549           TEST_DrawLevelField(xx, yy);
14550         }
14551       }
14552     }
14553     else if (IS_BELT_SWITCH(element))
14554     {
14555       ToggleBeltSwitch(x, y);
14556     }
14557     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14558              element == EL_SWITCHGATE_SWITCH_DOWN ||
14559              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14560              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14561     {
14562       ToggleSwitchgateSwitch(x, y);
14563     }
14564     else if (element == EL_LIGHT_SWITCH ||
14565              element == EL_LIGHT_SWITCH_ACTIVE)
14566     {
14567       ToggleLightSwitch(x, y);
14568     }
14569     else if (element == EL_TIMEGATE_SWITCH ||
14570              element == EL_DC_TIMEGATE_SWITCH)
14571     {
14572       ActivateTimegateSwitch(x, y);
14573     }
14574     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14575              element == EL_BALLOON_SWITCH_RIGHT ||
14576              element == EL_BALLOON_SWITCH_UP    ||
14577              element == EL_BALLOON_SWITCH_DOWN  ||
14578              element == EL_BALLOON_SWITCH_NONE  ||
14579              element == EL_BALLOON_SWITCH_ANY)
14580     {
14581       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14582                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14583                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14584                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14585                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14586                              move_direction);
14587     }
14588     else if (element == EL_LAMP)
14589     {
14590       Tile[x][y] = EL_LAMP_ACTIVE;
14591       game.lights_still_needed--;
14592
14593       ResetGfxAnimation(x, y);
14594       TEST_DrawLevelField(x, y);
14595     }
14596     else if (element == EL_TIME_ORB_FULL)
14597     {
14598       Tile[x][y] = EL_TIME_ORB_EMPTY;
14599
14600       if (level.time > 0 || level.use_time_orb_bug)
14601       {
14602         TimeLeft += level.time_orb_time;
14603         game.no_time_limit = FALSE;
14604
14605         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14606
14607         DisplayGameControlValues();
14608       }
14609
14610       ResetGfxAnimation(x, y);
14611       TEST_DrawLevelField(x, y);
14612     }
14613     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14614              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14615     {
14616       int xx, yy;
14617
14618       game.ball_active = !game.ball_active;
14619
14620       SCAN_PLAYFIELD(xx, yy)
14621       {
14622         int e = Tile[xx][yy];
14623
14624         if (game.ball_active)
14625         {
14626           if (e == EL_EMC_MAGIC_BALL)
14627             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14628           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14629             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14630         }
14631         else
14632         {
14633           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14634             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14635           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14636             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14637         }
14638       }
14639     }
14640
14641     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14642                                         player->index_bit, dig_side);
14643
14644     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14645                                         player->index_bit, dig_side);
14646
14647     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14648                                         player->index_bit, dig_side);
14649
14650     return MP_ACTION;
14651   }
14652   else
14653   {
14654     if (!PLAYER_SWITCHING(player, x, y))
14655     {
14656       player->is_switching = TRUE;
14657       player->switch_x = x;
14658       player->switch_y = y;
14659
14660       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14661                                  player->index_bit, dig_side);
14662       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14663                                           player->index_bit, dig_side);
14664
14665       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14666                                  player->index_bit, dig_side);
14667       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14668                                           player->index_bit, dig_side);
14669     }
14670
14671     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14672                                player->index_bit, dig_side);
14673     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14674                                         player->index_bit, dig_side);
14675
14676     return MP_NO_ACTION;
14677   }
14678
14679   player->push_delay = -1;
14680
14681   if (is_player)                // function can also be called by EL_PENGUIN
14682   {
14683     if (Tile[x][y] != element)          // really digged/collected something
14684     {
14685       player->is_collecting = !player->is_digging;
14686       player->is_active = TRUE;
14687
14688       player->last_removed_element = element;
14689     }
14690   }
14691
14692   return MP_MOVING;
14693 }
14694
14695 static boolean DigFieldByCE(int x, int y, int digging_element)
14696 {
14697   int element = Tile[x][y];
14698
14699   if (!IS_FREE(x, y))
14700   {
14701     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14702                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14703                   ACTION_BREAKING);
14704
14705     // no element can dig solid indestructible elements
14706     if (IS_INDESTRUCTIBLE(element) &&
14707         !IS_DIGGABLE(element) &&
14708         !IS_COLLECTIBLE(element))
14709       return FALSE;
14710
14711     if (AmoebaNr[x][y] &&
14712         (element == EL_AMOEBA_FULL ||
14713          element == EL_BD_AMOEBA ||
14714          element == EL_AMOEBA_GROWING))
14715     {
14716       AmoebaCnt[AmoebaNr[x][y]]--;
14717       AmoebaCnt2[AmoebaNr[x][y]]--;
14718     }
14719
14720     if (IS_MOVING(x, y))
14721       RemoveMovingField(x, y);
14722     else
14723     {
14724       RemoveField(x, y);
14725       TEST_DrawLevelField(x, y);
14726     }
14727
14728     // if digged element was about to explode, prevent the explosion
14729     ExplodeField[x][y] = EX_TYPE_NONE;
14730
14731     PlayLevelSoundAction(x, y, action);
14732   }
14733
14734   Store[x][y] = EL_EMPTY;
14735
14736   // this makes it possible to leave the removed element again
14737   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14738     Store[x][y] = element;
14739
14740   return TRUE;
14741 }
14742
14743 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14744 {
14745   int jx = player->jx, jy = player->jy;
14746   int x = jx + dx, y = jy + dy;
14747   int snap_direction = (dx == -1 ? MV_LEFT  :
14748                         dx == +1 ? MV_RIGHT :
14749                         dy == -1 ? MV_UP    :
14750                         dy == +1 ? MV_DOWN  : MV_NONE);
14751   boolean can_continue_snapping = (level.continuous_snapping &&
14752                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14753
14754   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14755     return FALSE;
14756
14757   if (!player->active || !IN_LEV_FIELD(x, y))
14758     return FALSE;
14759
14760   if (dx && dy)
14761     return FALSE;
14762
14763   if (!dx && !dy)
14764   {
14765     if (player->MovPos == 0)
14766       player->is_pushing = FALSE;
14767
14768     player->is_snapping = FALSE;
14769
14770     if (player->MovPos == 0)
14771     {
14772       player->is_moving = FALSE;
14773       player->is_digging = FALSE;
14774       player->is_collecting = FALSE;
14775     }
14776
14777     return FALSE;
14778   }
14779
14780   // prevent snapping with already pressed snap key when not allowed
14781   if (player->is_snapping && !can_continue_snapping)
14782     return FALSE;
14783
14784   player->MovDir = snap_direction;
14785
14786   if (player->MovPos == 0)
14787   {
14788     player->is_moving = FALSE;
14789     player->is_digging = FALSE;
14790     player->is_collecting = FALSE;
14791   }
14792
14793   player->is_dropping = FALSE;
14794   player->is_dropping_pressed = FALSE;
14795   player->drop_pressed_delay = 0;
14796
14797   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14798     return FALSE;
14799
14800   player->is_snapping = TRUE;
14801   player->is_active = TRUE;
14802
14803   if (player->MovPos == 0)
14804   {
14805     player->is_moving = FALSE;
14806     player->is_digging = FALSE;
14807     player->is_collecting = FALSE;
14808   }
14809
14810   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14811     TEST_DrawLevelField(player->last_jx, player->last_jy);
14812
14813   TEST_DrawLevelField(x, y);
14814
14815   return TRUE;
14816 }
14817
14818 static boolean DropElement(struct PlayerInfo *player)
14819 {
14820   int old_element, new_element;
14821   int dropx = player->jx, dropy = player->jy;
14822   int drop_direction = player->MovDir;
14823   int drop_side = drop_direction;
14824   int drop_element = get_next_dropped_element(player);
14825
14826   /* do not drop an element on top of another element; when holding drop key
14827      pressed without moving, dropped element must move away before the next
14828      element can be dropped (this is especially important if the next element
14829      is dynamite, which can be placed on background for historical reasons) */
14830   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14831     return MP_ACTION;
14832
14833   if (IS_THROWABLE(drop_element))
14834   {
14835     dropx += GET_DX_FROM_DIR(drop_direction);
14836     dropy += GET_DY_FROM_DIR(drop_direction);
14837
14838     if (!IN_LEV_FIELD(dropx, dropy))
14839       return FALSE;
14840   }
14841
14842   old_element = Tile[dropx][dropy];     // old element at dropping position
14843   new_element = drop_element;           // default: no change when dropping
14844
14845   // check if player is active, not moving and ready to drop
14846   if (!player->active || player->MovPos || player->drop_delay > 0)
14847     return FALSE;
14848
14849   // check if player has anything that can be dropped
14850   if (new_element == EL_UNDEFINED)
14851     return FALSE;
14852
14853   // only set if player has anything that can be dropped
14854   player->is_dropping_pressed = TRUE;
14855
14856   // check if drop key was pressed long enough for EM style dynamite
14857   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14858     return FALSE;
14859
14860   // check if anything can be dropped at the current position
14861   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14862     return FALSE;
14863
14864   // collected custom elements can only be dropped on empty fields
14865   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14866     return FALSE;
14867
14868   if (old_element != EL_EMPTY)
14869     Back[dropx][dropy] = old_element;   // store old element on this field
14870
14871   ResetGfxAnimation(dropx, dropy);
14872   ResetRandomAnimationValue(dropx, dropy);
14873
14874   if (player->inventory_size > 0 ||
14875       player->inventory_infinite_element != EL_UNDEFINED)
14876   {
14877     if (player->inventory_size > 0)
14878     {
14879       player->inventory_size--;
14880
14881       DrawGameDoorValues();
14882
14883       if (new_element == EL_DYNAMITE)
14884         new_element = EL_DYNAMITE_ACTIVE;
14885       else if (new_element == EL_EM_DYNAMITE)
14886         new_element = EL_EM_DYNAMITE_ACTIVE;
14887       else if (new_element == EL_SP_DISK_RED)
14888         new_element = EL_SP_DISK_RED_ACTIVE;
14889     }
14890
14891     Tile[dropx][dropy] = new_element;
14892
14893     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14894       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14895                           el2img(Tile[dropx][dropy]), 0);
14896
14897     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14898
14899     // needed if previous element just changed to "empty" in the last frame
14900     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14901
14902     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14903                                player->index_bit, drop_side);
14904     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14905                                         CE_PLAYER_DROPS_X,
14906                                         player->index_bit, drop_side);
14907
14908     TestIfElementTouchesCustomElement(dropx, dropy);
14909   }
14910   else          // player is dropping a dyna bomb
14911   {
14912     player->dynabombs_left--;
14913
14914     Tile[dropx][dropy] = new_element;
14915
14916     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14917       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14918                           el2img(Tile[dropx][dropy]), 0);
14919
14920     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14921   }
14922
14923   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14924     InitField_WithBug1(dropx, dropy, FALSE);
14925
14926   new_element = Tile[dropx][dropy];     // element might have changed
14927
14928   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14929       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14930   {
14931     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14932       MovDir[dropx][dropy] = drop_direction;
14933
14934     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14935
14936     // do not cause impact style collision by dropping elements that can fall
14937     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14938   }
14939
14940   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14941   player->is_dropping = TRUE;
14942
14943   player->drop_pressed_delay = 0;
14944   player->is_dropping_pressed = FALSE;
14945
14946   player->drop_x = dropx;
14947   player->drop_y = dropy;
14948
14949   return TRUE;
14950 }
14951
14952 // ----------------------------------------------------------------------------
14953 // game sound playing functions
14954 // ----------------------------------------------------------------------------
14955
14956 static int *loop_sound_frame = NULL;
14957 static int *loop_sound_volume = NULL;
14958
14959 void InitPlayLevelSound(void)
14960 {
14961   int num_sounds = getSoundListSize();
14962
14963   checked_free(loop_sound_frame);
14964   checked_free(loop_sound_volume);
14965
14966   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14967   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14968 }
14969
14970 static void PlayLevelSound(int x, int y, int nr)
14971 {
14972   int sx = SCREENX(x), sy = SCREENY(y);
14973   int volume, stereo_position;
14974   int max_distance = 8;
14975   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14976
14977   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14978       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14979     return;
14980
14981   if (!IN_LEV_FIELD(x, y) ||
14982       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14983       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14984     return;
14985
14986   volume = SOUND_MAX_VOLUME;
14987
14988   if (!IN_SCR_FIELD(sx, sy))
14989   {
14990     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14991     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14992
14993     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14994   }
14995
14996   stereo_position = (SOUND_MAX_LEFT +
14997                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14998                      (SCR_FIELDX + 2 * max_distance));
14999
15000   if (IS_LOOP_SOUND(nr))
15001   {
15002     /* This assures that quieter loop sounds do not overwrite louder ones,
15003        while restarting sound volume comparison with each new game frame. */
15004
15005     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15006       return;
15007
15008     loop_sound_volume[nr] = volume;
15009     loop_sound_frame[nr] = FrameCounter;
15010   }
15011
15012   PlaySoundExt(nr, volume, stereo_position, type);
15013 }
15014
15015 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15016 {
15017   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15018                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15019                  y < LEVELY(BY1) ? LEVELY(BY1) :
15020                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15021                  sound_action);
15022 }
15023
15024 static void PlayLevelSoundAction(int x, int y, int action)
15025 {
15026   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15027 }
15028
15029 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15030 {
15031   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15032
15033   if (sound_effect != SND_UNDEFINED)
15034     PlayLevelSound(x, y, sound_effect);
15035 }
15036
15037 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15038                                               int action)
15039 {
15040   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15041
15042   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15043     PlayLevelSound(x, y, sound_effect);
15044 }
15045
15046 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15047 {
15048   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15049
15050   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15051     PlayLevelSound(x, y, sound_effect);
15052 }
15053
15054 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15055 {
15056   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15057
15058   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15059     StopSound(sound_effect);
15060 }
15061
15062 static int getLevelMusicNr(void)
15063 {
15064   if (levelset.music[level_nr] != MUS_UNDEFINED)
15065     return levelset.music[level_nr];            // from config file
15066   else
15067     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15068 }
15069
15070 static void FadeLevelSounds(void)
15071 {
15072   FadeSounds();
15073 }
15074
15075 static void FadeLevelMusic(void)
15076 {
15077   int music_nr = getLevelMusicNr();
15078   char *curr_music = getCurrentlyPlayingMusicFilename();
15079   char *next_music = getMusicInfoEntryFilename(music_nr);
15080
15081   if (!strEqual(curr_music, next_music))
15082     FadeMusic();
15083 }
15084
15085 void FadeLevelSoundsAndMusic(void)
15086 {
15087   FadeLevelSounds();
15088   FadeLevelMusic();
15089 }
15090
15091 static void PlayLevelMusic(void)
15092 {
15093   int music_nr = getLevelMusicNr();
15094   char *curr_music = getCurrentlyPlayingMusicFilename();
15095   char *next_music = getMusicInfoEntryFilename(music_nr);
15096
15097   if (!strEqual(curr_music, next_music))
15098     PlayMusicLoop(music_nr);
15099 }
15100
15101 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15102 {
15103   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15104   int offset = 0;
15105   int x = xx - offset;
15106   int y = yy - offset;
15107
15108   switch (sample)
15109   {
15110     case SOUND_blank:
15111       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15112       break;
15113
15114     case SOUND_roll:
15115       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15116       break;
15117
15118     case SOUND_stone:
15119       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15120       break;
15121
15122     case SOUND_nut:
15123       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15124       break;
15125
15126     case SOUND_crack:
15127       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15128       break;
15129
15130     case SOUND_bug:
15131       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15132       break;
15133
15134     case SOUND_tank:
15135       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15136       break;
15137
15138     case SOUND_android_clone:
15139       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15140       break;
15141
15142     case SOUND_android_move:
15143       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15144       break;
15145
15146     case SOUND_spring:
15147       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15148       break;
15149
15150     case SOUND_slurp:
15151       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15152       break;
15153
15154     case SOUND_eater:
15155       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15156       break;
15157
15158     case SOUND_eater_eat:
15159       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15160       break;
15161
15162     case SOUND_alien:
15163       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15164       break;
15165
15166     case SOUND_collect:
15167       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15168       break;
15169
15170     case SOUND_diamond:
15171       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15172       break;
15173
15174     case SOUND_squash:
15175       // !!! CHECK THIS !!!
15176 #if 1
15177       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15178 #else
15179       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15180 #endif
15181       break;
15182
15183     case SOUND_wonderfall:
15184       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15185       break;
15186
15187     case SOUND_drip:
15188       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15189       break;
15190
15191     case SOUND_push:
15192       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15193       break;
15194
15195     case SOUND_dirt:
15196       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15197       break;
15198
15199     case SOUND_acid:
15200       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15201       break;
15202
15203     case SOUND_ball:
15204       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15205       break;
15206
15207     case SOUND_slide:
15208       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15209       break;
15210
15211     case SOUND_wonder:
15212       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15213       break;
15214
15215     case SOUND_door:
15216       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15217       break;
15218
15219     case SOUND_exit_open:
15220       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15221       break;
15222
15223     case SOUND_exit_leave:
15224       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15225       break;
15226
15227     case SOUND_dynamite:
15228       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15229       break;
15230
15231     case SOUND_tick:
15232       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15233       break;
15234
15235     case SOUND_press:
15236       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15237       break;
15238
15239     case SOUND_wheel:
15240       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15241       break;
15242
15243     case SOUND_boom:
15244       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15245       break;
15246
15247     case SOUND_die:
15248       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15249       break;
15250
15251     case SOUND_time:
15252       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15253       break;
15254
15255     default:
15256       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15257       break;
15258   }
15259 }
15260
15261 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15262 {
15263   int element = map_element_SP_to_RND(element_sp);
15264   int action = map_action_SP_to_RND(action_sp);
15265   int offset = (setup.sp_show_border_elements ? 0 : 1);
15266   int x = xx - offset;
15267   int y = yy - offset;
15268
15269   PlayLevelSoundElementAction(x, y, element, action);
15270 }
15271
15272 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15273 {
15274   int element = map_element_MM_to_RND(element_mm);
15275   int action = map_action_MM_to_RND(action_mm);
15276   int offset = 0;
15277   int x = xx - offset;
15278   int y = yy - offset;
15279
15280   if (!IS_MM_ELEMENT(element))
15281     element = EL_MM_DEFAULT;
15282
15283   PlayLevelSoundElementAction(x, y, element, action);
15284 }
15285
15286 void PlaySound_MM(int sound_mm)
15287 {
15288   int sound = map_sound_MM_to_RND(sound_mm);
15289
15290   if (sound == SND_UNDEFINED)
15291     return;
15292
15293   PlaySound(sound);
15294 }
15295
15296 void PlaySoundLoop_MM(int sound_mm)
15297 {
15298   int sound = map_sound_MM_to_RND(sound_mm);
15299
15300   if (sound == SND_UNDEFINED)
15301     return;
15302
15303   PlaySoundLoop(sound);
15304 }
15305
15306 void StopSound_MM(int sound_mm)
15307 {
15308   int sound = map_sound_MM_to_RND(sound_mm);
15309
15310   if (sound == SND_UNDEFINED)
15311     return;
15312
15313   StopSound(sound);
15314 }
15315
15316 void RaiseScore(int value)
15317 {
15318   game.score += value;
15319
15320   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15321
15322   DisplayGameControlValues();
15323 }
15324
15325 void RaiseScoreElement(int element)
15326 {
15327   switch (element)
15328   {
15329     case EL_EMERALD:
15330     case EL_BD_DIAMOND:
15331     case EL_EMERALD_YELLOW:
15332     case EL_EMERALD_RED:
15333     case EL_EMERALD_PURPLE:
15334     case EL_SP_INFOTRON:
15335       RaiseScore(level.score[SC_EMERALD]);
15336       break;
15337     case EL_DIAMOND:
15338       RaiseScore(level.score[SC_DIAMOND]);
15339       break;
15340     case EL_CRYSTAL:
15341       RaiseScore(level.score[SC_CRYSTAL]);
15342       break;
15343     case EL_PEARL:
15344       RaiseScore(level.score[SC_PEARL]);
15345       break;
15346     case EL_BUG:
15347     case EL_BD_BUTTERFLY:
15348     case EL_SP_ELECTRON:
15349       RaiseScore(level.score[SC_BUG]);
15350       break;
15351     case EL_SPACESHIP:
15352     case EL_BD_FIREFLY:
15353     case EL_SP_SNIKSNAK:
15354       RaiseScore(level.score[SC_SPACESHIP]);
15355       break;
15356     case EL_YAMYAM:
15357     case EL_DARK_YAMYAM:
15358       RaiseScore(level.score[SC_YAMYAM]);
15359       break;
15360     case EL_ROBOT:
15361       RaiseScore(level.score[SC_ROBOT]);
15362       break;
15363     case EL_PACMAN:
15364       RaiseScore(level.score[SC_PACMAN]);
15365       break;
15366     case EL_NUT:
15367       RaiseScore(level.score[SC_NUT]);
15368       break;
15369     case EL_DYNAMITE:
15370     case EL_EM_DYNAMITE:
15371     case EL_SP_DISK_RED:
15372     case EL_DYNABOMB_INCREASE_NUMBER:
15373     case EL_DYNABOMB_INCREASE_SIZE:
15374     case EL_DYNABOMB_INCREASE_POWER:
15375       RaiseScore(level.score[SC_DYNAMITE]);
15376       break;
15377     case EL_SHIELD_NORMAL:
15378     case EL_SHIELD_DEADLY:
15379       RaiseScore(level.score[SC_SHIELD]);
15380       break;
15381     case EL_EXTRA_TIME:
15382       RaiseScore(level.extra_time_score);
15383       break;
15384     case EL_KEY_1:
15385     case EL_KEY_2:
15386     case EL_KEY_3:
15387     case EL_KEY_4:
15388     case EL_EM_KEY_1:
15389     case EL_EM_KEY_2:
15390     case EL_EM_KEY_3:
15391     case EL_EM_KEY_4:
15392     case EL_EMC_KEY_5:
15393     case EL_EMC_KEY_6:
15394     case EL_EMC_KEY_7:
15395     case EL_EMC_KEY_8:
15396     case EL_DC_KEY_WHITE:
15397       RaiseScore(level.score[SC_KEY]);
15398       break;
15399     default:
15400       RaiseScore(element_info[element].collect_score);
15401       break;
15402   }
15403 }
15404
15405 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15406 {
15407   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15408   {
15409     if (!quick_quit)
15410     {
15411       // prevent short reactivation of overlay buttons while closing door
15412       SetOverlayActive(FALSE);
15413
15414       // door may still be open due to skipped or envelope style request
15415       CloseDoor(DOOR_CLOSE_1);
15416     }
15417
15418     if (network.enabled)
15419       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15420     else
15421     {
15422       if (quick_quit)
15423         FadeSkipNextFadeIn();
15424
15425       SetGameStatus(GAME_MODE_MAIN);
15426
15427       DrawMainMenu();
15428     }
15429   }
15430   else          // continue playing the game
15431   {
15432     if (tape.playing && tape.deactivate_display)
15433       TapeDeactivateDisplayOff(TRUE);
15434
15435     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15436
15437     if (tape.playing && tape.deactivate_display)
15438       TapeDeactivateDisplayOn();
15439   }
15440 }
15441
15442 void RequestQuitGame(boolean escape_key_pressed)
15443 {
15444   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15445   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15446                         level_editor_test_game);
15447   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15448                           quick_quit);
15449
15450   RequestQuitGameExt(skip_request, quick_quit,
15451                      "Do you really want to quit the game?");
15452 }
15453
15454 void RequestRestartGame(char *message)
15455 {
15456   game.restart_game_message = NULL;
15457
15458   boolean has_started_game = hasStartedNetworkGame();
15459   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15460
15461   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15462   {
15463     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15464   }
15465   else
15466   {
15467     // needed in case of envelope request to close game panel
15468     CloseDoor(DOOR_CLOSE_1);
15469
15470     SetGameStatus(GAME_MODE_MAIN);
15471
15472     DrawMainMenu();
15473   }
15474 }
15475
15476 void CheckGameOver(void)
15477 {
15478   static boolean last_game_over = FALSE;
15479   static int game_over_delay = 0;
15480   int game_over_delay_value = 50;
15481   boolean game_over = checkGameFailed();
15482
15483   // do not handle game over if request dialog is already active
15484   if (game.request_active)
15485     return;
15486
15487   // do not ask to play again if game was never actually played
15488   if (!game.GamePlayed)
15489     return;
15490
15491   if (!game_over)
15492   {
15493     last_game_over = FALSE;
15494     game_over_delay = game_over_delay_value;
15495
15496     return;
15497   }
15498
15499   if (game_over_delay > 0)
15500   {
15501     game_over_delay--;
15502
15503     return;
15504   }
15505
15506   if (last_game_over != game_over)
15507     game.restart_game_message = (hasStartedNetworkGame() ?
15508                                  "Game over! Play it again?" :
15509                                  "Game over!");
15510
15511   last_game_over = game_over;
15512 }
15513
15514 boolean checkGameSolved(void)
15515 {
15516   // set for all game engines if level was solved
15517   return game.LevelSolved_GameEnd;
15518 }
15519
15520 boolean checkGameFailed(void)
15521 {
15522   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15523     return (game_em.game_over && !game_em.level_solved);
15524   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15525     return (game_sp.game_over && !game_sp.level_solved);
15526   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15527     return (game_mm.game_over && !game_mm.level_solved);
15528   else                          // GAME_ENGINE_TYPE_RND
15529     return (game.GameOver && !game.LevelSolved);
15530 }
15531
15532 boolean checkGameEnded(void)
15533 {
15534   return (checkGameSolved() || checkGameFailed());
15535 }
15536
15537
15538 // ----------------------------------------------------------------------------
15539 // random generator functions
15540 // ----------------------------------------------------------------------------
15541
15542 unsigned int InitEngineRandom_RND(int seed)
15543 {
15544   game.num_random_calls = 0;
15545
15546   return InitEngineRandom(seed);
15547 }
15548
15549 unsigned int RND(int max)
15550 {
15551   if (max > 0)
15552   {
15553     game.num_random_calls++;
15554
15555     return GetEngineRandom(max);
15556   }
15557
15558   return 0;
15559 }
15560
15561
15562 // ----------------------------------------------------------------------------
15563 // game engine snapshot handling functions
15564 // ----------------------------------------------------------------------------
15565
15566 struct EngineSnapshotInfo
15567 {
15568   // runtime values for custom element collect score
15569   int collect_score[NUM_CUSTOM_ELEMENTS];
15570
15571   // runtime values for group element choice position
15572   int choice_pos[NUM_GROUP_ELEMENTS];
15573
15574   // runtime values for belt position animations
15575   int belt_graphic[4][NUM_BELT_PARTS];
15576   int belt_anim_mode[4][NUM_BELT_PARTS];
15577 };
15578
15579 static struct EngineSnapshotInfo engine_snapshot_rnd;
15580 static char *snapshot_level_identifier = NULL;
15581 static int snapshot_level_nr = -1;
15582
15583 static void SaveEngineSnapshotValues_RND(void)
15584 {
15585   static int belt_base_active_element[4] =
15586   {
15587     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15588     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15589     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15590     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15591   };
15592   int i, j;
15593
15594   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15595   {
15596     int element = EL_CUSTOM_START + i;
15597
15598     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15599   }
15600
15601   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15602   {
15603     int element = EL_GROUP_START + i;
15604
15605     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15606   }
15607
15608   for (i = 0; i < 4; i++)
15609   {
15610     for (j = 0; j < NUM_BELT_PARTS; j++)
15611     {
15612       int element = belt_base_active_element[i] + j;
15613       int graphic = el2img(element);
15614       int anim_mode = graphic_info[graphic].anim_mode;
15615
15616       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15617       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15618     }
15619   }
15620 }
15621
15622 static void LoadEngineSnapshotValues_RND(void)
15623 {
15624   unsigned int num_random_calls = game.num_random_calls;
15625   int i, j;
15626
15627   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15628   {
15629     int element = EL_CUSTOM_START + i;
15630
15631     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15632   }
15633
15634   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15635   {
15636     int element = EL_GROUP_START + i;
15637
15638     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15639   }
15640
15641   for (i = 0; i < 4; i++)
15642   {
15643     for (j = 0; j < NUM_BELT_PARTS; j++)
15644     {
15645       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15646       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15647
15648       graphic_info[graphic].anim_mode = anim_mode;
15649     }
15650   }
15651
15652   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15653   {
15654     InitRND(tape.random_seed);
15655     for (i = 0; i < num_random_calls; i++)
15656       RND(1);
15657   }
15658
15659   if (game.num_random_calls != num_random_calls)
15660   {
15661     Error("number of random calls out of sync");
15662     Error("number of random calls should be %d", num_random_calls);
15663     Error("number of random calls is %d", game.num_random_calls);
15664
15665     Fail("this should not happen -- please debug");
15666   }
15667 }
15668
15669 void FreeEngineSnapshotSingle(void)
15670 {
15671   FreeSnapshotSingle();
15672
15673   setString(&snapshot_level_identifier, NULL);
15674   snapshot_level_nr = -1;
15675 }
15676
15677 void FreeEngineSnapshotList(void)
15678 {
15679   FreeSnapshotList();
15680 }
15681
15682 static ListNode *SaveEngineSnapshotBuffers(void)
15683 {
15684   ListNode *buffers = NULL;
15685
15686   // copy some special values to a structure better suited for the snapshot
15687
15688   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15689     SaveEngineSnapshotValues_RND();
15690   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15691     SaveEngineSnapshotValues_EM();
15692   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15693     SaveEngineSnapshotValues_SP(&buffers);
15694   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15695     SaveEngineSnapshotValues_MM(&buffers);
15696
15697   // save values stored in special snapshot structure
15698
15699   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15700     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15701   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15702     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15703   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15704     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15705   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15706     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15707
15708   // save further RND engine values
15709
15710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15713
15714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15719
15720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15723
15724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15725
15726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15728
15729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15735   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15737   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15739   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15740   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15741   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15742   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15743   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15744   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15745   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15746   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15747
15748   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15750
15751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15754
15755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15757
15758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15760   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15761   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15763
15764   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15765   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15766
15767 #if 0
15768   ListNode *node = engine_snapshot_list_rnd;
15769   int num_bytes = 0;
15770
15771   while (node != NULL)
15772   {
15773     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15774
15775     node = node->next;
15776   }
15777
15778   Debug("game:playing:SaveEngineSnapshotBuffers",
15779         "size of engine snapshot: %d bytes", num_bytes);
15780 #endif
15781
15782   return buffers;
15783 }
15784
15785 void SaveEngineSnapshotSingle(void)
15786 {
15787   ListNode *buffers = SaveEngineSnapshotBuffers();
15788
15789   // finally save all snapshot buffers to single snapshot
15790   SaveSnapshotSingle(buffers);
15791
15792   // save level identification information
15793   setString(&snapshot_level_identifier, leveldir_current->identifier);
15794   snapshot_level_nr = level_nr;
15795 }
15796
15797 boolean CheckSaveEngineSnapshotToList(void)
15798 {
15799   boolean save_snapshot =
15800     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15801      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15802       game.snapshot.changed_action) ||
15803      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15804       game.snapshot.collected_item));
15805
15806   game.snapshot.changed_action = FALSE;
15807   game.snapshot.collected_item = FALSE;
15808   game.snapshot.save_snapshot = save_snapshot;
15809
15810   return save_snapshot;
15811 }
15812
15813 void SaveEngineSnapshotToList(void)
15814 {
15815   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15816       tape.quick_resume)
15817     return;
15818
15819   ListNode *buffers = SaveEngineSnapshotBuffers();
15820
15821   // finally save all snapshot buffers to snapshot list
15822   SaveSnapshotToList(buffers);
15823 }
15824
15825 void SaveEngineSnapshotToListInitial(void)
15826 {
15827   FreeEngineSnapshotList();
15828
15829   SaveEngineSnapshotToList();
15830 }
15831
15832 static void LoadEngineSnapshotValues(void)
15833 {
15834   // restore special values from snapshot structure
15835
15836   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15837     LoadEngineSnapshotValues_RND();
15838   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15839     LoadEngineSnapshotValues_EM();
15840   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15841     LoadEngineSnapshotValues_SP();
15842   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15843     LoadEngineSnapshotValues_MM();
15844 }
15845
15846 void LoadEngineSnapshotSingle(void)
15847 {
15848   LoadSnapshotSingle();
15849
15850   LoadEngineSnapshotValues();
15851 }
15852
15853 static void LoadEngineSnapshot_Undo(int steps)
15854 {
15855   LoadSnapshotFromList_Older(steps);
15856
15857   LoadEngineSnapshotValues();
15858 }
15859
15860 static void LoadEngineSnapshot_Redo(int steps)
15861 {
15862   LoadSnapshotFromList_Newer(steps);
15863
15864   LoadEngineSnapshotValues();
15865 }
15866
15867 boolean CheckEngineSnapshotSingle(void)
15868 {
15869   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15870           snapshot_level_nr == level_nr);
15871 }
15872
15873 boolean CheckEngineSnapshotList(void)
15874 {
15875   return CheckSnapshotList();
15876 }
15877
15878
15879 // ---------- new game button stuff -------------------------------------------
15880
15881 static struct
15882 {
15883   int graphic;
15884   struct XY *pos;
15885   int gadget_id;
15886   boolean *setup_value;
15887   boolean allowed_on_tape;
15888   boolean is_touch_button;
15889   char *infotext;
15890 } gamebutton_info[NUM_GAME_BUTTONS] =
15891 {
15892   {
15893     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15894     GAME_CTRL_ID_STOP,                          NULL,
15895     TRUE, FALSE,                                "stop game"
15896   },
15897   {
15898     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15899     GAME_CTRL_ID_PAUSE,                         NULL,
15900     TRUE, FALSE,                                "pause game"
15901   },
15902   {
15903     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15904     GAME_CTRL_ID_PLAY,                          NULL,
15905     TRUE, FALSE,                                "play game"
15906   },
15907   {
15908     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15909     GAME_CTRL_ID_UNDO,                          NULL,
15910     TRUE, FALSE,                                "undo step"
15911   },
15912   {
15913     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15914     GAME_CTRL_ID_REDO,                          NULL,
15915     TRUE, FALSE,                                "redo step"
15916   },
15917   {
15918     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15919     GAME_CTRL_ID_SAVE,                          NULL,
15920     TRUE, FALSE,                                "save game"
15921   },
15922   {
15923     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15924     GAME_CTRL_ID_PAUSE2,                        NULL,
15925     TRUE, FALSE,                                "pause game"
15926   },
15927   {
15928     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15929     GAME_CTRL_ID_LOAD,                          NULL,
15930     TRUE, FALSE,                                "load game"
15931   },
15932   {
15933     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15934     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15935     FALSE, FALSE,                               "stop game"
15936   },
15937   {
15938     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15939     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15940     FALSE, FALSE,                               "pause game"
15941   },
15942   {
15943     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15944     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15945     FALSE, FALSE,                               "play game"
15946   },
15947   {
15948     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15949     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15950     FALSE, TRUE,                                "stop game"
15951   },
15952   {
15953     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15954     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15955     FALSE, TRUE,                                "pause game"
15956   },
15957   {
15958     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15959     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15960     TRUE, FALSE,                                "background music on/off"
15961   },
15962   {
15963     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15964     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15965     TRUE, FALSE,                                "sound loops on/off"
15966   },
15967   {
15968     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15969     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15970     TRUE, FALSE,                                "normal sounds on/off"
15971   },
15972   {
15973     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15974     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15975     FALSE, FALSE,                               "background music on/off"
15976   },
15977   {
15978     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15979     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15980     FALSE, FALSE,                               "sound loops on/off"
15981   },
15982   {
15983     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15984     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15985     FALSE, FALSE,                               "normal sounds on/off"
15986   }
15987 };
15988
15989 void CreateGameButtons(void)
15990 {
15991   int i;
15992
15993   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15994   {
15995     int graphic = gamebutton_info[i].graphic;
15996     struct GraphicInfo *gfx = &graphic_info[graphic];
15997     struct XY *pos = gamebutton_info[i].pos;
15998     struct GadgetInfo *gi;
15999     int button_type;
16000     boolean checked;
16001     unsigned int event_mask;
16002     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16003     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16004     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16005     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16006     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16007     int gd_x   = gfx->src_x;
16008     int gd_y   = gfx->src_y;
16009     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16010     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16011     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16012     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16013     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16014     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16015     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16016     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16017     int id = i;
16018
16019     if (gfx->bitmap == NULL)
16020     {
16021       game_gadget[id] = NULL;
16022
16023       continue;
16024     }
16025
16026     if (id == GAME_CTRL_ID_STOP ||
16027         id == GAME_CTRL_ID_PANEL_STOP ||
16028         id == GAME_CTRL_ID_TOUCH_STOP ||
16029         id == GAME_CTRL_ID_PLAY ||
16030         id == GAME_CTRL_ID_PANEL_PLAY ||
16031         id == GAME_CTRL_ID_SAVE ||
16032         id == GAME_CTRL_ID_LOAD)
16033     {
16034       button_type = GD_TYPE_NORMAL_BUTTON;
16035       checked = FALSE;
16036       event_mask = GD_EVENT_RELEASED;
16037     }
16038     else if (id == GAME_CTRL_ID_UNDO ||
16039              id == GAME_CTRL_ID_REDO)
16040     {
16041       button_type = GD_TYPE_NORMAL_BUTTON;
16042       checked = FALSE;
16043       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16044     }
16045     else
16046     {
16047       button_type = GD_TYPE_CHECK_BUTTON;
16048       checked = (gamebutton_info[i].setup_value != NULL ?
16049                  *gamebutton_info[i].setup_value : FALSE);
16050       event_mask = GD_EVENT_PRESSED;
16051     }
16052
16053     gi = CreateGadget(GDI_CUSTOM_ID, id,
16054                       GDI_IMAGE_ID, graphic,
16055                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16056                       GDI_X, base_x + x,
16057                       GDI_Y, base_y + y,
16058                       GDI_WIDTH, gfx->width,
16059                       GDI_HEIGHT, gfx->height,
16060                       GDI_TYPE, button_type,
16061                       GDI_STATE, GD_BUTTON_UNPRESSED,
16062                       GDI_CHECKED, checked,
16063                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16064                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16065                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16066                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16067                       GDI_DIRECT_DRAW, FALSE,
16068                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16069                       GDI_EVENT_MASK, event_mask,
16070                       GDI_CALLBACK_ACTION, HandleGameButtons,
16071                       GDI_END);
16072
16073     if (gi == NULL)
16074       Fail("cannot create gadget");
16075
16076     game_gadget[id] = gi;
16077   }
16078 }
16079
16080 void FreeGameButtons(void)
16081 {
16082   int i;
16083
16084   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16085     FreeGadget(game_gadget[i]);
16086 }
16087
16088 static void UnmapGameButtonsAtSamePosition(int id)
16089 {
16090   int i;
16091
16092   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16093     if (i != id &&
16094         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16095         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16096       UnmapGadget(game_gadget[i]);
16097 }
16098
16099 static void UnmapGameButtonsAtSamePosition_All(void)
16100 {
16101   if (setup.show_snapshot_buttons)
16102   {
16103     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16104     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16105     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16106   }
16107   else
16108   {
16109     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16110     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16111     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16112
16113     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16114     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16115     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16116   }
16117 }
16118
16119 static void MapGameButtonsAtSamePosition(int id)
16120 {
16121   int i;
16122
16123   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16124     if (i != id &&
16125         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16126         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16127       MapGadget(game_gadget[i]);
16128
16129   UnmapGameButtonsAtSamePosition_All();
16130 }
16131
16132 void MapUndoRedoButtons(void)
16133 {
16134   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16135   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16136
16137   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16138   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16139 }
16140
16141 void UnmapUndoRedoButtons(void)
16142 {
16143   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16144   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16145
16146   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16147   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16148 }
16149
16150 void ModifyPauseButtons(void)
16151 {
16152   static int ids[] =
16153   {
16154     GAME_CTRL_ID_PAUSE,
16155     GAME_CTRL_ID_PAUSE2,
16156     GAME_CTRL_ID_PANEL_PAUSE,
16157     GAME_CTRL_ID_TOUCH_PAUSE,
16158     -1
16159   };
16160   int i;
16161
16162   for (i = 0; ids[i] > -1; i++)
16163     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16164 }
16165
16166 static void MapGameButtonsExt(boolean on_tape)
16167 {
16168   int i;
16169
16170   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16171     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16172         i != GAME_CTRL_ID_UNDO &&
16173         i != GAME_CTRL_ID_REDO)
16174       MapGadget(game_gadget[i]);
16175
16176   UnmapGameButtonsAtSamePosition_All();
16177
16178   RedrawGameButtons();
16179 }
16180
16181 static void UnmapGameButtonsExt(boolean on_tape)
16182 {
16183   int i;
16184
16185   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16186     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16187       UnmapGadget(game_gadget[i]);
16188 }
16189
16190 static void RedrawGameButtonsExt(boolean on_tape)
16191 {
16192   int i;
16193
16194   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16195     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16196       RedrawGadget(game_gadget[i]);
16197 }
16198
16199 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16200 {
16201   if (gi == NULL)
16202     return;
16203
16204   gi->checked = state;
16205 }
16206
16207 static void RedrawSoundButtonGadget(int id)
16208 {
16209   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16210              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16211              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16212              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16213              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16214              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16215              id);
16216
16217   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16218   RedrawGadget(game_gadget[id2]);
16219 }
16220
16221 void MapGameButtons(void)
16222 {
16223   MapGameButtonsExt(FALSE);
16224 }
16225
16226 void UnmapGameButtons(void)
16227 {
16228   UnmapGameButtonsExt(FALSE);
16229 }
16230
16231 void RedrawGameButtons(void)
16232 {
16233   RedrawGameButtonsExt(FALSE);
16234 }
16235
16236 void MapGameButtonsOnTape(void)
16237 {
16238   MapGameButtonsExt(TRUE);
16239 }
16240
16241 void UnmapGameButtonsOnTape(void)
16242 {
16243   UnmapGameButtonsExt(TRUE);
16244 }
16245
16246 void RedrawGameButtonsOnTape(void)
16247 {
16248   RedrawGameButtonsExt(TRUE);
16249 }
16250
16251 static void GameUndoRedoExt(void)
16252 {
16253   ClearPlayerAction();
16254
16255   tape.pausing = TRUE;
16256
16257   RedrawPlayfield();
16258   UpdateAndDisplayGameControlValues();
16259
16260   DrawCompleteVideoDisplay();
16261   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16262   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16263   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16264
16265   BackToFront();
16266 }
16267
16268 static void GameUndo(int steps)
16269 {
16270   if (!CheckEngineSnapshotList())
16271     return;
16272
16273   LoadEngineSnapshot_Undo(steps);
16274
16275   GameUndoRedoExt();
16276 }
16277
16278 static void GameRedo(int steps)
16279 {
16280   if (!CheckEngineSnapshotList())
16281     return;
16282
16283   LoadEngineSnapshot_Redo(steps);
16284
16285   GameUndoRedoExt();
16286 }
16287
16288 static void HandleGameButtonsExt(int id, int button)
16289 {
16290   static boolean game_undo_executed = FALSE;
16291   int steps = BUTTON_STEPSIZE(button);
16292   boolean handle_game_buttons =
16293     (game_status == GAME_MODE_PLAYING ||
16294      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16295
16296   if (!handle_game_buttons)
16297     return;
16298
16299   switch (id)
16300   {
16301     case GAME_CTRL_ID_STOP:
16302     case GAME_CTRL_ID_PANEL_STOP:
16303     case GAME_CTRL_ID_TOUCH_STOP:
16304       if (game_status == GAME_MODE_MAIN)
16305         break;
16306
16307       if (tape.playing)
16308         TapeStop();
16309       else
16310         RequestQuitGame(FALSE);
16311
16312       break;
16313
16314     case GAME_CTRL_ID_PAUSE:
16315     case GAME_CTRL_ID_PAUSE2:
16316     case GAME_CTRL_ID_PANEL_PAUSE:
16317     case GAME_CTRL_ID_TOUCH_PAUSE:
16318       if (network.enabled && game_status == GAME_MODE_PLAYING)
16319       {
16320         if (tape.pausing)
16321           SendToServer_ContinuePlaying();
16322         else
16323           SendToServer_PausePlaying();
16324       }
16325       else
16326         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16327
16328       game_undo_executed = FALSE;
16329
16330       break;
16331
16332     case GAME_CTRL_ID_PLAY:
16333     case GAME_CTRL_ID_PANEL_PLAY:
16334       if (game_status == GAME_MODE_MAIN)
16335       {
16336         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16337       }
16338       else if (tape.pausing)
16339       {
16340         if (network.enabled)
16341           SendToServer_ContinuePlaying();
16342         else
16343           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16344       }
16345       break;
16346
16347     case GAME_CTRL_ID_UNDO:
16348       // Important: When using "save snapshot when collecting an item" mode,
16349       // load last (current) snapshot for first "undo" after pressing "pause"
16350       // (else the last-but-one snapshot would be loaded, because the snapshot
16351       // pointer already points to the last snapshot when pressing "pause",
16352       // which is fine for "every step/move" mode, but not for "every collect")
16353       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16354           !game_undo_executed)
16355         steps--;
16356
16357       game_undo_executed = TRUE;
16358
16359       GameUndo(steps);
16360       break;
16361
16362     case GAME_CTRL_ID_REDO:
16363       GameRedo(steps);
16364       break;
16365
16366     case GAME_CTRL_ID_SAVE:
16367       TapeQuickSave();
16368       break;
16369
16370     case GAME_CTRL_ID_LOAD:
16371       TapeQuickLoad();
16372       break;
16373
16374     case SOUND_CTRL_ID_MUSIC:
16375     case SOUND_CTRL_ID_PANEL_MUSIC:
16376       if (setup.sound_music)
16377       { 
16378         setup.sound_music = FALSE;
16379
16380         FadeMusic();
16381       }
16382       else if (audio.music_available)
16383       { 
16384         setup.sound = setup.sound_music = TRUE;
16385
16386         SetAudioMode(setup.sound);
16387
16388         if (game_status == GAME_MODE_PLAYING)
16389           PlayLevelMusic();
16390       }
16391
16392       RedrawSoundButtonGadget(id);
16393
16394       break;
16395
16396     case SOUND_CTRL_ID_LOOPS:
16397     case SOUND_CTRL_ID_PANEL_LOOPS:
16398       if (setup.sound_loops)
16399         setup.sound_loops = FALSE;
16400       else if (audio.loops_available)
16401       {
16402         setup.sound = setup.sound_loops = TRUE;
16403
16404         SetAudioMode(setup.sound);
16405       }
16406
16407       RedrawSoundButtonGadget(id);
16408
16409       break;
16410
16411     case SOUND_CTRL_ID_SIMPLE:
16412     case SOUND_CTRL_ID_PANEL_SIMPLE:
16413       if (setup.sound_simple)
16414         setup.sound_simple = FALSE;
16415       else if (audio.sound_available)
16416       {
16417         setup.sound = setup.sound_simple = TRUE;
16418
16419         SetAudioMode(setup.sound);
16420       }
16421
16422       RedrawSoundButtonGadget(id);
16423
16424       break;
16425
16426     default:
16427       break;
16428   }
16429 }
16430
16431 static void HandleGameButtons(struct GadgetInfo *gi)
16432 {
16433   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16434 }
16435
16436 void HandleSoundButtonKeys(Key key)
16437 {
16438   if (key == setup.shortcut.sound_simple)
16439     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16440   else if (key == setup.shortcut.sound_loops)
16441     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16442   else if (key == setup.shortcut.sound_music)
16443     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16444 }