added function to load and merge local and server scores
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void TestIfPlayerTouchesCustomElement(int, int);
1062 static void TestIfElementTouchesCustomElement(int, int);
1063 static void TestIfElementHitsCustomElement(int, int, int);
1064
1065 static void HandleElementChange(int, int, int);
1066 static void ExecuteCustomElementAction(int, int, int, int);
1067 static boolean ChangeElement(int, int, int, int);
1068
1069 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1070 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1072 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1073         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1074 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1075         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1076 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1077         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1078 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1079         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1080
1081 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1082 #define CheckElementChange(x, y, e, te, ev)                             \
1083         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1084 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1085         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1086 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1087         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1088 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1089         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1090
1091 static void PlayLevelSound(int, int, int);
1092 static void PlayLevelSoundNearest(int, int, int);
1093 static void PlayLevelSoundAction(int, int, int);
1094 static void PlayLevelSoundElementAction(int, int, int, int);
1095 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1096 static void PlayLevelSoundActionIfLoop(int, int, int);
1097 static void StopLevelSoundActionIfLoop(int, int, int);
1098 static void PlayLevelMusic(void);
1099 static void FadeLevelSoundsAndMusic(void);
1100
1101 static void HandleGameButtons(struct GadgetInfo *);
1102
1103 int AmoebaNeighbourNr(int, int);
1104 void AmoebaToDiamond(int, int);
1105 void ContinueMoving(int, int);
1106 void Bang(int, int);
1107 void InitMovDir(int, int);
1108 void InitAmoebaNr(int, int);
1109 void NewHighScore(int);
1110
1111 void TestIfGoodThingHitsBadThing(int, int, int);
1112 void TestIfBadThingHitsGoodThing(int, int, int);
1113 void TestIfPlayerTouchesBadThing(int, int);
1114 void TestIfPlayerRunsIntoBadThing(int, int, int);
1115 void TestIfBadThingTouchesPlayer(int, int);
1116 void TestIfBadThingRunsIntoPlayer(int, int, int);
1117 void TestIfFriendTouchesBadThing(int, int);
1118 void TestIfBadThingTouchesFriend(int, int);
1119 void TestIfBadThingTouchesOtherBadThing(int, int);
1120 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1121
1122 void KillPlayer(struct PlayerInfo *);
1123 void BuryPlayer(struct PlayerInfo *);
1124 void RemovePlayer(struct PlayerInfo *);
1125 void ExitPlayer(struct PlayerInfo *);
1126
1127 static int getInvisibleActiveFromInvisibleElement(int);
1128 static int getInvisibleFromInvisibleActiveElement(int);
1129
1130 static void TestFieldAfterSnapping(int, int, int, int, int);
1131
1132 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1133
1134 // for detection of endless loops, caused by custom element programming
1135 // (using maximal playfield width x 10 is just a rough approximation)
1136 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1137
1138 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1139 {                                                                       \
1140   if (recursion_loop_detected)                                          \
1141     return (rc);                                                        \
1142                                                                         \
1143   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1144   {                                                                     \
1145     recursion_loop_detected = TRUE;                                     \
1146     recursion_loop_element = (e);                                       \
1147   }                                                                     \
1148                                                                         \
1149   recursion_loop_depth++;                                               \
1150 }
1151
1152 #define RECURSION_LOOP_DETECTION_END()                                  \
1153 {                                                                       \
1154   recursion_loop_depth--;                                               \
1155 }
1156
1157 static int recursion_loop_depth;
1158 static boolean recursion_loop_detected;
1159 static boolean recursion_loop_element;
1160
1161 static int map_player_action[MAX_PLAYERS];
1162
1163
1164 // ----------------------------------------------------------------------------
1165 // definition of elements that automatically change to other elements after
1166 // a specified time, eventually calling a function when changing
1167 // ----------------------------------------------------------------------------
1168
1169 // forward declaration for changer functions
1170 static void InitBuggyBase(int, int);
1171 static void WarnBuggyBase(int, int);
1172
1173 static void InitTrap(int, int);
1174 static void ActivateTrap(int, int);
1175 static void ChangeActiveTrap(int, int);
1176
1177 static void InitRobotWheel(int, int);
1178 static void RunRobotWheel(int, int);
1179 static void StopRobotWheel(int, int);
1180
1181 static void InitTimegateWheel(int, int);
1182 static void RunTimegateWheel(int, int);
1183
1184 static void InitMagicBallDelay(int, int);
1185 static void ActivateMagicBall(int, int);
1186
1187 struct ChangingElementInfo
1188 {
1189   int element;
1190   int target_element;
1191   int change_delay;
1192   void (*pre_change_function)(int x, int y);
1193   void (*change_function)(int x, int y);
1194   void (*post_change_function)(int x, int y);
1195 };
1196
1197 static struct ChangingElementInfo change_delay_list[] =
1198 {
1199   {
1200     EL_NUT_BREAKING,
1201     EL_EMERALD,
1202     6,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_PEARL_BREAKING,
1209     EL_EMPTY,
1210     8,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_OPENING,
1217     EL_EXIT_OPEN,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_EXIT_CLOSING,
1225     EL_EXIT_CLOSED,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_OPENING,
1233     EL_STEEL_EXIT_OPEN,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_STEEL_EXIT_CLOSING,
1241     EL_STEEL_EXIT_CLOSED,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_OPENING,
1249     EL_EM_EXIT_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_EXIT_CLOSING,
1257     EL_EMPTY,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_OPENING,
1265     EL_EM_STEEL_EXIT_OPEN,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_STEEL_EXIT_CLOSING,
1273     EL_STEELWALL,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_OPENING,
1281     EL_SP_EXIT_OPEN,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_EXIT_CLOSING,
1289     EL_SP_EXIT_CLOSED,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_OPENING,
1297     EL_SWITCHGATE_OPEN,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SWITCHGATE_CLOSING,
1305     EL_SWITCHGATE_CLOSED,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_OPENING,
1313     EL_TIMEGATE_OPEN,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_TIMEGATE_CLOSING,
1321     EL_TIMEGATE_CLOSED,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327
1328   {
1329     EL_ACID_SPLASH_LEFT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_ACID_SPLASH_RIGHT,
1338     EL_EMPTY,
1339     8,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE,
1346     EL_SP_BUGGY_BASE_ACTIVATING,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVATING,
1354     EL_SP_BUGGY_BASE_ACTIVE,
1355     0,
1356     InitBuggyBase,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_SP_BUGGY_BASE_ACTIVE,
1362     EL_SP_BUGGY_BASE,
1363     0,
1364     InitBuggyBase,
1365     WarnBuggyBase,
1366     NULL
1367   },
1368   {
1369     EL_TRAP,
1370     EL_TRAP_ACTIVE,
1371     0,
1372     InitTrap,
1373     NULL,
1374     ActivateTrap
1375   },
1376   {
1377     EL_TRAP_ACTIVE,
1378     EL_TRAP,
1379     31,
1380     NULL,
1381     ChangeActiveTrap,
1382     NULL
1383   },
1384   {
1385     EL_ROBOT_WHEEL_ACTIVE,
1386     EL_ROBOT_WHEEL,
1387     0,
1388     InitRobotWheel,
1389     RunRobotWheel,
1390     StopRobotWheel
1391   },
1392   {
1393     EL_TIMEGATE_SWITCH_ACTIVE,
1394     EL_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1402     EL_DC_TIMEGATE_SWITCH,
1403     0,
1404     InitTimegateWheel,
1405     RunTimegateWheel,
1406     NULL
1407   },
1408   {
1409     EL_EMC_MAGIC_BALL_ACTIVE,
1410     EL_EMC_MAGIC_BALL_ACTIVE,
1411     0,
1412     InitMagicBallDelay,
1413     NULL,
1414     ActivateMagicBall
1415   },
1416   {
1417     EL_EMC_SPRING_BUMPER_ACTIVE,
1418     EL_EMC_SPRING_BUMPER,
1419     8,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_SHRINKING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL
1431   },
1432   {
1433     EL_DIAGONAL_GROWING,
1434     EL_UNDEFINED,
1435     0,
1436     NULL,
1437     NULL,
1438     NULL,
1439   },
1440
1441   {
1442     EL_UNDEFINED,
1443     EL_UNDEFINED,
1444     -1,
1445     NULL,
1446     NULL,
1447     NULL
1448   }
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int push_delay_fixed, push_delay_random;
1455 }
1456 push_delay_list[] =
1457 {
1458   { EL_SPRING,                  0, 0 },
1459   { EL_BALLOON,                 0, 0 },
1460
1461   { EL_SOKOBAN_OBJECT,          2, 0 },
1462   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1463   { EL_SATELLITE,               2, 0 },
1464   { EL_SP_DISK_YELLOW,          2, 0 },
1465
1466   { EL_UNDEFINED,               0, 0 },
1467 };
1468
1469 struct
1470 {
1471   int element;
1472   int move_stepsize;
1473 }
1474 move_stepsize_list[] =
1475 {
1476   { EL_AMOEBA_DROP,             2 },
1477   { EL_AMOEBA_DROPPING,         2 },
1478   { EL_QUICKSAND_FILLING,       1 },
1479   { EL_QUICKSAND_EMPTYING,      1 },
1480   { EL_QUICKSAND_FAST_FILLING,  2 },
1481   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1482   { EL_MAGIC_WALL_FILLING,      2 },
1483   { EL_MAGIC_WALL_EMPTYING,     2 },
1484   { EL_BD_MAGIC_WALL_FILLING,   2 },
1485   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1486   { EL_DC_MAGIC_WALL_FILLING,   2 },
1487   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1488
1489   { EL_UNDEFINED,               0 },
1490 };
1491
1492 struct
1493 {
1494   int element;
1495   int count;
1496 }
1497 collect_count_list[] =
1498 {
1499   { EL_EMERALD,                 1 },
1500   { EL_BD_DIAMOND,              1 },
1501   { EL_EMERALD_YELLOW,          1 },
1502   { EL_EMERALD_RED,             1 },
1503   { EL_EMERALD_PURPLE,          1 },
1504   { EL_DIAMOND,                 3 },
1505   { EL_SP_INFOTRON,             1 },
1506   { EL_PEARL,                   5 },
1507   { EL_CRYSTAL,                 8 },
1508
1509   { EL_UNDEFINED,               0 },
1510 };
1511
1512 struct
1513 {
1514   int element;
1515   int direction;
1516 }
1517 access_direction_list[] =
1518 {
1519   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1521   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1522   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1525   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1526   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1527   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1528   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1529   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1530
1531   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1532   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1533   { EL_SP_PORT_UP,                                                   MV_DOWN },
1534   { EL_SP_PORT_DOWN,                                         MV_UP           },
1535   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1536   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1537   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1538   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1539   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1540   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1542   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1543   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1544   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1545   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1546   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1547   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1548   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1549   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1550
1551   { EL_UNDEFINED,                       MV_NONE                              }
1552 };
1553
1554 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1555
1556 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1557 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1558 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1559                                  IS_JUST_CHANGING(x, y))
1560
1561 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1562
1563 // static variables for playfield scan mode (scanning forward or backward)
1564 static int playfield_scan_start_x = 0;
1565 static int playfield_scan_start_y = 0;
1566 static int playfield_scan_delta_x = 1;
1567 static int playfield_scan_delta_y = 1;
1568
1569 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1570                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1571                                      (y) += playfield_scan_delta_y)     \
1572                                 for ((x) = playfield_scan_start_x;      \
1573                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1574                                      (x) += playfield_scan_delta_x)
1575
1576 #ifdef DEBUG
1577 void DEBUG_SetMaximumDynamite(void)
1578 {
1579   int i;
1580
1581   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1582     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1583       local_player->inventory_element[local_player->inventory_size++] =
1584         EL_DYNAMITE;
1585 }
1586 #endif
1587
1588 static void InitPlayfieldScanModeVars(void)
1589 {
1590   if (game.use_reverse_scan_direction)
1591   {
1592     playfield_scan_start_x = lev_fieldx - 1;
1593     playfield_scan_start_y = lev_fieldy - 1;
1594
1595     playfield_scan_delta_x = -1;
1596     playfield_scan_delta_y = -1;
1597   }
1598   else
1599   {
1600     playfield_scan_start_x = 0;
1601     playfield_scan_start_y = 0;
1602
1603     playfield_scan_delta_x = 1;
1604     playfield_scan_delta_y = 1;
1605   }
1606 }
1607
1608 static void InitPlayfieldScanMode(int mode)
1609 {
1610   game.use_reverse_scan_direction =
1611     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1612
1613   InitPlayfieldScanModeVars();
1614 }
1615
1616 static int get_move_delay_from_stepsize(int move_stepsize)
1617 {
1618   move_stepsize =
1619     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1620
1621   // make sure that stepsize value is always a power of 2
1622   move_stepsize = (1 << log_2(move_stepsize));
1623
1624   return TILEX / move_stepsize;
1625 }
1626
1627 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1628                                boolean init_game)
1629 {
1630   int player_nr = player->index_nr;
1631   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1632   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1633
1634   // do no immediately change move delay -- the player might just be moving
1635   player->move_delay_value_next = move_delay;
1636
1637   // information if player can move must be set separately
1638   player->cannot_move = cannot_move;
1639
1640   if (init_game)
1641   {
1642     player->move_delay       = game.initial_move_delay[player_nr];
1643     player->move_delay_value = game.initial_move_delay_value[player_nr];
1644
1645     player->move_delay_value_next = -1;
1646
1647     player->move_delay_reset_counter = 0;
1648   }
1649 }
1650
1651 void GetPlayerConfig(void)
1652 {
1653   GameFrameDelay = setup.game_frame_delay;
1654
1655   if (!audio.sound_available)
1656     setup.sound_simple = FALSE;
1657
1658   if (!audio.loops_available)
1659     setup.sound_loops = FALSE;
1660
1661   if (!audio.music_available)
1662     setup.sound_music = FALSE;
1663
1664   if (!video.fullscreen_available)
1665     setup.fullscreen = FALSE;
1666
1667   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1668
1669   SetAudioMode(setup.sound);
1670 }
1671
1672 int GetElementFromGroupElement(int element)
1673 {
1674   if (IS_GROUP_ELEMENT(element))
1675   {
1676     struct ElementGroupInfo *group = element_info[element].group;
1677     int last_anim_random_frame = gfx.anim_random_frame;
1678     int element_pos;
1679
1680     if (group->choice_mode == ANIM_RANDOM)
1681       gfx.anim_random_frame = RND(group->num_elements_resolved);
1682
1683     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1684                                     group->choice_mode, 0,
1685                                     group->choice_pos);
1686
1687     if (group->choice_mode == ANIM_RANDOM)
1688       gfx.anim_random_frame = last_anim_random_frame;
1689
1690     group->choice_pos++;
1691
1692     element = group->element_resolved[element_pos];
1693   }
1694
1695   return element;
1696 }
1697
1698 static void IncrementSokobanFieldsNeeded(void)
1699 {
1700   if (level.sb_fields_needed)
1701     game.sokoban_fields_still_needed++;
1702 }
1703
1704 static void IncrementSokobanObjectsNeeded(void)
1705 {
1706   if (level.sb_objects_needed)
1707     game.sokoban_objects_still_needed++;
1708 }
1709
1710 static void DecrementSokobanFieldsNeeded(void)
1711 {
1712   if (game.sokoban_fields_still_needed > 0)
1713     game.sokoban_fields_still_needed--;
1714 }
1715
1716 static void DecrementSokobanObjectsNeeded(void)
1717 {
1718   if (game.sokoban_objects_still_needed > 0)
1719     game.sokoban_objects_still_needed--;
1720 }
1721
1722 static void InitPlayerField(int x, int y, int element, boolean init_game)
1723 {
1724   if (element == EL_SP_MURPHY)
1725   {
1726     if (init_game)
1727     {
1728       if (stored_player[0].present)
1729       {
1730         Tile[x][y] = EL_SP_MURPHY_CLONE;
1731
1732         return;
1733       }
1734       else
1735       {
1736         stored_player[0].initial_element = element;
1737         stored_player[0].use_murphy = TRUE;
1738
1739         if (!level.use_artwork_element[0])
1740           stored_player[0].artwork_element = EL_SP_MURPHY;
1741       }
1742
1743       Tile[x][y] = EL_PLAYER_1;
1744     }
1745   }
1746
1747   if (init_game)
1748   {
1749     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1750     int jx = player->jx, jy = player->jy;
1751
1752     player->present = TRUE;
1753
1754     player->block_last_field = (element == EL_SP_MURPHY ?
1755                                 level.sp_block_last_field :
1756                                 level.block_last_field);
1757
1758     // ---------- initialize player's last field block delay ------------------
1759
1760     // always start with reliable default value (no adjustment needed)
1761     player->block_delay_adjustment = 0;
1762
1763     // special case 1: in Supaplex, Murphy blocks last field one more frame
1764     if (player->block_last_field && element == EL_SP_MURPHY)
1765       player->block_delay_adjustment = 1;
1766
1767     // special case 2: in game engines before 3.1.1, blocking was different
1768     if (game.use_block_last_field_bug)
1769       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1770
1771     if (!network.enabled || player->connected_network)
1772     {
1773       player->active = TRUE;
1774
1775       // remove potentially duplicate players
1776       if (StorePlayer[jx][jy] == Tile[x][y])
1777         StorePlayer[jx][jy] = 0;
1778
1779       StorePlayer[x][y] = Tile[x][y];
1780
1781 #if DEBUG_INIT_PLAYER
1782       Debug("game:init:player", "- player element %d activated",
1783             player->element_nr);
1784       Debug("game:init:player", "  (local player is %d and currently %s)",
1785             local_player->element_nr,
1786             local_player->active ? "active" : "not active");
1787     }
1788 #endif
1789
1790     Tile[x][y] = EL_EMPTY;
1791
1792     player->jx = player->last_jx = x;
1793     player->jy = player->last_jy = y;
1794   }
1795
1796   // always check if player was just killed and should be reanimated
1797   {
1798     int player_nr = GET_PLAYER_NR(element);
1799     struct PlayerInfo *player = &stored_player[player_nr];
1800
1801     if (player->active && player->killed)
1802       player->reanimated = TRUE; // if player was just killed, reanimate him
1803   }
1804 }
1805
1806 static void InitField(int x, int y, boolean init_game)
1807 {
1808   int element = Tile[x][y];
1809
1810   switch (element)
1811   {
1812     case EL_SP_MURPHY:
1813     case EL_PLAYER_1:
1814     case EL_PLAYER_2:
1815     case EL_PLAYER_3:
1816     case EL_PLAYER_4:
1817       InitPlayerField(x, y, element, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_PLAYER:
1821       element = Tile[x][y] = EL_PLAYER_1;
1822       InitField(x, y, init_game);
1823
1824       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1825       InitField(x, y, init_game);
1826       break;
1827
1828     case EL_SOKOBAN_FIELD_EMPTY:
1829       IncrementSokobanFieldsNeeded();
1830       break;
1831
1832     case EL_SOKOBAN_OBJECT:
1833       IncrementSokobanObjectsNeeded();
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1838         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1840         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1844         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888     case EL_SPRING_LEFT:
1889     case EL_SPRING_RIGHT:
1890       InitMovDir(x, y);
1891       break;
1892
1893     case EL_AMOEBA_FULL:
1894     case EL_BD_AMOEBA:
1895       InitAmoebaNr(x, y);
1896       break;
1897
1898     case EL_AMOEBA_DROP:
1899       if (y == lev_fieldy - 1)
1900       {
1901         Tile[x][y] = EL_AMOEBA_GROWING;
1902         Store[x][y] = EL_AMOEBA_WET;
1903       }
1904       break;
1905
1906     case EL_DYNAMITE_ACTIVE:
1907     case EL_SP_DISK_RED_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1910     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1912       MovDelay[x][y] = 96;
1913       break;
1914
1915     case EL_EM_DYNAMITE_ACTIVE:
1916       MovDelay[x][y] = 32;
1917       break;
1918
1919     case EL_LAMP:
1920       game.lights_still_needed++;
1921       break;
1922
1923     case EL_PENGUIN:
1924       game.friends_still_needed++;
1925       break;
1926
1927     case EL_PIG:
1928     case EL_DRAGON:
1929       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930       break;
1931
1932     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1933     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1934     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1935     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944       if (init_game)
1945       {
1946         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1947         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1948         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1949
1950         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1951         {
1952           game.belt_dir[belt_nr] = belt_dir;
1953           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1954         }
1955         else    // more than one switch -- set it like the first switch
1956         {
1957           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1958         }
1959       }
1960       break;
1961
1962     case EL_LIGHT_SWITCH_ACTIVE:
1963       if (init_game)
1964         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1965       break;
1966
1967     case EL_INVISIBLE_STEELWALL:
1968     case EL_INVISIBLE_WALL:
1969     case EL_INVISIBLE_SAND:
1970       if (game.light_time_left > 0 ||
1971           game.lenses_time_left > 0)
1972         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1973       break;
1974
1975     case EL_EMC_MAGIC_BALL:
1976       if (game.ball_active)
1977         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1978       break;
1979
1980     case EL_EMC_MAGIC_BALL_SWITCH:
1981       if (game.ball_active)
1982         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1983       break;
1984
1985     case EL_TRIGGER_PLAYER:
1986     case EL_TRIGGER_ELEMENT:
1987     case EL_TRIGGER_CE_VALUE:
1988     case EL_TRIGGER_CE_SCORE:
1989     case EL_SELF:
1990     case EL_ANY_ELEMENT:
1991     case EL_CURRENT_CE_VALUE:
1992     case EL_CURRENT_CE_SCORE:
1993     case EL_PREV_CE_1:
1994     case EL_PREV_CE_2:
1995     case EL_PREV_CE_3:
1996     case EL_PREV_CE_4:
1997     case EL_PREV_CE_5:
1998     case EL_PREV_CE_6:
1999     case EL_PREV_CE_7:
2000     case EL_PREV_CE_8:
2001     case EL_NEXT_CE_1:
2002     case EL_NEXT_CE_2:
2003     case EL_NEXT_CE_3:
2004     case EL_NEXT_CE_4:
2005     case EL_NEXT_CE_5:
2006     case EL_NEXT_CE_6:
2007     case EL_NEXT_CE_7:
2008     case EL_NEXT_CE_8:
2009       // reference elements should not be used on the playfield
2010       Tile[x][y] = EL_EMPTY;
2011       break;
2012
2013     default:
2014       if (IS_CUSTOM_ELEMENT(element))
2015       {
2016         if (CAN_MOVE(element))
2017           InitMovDir(x, y);
2018
2019         if (!element_info[element].use_last_ce_value || init_game)
2020           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2021       }
2022       else if (IS_GROUP_ELEMENT(element))
2023       {
2024         Tile[x][y] = GetElementFromGroupElement(element);
2025
2026         InitField(x, y, init_game);
2027       }
2028
2029       break;
2030   }
2031
2032   if (!init_game)
2033     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2034 }
2035
2036 static void InitField_WithBug1(int x, int y, boolean init_game)
2037 {
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(Tile[x][y]))
2043     InitMovDir(x, y);
2044 }
2045
2046 static void InitField_WithBug2(int x, int y, boolean init_game)
2047 {
2048   int old_element = Tile[x][y];
2049
2050   InitField(x, y, init_game);
2051
2052   // not needed to call InitMovDir() -- already done by InitField()!
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(old_element) &&
2055       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2056     InitMovDir(x, y);
2057
2058   /* this case is in fact a combination of not less than three bugs:
2059      first, it calls InitMovDir() for elements that can move, although this is
2060      already done by InitField(); then, it checks the element that was at this
2061      field _before_ the call to InitField() (which can change it); lastly, it
2062      was not called for "mole with direction" elements, which were treated as
2063      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2064   */
2065 }
2066
2067 static int get_key_element_from_nr(int key_nr)
2068 {
2069   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2070                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2071                           EL_EM_KEY_1 : EL_KEY_1);
2072
2073   return key_base_element + key_nr;
2074 }
2075
2076 static int get_next_dropped_element(struct PlayerInfo *player)
2077 {
2078   return (player->inventory_size > 0 ?
2079           player->inventory_element[player->inventory_size - 1] :
2080           player->inventory_infinite_element != EL_UNDEFINED ?
2081           player->inventory_infinite_element :
2082           player->dynabombs_left > 0 ?
2083           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2084           EL_UNDEFINED);
2085 }
2086
2087 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2088 {
2089   // pos >= 0: get element from bottom of the stack;
2090   // pos <  0: get element from top of the stack
2091
2092   if (pos < 0)
2093   {
2094     int min_inventory_size = -pos;
2095     int inventory_pos = player->inventory_size - min_inventory_size;
2096     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2097
2098     return (player->inventory_size >= min_inventory_size ?
2099             player->inventory_element[inventory_pos] :
2100             player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             EL_UNDEFINED);
2105   }
2106   else
2107   {
2108     int min_dynabombs_left = pos + 1;
2109     int min_inventory_size = pos + 1 - player->dynabombs_left;
2110     int inventory_pos = pos - player->dynabombs_left;
2111
2112     return (player->inventory_infinite_element != EL_UNDEFINED ?
2113             player->inventory_infinite_element :
2114             player->dynabombs_left >= min_dynabombs_left ?
2115             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2116             player->inventory_size >= min_inventory_size ?
2117             player->inventory_element[inventory_pos] :
2118             EL_UNDEFINED);
2119   }
2120 }
2121
2122 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2123 {
2124   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2125   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2126   int compare_result;
2127
2128   if (gpo1->sort_priority != gpo2->sort_priority)
2129     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2130   else
2131     compare_result = gpo1->nr - gpo2->nr;
2132
2133   return compare_result;
2134 }
2135
2136 int getPlayerInventorySize(int player_nr)
2137 {
2138   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2139     return game_em.ply[player_nr]->dynamite;
2140   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2141     return game_sp.red_disk_count;
2142   else
2143     return stored_player[player_nr].inventory_size;
2144 }
2145
2146 static void InitGameControlValues(void)
2147 {
2148   int i;
2149
2150   for (i = 0; game_panel_controls[i].nr != -1; i++)
2151   {
2152     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2153     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2154     struct TextPosInfo *pos = gpc->pos;
2155     int nr = gpc->nr;
2156     int type = gpc->type;
2157
2158     if (nr != i)
2159     {
2160       Error("'game_panel_controls' structure corrupted at %d", i);
2161
2162       Fail("this should not happen -- please debug");
2163     }
2164
2165     // force update of game controls after initialization
2166     gpc->value = gpc->last_value = -1;
2167     gpc->frame = gpc->last_frame = -1;
2168     gpc->gfx_frame = -1;
2169
2170     // determine panel value width for later calculation of alignment
2171     if (type == TYPE_INTEGER || type == TYPE_STRING)
2172     {
2173       pos->width = pos->size * getFontWidth(pos->font);
2174       pos->height = getFontHeight(pos->font);
2175     }
2176     else if (type == TYPE_ELEMENT)
2177     {
2178       pos->width = pos->size;
2179       pos->height = pos->size;
2180     }
2181
2182     // fill structure for game panel draw order
2183     gpo->nr = gpc->nr;
2184     gpo->sort_priority = pos->sort_priority;
2185   }
2186
2187   // sort game panel controls according to sort_priority and control number
2188   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2189         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2190 }
2191
2192 static void UpdatePlayfieldElementCount(void)
2193 {
2194   boolean use_element_count = FALSE;
2195   int i, j, x, y;
2196
2197   // first check if it is needed at all to calculate playfield element count
2198   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2199     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2200       use_element_count = TRUE;
2201
2202   if (!use_element_count)
2203     return;
2204
2205   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2206     element_info[i].element_count = 0;
2207
2208   SCAN_PLAYFIELD(x, y)
2209   {
2210     element_info[Tile[x][y]].element_count++;
2211   }
2212
2213   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2214     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2215       if (IS_IN_GROUP(j, i))
2216         element_info[EL_GROUP_START + i].element_count +=
2217           element_info[j].element_count;
2218 }
2219
2220 static void UpdateGameControlValues(void)
2221 {
2222   int i, k;
2223   int time = (game.LevelSolved ?
2224               game.LevelSolved_CountingTime :
2225               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226               game_em.lev->time :
2227               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2228               game_sp.time_played :
2229               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2230               game_mm.energy_left :
2231               game.no_time_limit ? TimePlayed : TimeLeft);
2232   int score = (game.LevelSolved ?
2233                game.LevelSolved_CountingScore :
2234                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2235                game_em.lev->score :
2236                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2237                game_sp.score :
2238                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2239                game_mm.score :
2240                game.score);
2241   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2242               game_em.lev->gems_needed :
2243               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2244               game_sp.infotrons_still_needed :
2245               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2246               game_mm.kettles_still_needed :
2247               game.gems_still_needed);
2248   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2249                      game_em.lev->gems_needed > 0 :
2250                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2251                      game_sp.infotrons_still_needed > 0 :
2252                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2253                      game_mm.kettles_still_needed > 0 ||
2254                      game_mm.lights_still_needed > 0 :
2255                      game.gems_still_needed > 0 ||
2256                      game.sokoban_fields_still_needed > 0 ||
2257                      game.sokoban_objects_still_needed > 0 ||
2258                      game.lights_still_needed > 0);
2259   int health = (game.LevelSolved ?
2260                 game.LevelSolved_CountingHealth :
2261                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2262                 MM_HEALTH(game_mm.laser_overload_value) :
2263                 game.health);
2264   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2265
2266   UpdatePlayfieldElementCount();
2267
2268   // update game panel control values
2269
2270   // used instead of "level_nr" (for network games)
2271   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2272   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2273
2274   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2275   for (i = 0; i < MAX_NUM_KEYS; i++)
2276     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2277   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2278   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2279
2280   if (game.centered_player_nr == -1)
2281   {
2282     for (i = 0; i < MAX_PLAYERS; i++)
2283     {
2284       // only one player in Supaplex game engine
2285       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2286         break;
2287
2288       for (k = 0; k < MAX_NUM_KEYS; k++)
2289       {
2290         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291         {
2292           if (game_em.ply[i]->keys & (1 << k))
2293             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294               get_key_element_from_nr(k);
2295         }
2296         else if (stored_player[i].key[k])
2297           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298             get_key_element_from_nr(k);
2299       }
2300
2301       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302         getPlayerInventorySize(i);
2303
2304       if (stored_player[i].num_white_keys > 0)
2305         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2306           EL_DC_KEY_WHITE;
2307
2308       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2309         stored_player[i].num_white_keys;
2310     }
2311   }
2312   else
2313   {
2314     int player_nr = game.centered_player_nr;
2315
2316     for (k = 0; k < MAX_NUM_KEYS; k++)
2317     {
2318       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2319       {
2320         if (game_em.ply[player_nr]->keys & (1 << k))
2321           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2322             get_key_element_from_nr(k);
2323       }
2324       else if (stored_player[player_nr].key[k])
2325         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2326           get_key_element_from_nr(k);
2327     }
2328
2329     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330       getPlayerInventorySize(player_nr);
2331
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338
2339   // re-arrange keys on game panel, if needed or if defined by style settings
2340   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2341   {
2342     int nr = GAME_PANEL_KEY_1 + i;
2343     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2344     struct TextPosInfo *pos = gpc->pos;
2345
2346     // skip check if key is not in the player's inventory
2347     if (gpc->value == EL_EMPTY)
2348       continue;
2349
2350     // check if keys should be arranged on panel from left to right
2351     if (pos->style == STYLE_LEFTMOST_POSITION)
2352     {
2353       // check previous key positions (left from current key)
2354       for (k = 0; k < i; k++)
2355       {
2356         int nr_new = GAME_PANEL_KEY_1 + k;
2357
2358         if (game_panel_controls[nr_new].value == EL_EMPTY)
2359         {
2360           game_panel_controls[nr_new].value = gpc->value;
2361           gpc->value = EL_EMPTY;
2362
2363           break;
2364         }
2365       }
2366     }
2367
2368     // check if "undefined" keys can be placed at some other position
2369     if (pos->x == -1 && pos->y == -1)
2370     {
2371       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2372
2373       // 1st try: display key at the same position as normal or EM keys
2374       if (game_panel_controls[nr_new].value == EL_EMPTY)
2375       {
2376         game_panel_controls[nr_new].value = gpc->value;
2377       }
2378       else
2379       {
2380         // 2nd try: display key at the next free position in the key panel
2381         for (k = 0; k < STD_NUM_KEYS; k++)
2382         {
2383           nr_new = GAME_PANEL_KEY_1 + k;
2384
2385           if (game_panel_controls[nr_new].value == EL_EMPTY)
2386           {
2387             game_panel_controls[nr_new].value = gpc->value;
2388
2389             break;
2390           }
2391         }
2392       }
2393     }
2394   }
2395
2396   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2397   {
2398     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2399       get_inventory_element_from_pos(local_player, i);
2400     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2401       get_inventory_element_from_pos(local_player, -i - 1);
2402   }
2403
2404   game_panel_controls[GAME_PANEL_SCORE].value = score;
2405   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2406
2407   game_panel_controls[GAME_PANEL_TIME].value = time;
2408
2409   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2410   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2411   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2412
2413   if (level.time == 0)
2414     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2415   else
2416     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2417
2418   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2419   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2420
2421   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2422
2423   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2424     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2425      EL_EMPTY);
2426   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2427     local_player->shield_normal_time_left;
2428   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2429     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2430      EL_EMPTY);
2431   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2432     local_player->shield_deadly_time_left;
2433
2434   game_panel_controls[GAME_PANEL_EXIT].value =
2435     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2436
2437   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2438     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2439   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2440     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2441      EL_EMC_MAGIC_BALL_SWITCH);
2442
2443   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2444     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2445   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2446     game.light_time_left;
2447
2448   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2449     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2450   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2451     game.timegate_time_left;
2452
2453   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2454     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2455
2456   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2457     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2458   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2459     game.lenses_time_left;
2460
2461   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2462     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2463   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2464     game.magnify_time_left;
2465
2466   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2467     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2468      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2469      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2470      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2471      EL_BALLOON_SWITCH_NONE);
2472
2473   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2474     local_player->dynabomb_count;
2475   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2476     local_player->dynabomb_size;
2477   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2478     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2479
2480   game_panel_controls[GAME_PANEL_PENGUINS].value =
2481     game.friends_still_needed;
2482
2483   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2484     game.sokoban_objects_still_needed;
2485   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2486     game.sokoban_fields_still_needed;
2487
2488   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2489     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2490
2491   for (i = 0; i < NUM_BELTS; i++)
2492   {
2493     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2494       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2495        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2496     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2497       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2498   }
2499
2500   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2501     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2502   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2503     game.magic_wall_time_left;
2504
2505   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2506     local_player->gravity;
2507
2508   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2509     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2510
2511   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2512     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2513       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2514        game.panel.element[i].id : EL_UNDEFINED);
2515
2516   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2517     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2518       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2519        element_info[game.panel.element_count[i].id].element_count : 0);
2520
2521   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2522     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2523       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2524        element_info[game.panel.ce_score[i].id].collect_score : 0);
2525
2526   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2527     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2528       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2529        element_info[game.panel.ce_score_element[i].id].collect_score :
2530        EL_UNDEFINED);
2531
2532   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2533   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2534   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2535
2536   // update game panel control frames
2537
2538   for (i = 0; game_panel_controls[i].nr != -1; i++)
2539   {
2540     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2541
2542     if (gpc->type == TYPE_ELEMENT)
2543     {
2544       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2545       {
2546         int last_anim_random_frame = gfx.anim_random_frame;
2547         int element = gpc->value;
2548         int graphic = el2panelimg(element);
2549         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2550                                sync_random_frame : INIT_GFX_RANDOM());
2551
2552         if (gpc->value != gpc->last_value)
2553         {
2554           gpc->gfx_frame = 0;
2555           gpc->gfx_random = init_gfx_random;
2556         }
2557         else
2558         {
2559           gpc->gfx_frame++;
2560
2561           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2562               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2563             gpc->gfx_random = init_gfx_random;
2564         }
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = gpc->gfx_random;
2568
2569         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2570           gpc->gfx_frame = element_info[element].collect_score;
2571
2572         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2573
2574         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2575           gfx.anim_random_frame = last_anim_random_frame;
2576       }
2577     }
2578     else if (gpc->type == TYPE_GRAPHIC)
2579     {
2580       if (gpc->graphic != IMG_UNDEFINED)
2581       {
2582         int last_anim_random_frame = gfx.anim_random_frame;
2583         int graphic = gpc->graphic;
2584         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2585                                sync_random_frame : INIT_GFX_RANDOM());
2586
2587         if (gpc->value != gpc->last_value)
2588         {
2589           gpc->gfx_frame = 0;
2590           gpc->gfx_random = init_gfx_random;
2591         }
2592         else
2593         {
2594           gpc->gfx_frame++;
2595
2596           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2597               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2598             gpc->gfx_random = init_gfx_random;
2599         }
2600
2601         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2602           gfx.anim_random_frame = gpc->gfx_random;
2603
2604         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2605
2606         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2607           gfx.anim_random_frame = last_anim_random_frame;
2608       }
2609     }
2610   }
2611 }
2612
2613 static void DisplayGameControlValues(void)
2614 {
2615   boolean redraw_panel = FALSE;
2616   int i;
2617
2618   for (i = 0; game_panel_controls[i].nr != -1; i++)
2619   {
2620     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2621
2622     if (PANEL_DEACTIVATED(gpc->pos))
2623       continue;
2624
2625     if (gpc->value == gpc->last_value &&
2626         gpc->frame == gpc->last_frame)
2627       continue;
2628
2629     redraw_panel = TRUE;
2630   }
2631
2632   if (!redraw_panel)
2633     return;
2634
2635   // copy default game door content to main double buffer
2636
2637   // !!! CHECK AGAIN !!!
2638   SetPanelBackground();
2639   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2640   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2641
2642   // redraw game control buttons
2643   RedrawGameButtons();
2644
2645   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2646
2647   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2648   {
2649     int nr = game_panel_order[i].nr;
2650     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2651     struct TextPosInfo *pos = gpc->pos;
2652     int type = gpc->type;
2653     int value = gpc->value;
2654     int frame = gpc->frame;
2655     int size = pos->size;
2656     int font = pos->font;
2657     boolean draw_masked = pos->draw_masked;
2658     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2659
2660     if (PANEL_DEACTIVATED(pos))
2661       continue;
2662
2663     if (pos->class == get_hash_from_key("extra_panel_items") &&
2664         !setup.prefer_extra_panel_items)
2665       continue;
2666
2667     gpc->last_value = value;
2668     gpc->last_frame = frame;
2669
2670     if (type == TYPE_INTEGER)
2671     {
2672       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2673           nr == GAME_PANEL_TIME)
2674       {
2675         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2676
2677         if (use_dynamic_size)           // use dynamic number of digits
2678         {
2679           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2680           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2681           int size2 = size1 + 1;
2682           int font1 = pos->font;
2683           int font2 = pos->font_alt;
2684
2685           size = (value < value_change ? size1 : size2);
2686           font = (value < value_change ? font1 : font2);
2687         }
2688       }
2689
2690       // correct text size if "digits" is zero or less
2691       if (size <= 0)
2692         size = strlen(int2str(value, size));
2693
2694       // dynamically correct text alignment
2695       pos->width = size * getFontWidth(font);
2696
2697       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2698                   int2str(value, size), font, mask_mode);
2699     }
2700     else if (type == TYPE_ELEMENT)
2701     {
2702       int element, graphic;
2703       Bitmap *src_bitmap;
2704       int src_x, src_y;
2705       int width, height;
2706       int dst_x = PANEL_XPOS(pos);
2707       int dst_y = PANEL_YPOS(pos);
2708
2709       if (value != EL_UNDEFINED && value != EL_EMPTY)
2710       {
2711         element = value;
2712         graphic = el2panelimg(value);
2713
2714 #if 0
2715         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2716               element, EL_NAME(element), size);
2717 #endif
2718
2719         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2720           size = TILESIZE;
2721
2722         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2723                               &src_x, &src_y);
2724
2725         width  = graphic_info[graphic].width  * size / TILESIZE;
2726         height = graphic_info[graphic].height * size / TILESIZE;
2727
2728         if (draw_masked)
2729           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2730                            dst_x, dst_y);
2731         else
2732           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2733                      dst_x, dst_y);
2734       }
2735     }
2736     else if (type == TYPE_GRAPHIC)
2737     {
2738       int graphic        = gpc->graphic;
2739       int graphic_active = gpc->graphic_active;
2740       Bitmap *src_bitmap;
2741       int src_x, src_y;
2742       int width, height;
2743       int dst_x = PANEL_XPOS(pos);
2744       int dst_y = PANEL_YPOS(pos);
2745       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2746                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2747
2748       if (graphic != IMG_UNDEFINED && !skip)
2749       {
2750         if (pos->style == STYLE_REVERSE)
2751           value = 100 - value;
2752
2753         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2754
2755         if (pos->direction & MV_HORIZONTAL)
2756         {
2757           width  = graphic_info[graphic_active].width * value / 100;
2758           height = graphic_info[graphic_active].height;
2759
2760           if (pos->direction == MV_LEFT)
2761           {
2762             src_x += graphic_info[graphic_active].width - width;
2763             dst_x += graphic_info[graphic_active].width - width;
2764           }
2765         }
2766         else
2767         {
2768           width  = graphic_info[graphic_active].width;
2769           height = graphic_info[graphic_active].height * value / 100;
2770
2771           if (pos->direction == MV_UP)
2772           {
2773             src_y += graphic_info[graphic_active].height - height;
2774             dst_y += graphic_info[graphic_active].height - height;
2775           }
2776         }
2777
2778         if (draw_masked)
2779           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2780                            dst_x, dst_y);
2781         else
2782           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2783                      dst_x, dst_y);
2784
2785         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2786
2787         if (pos->direction & MV_HORIZONTAL)
2788         {
2789           if (pos->direction == MV_RIGHT)
2790           {
2791             src_x += width;
2792             dst_x += width;
2793           }
2794           else
2795           {
2796             dst_x = PANEL_XPOS(pos);
2797           }
2798
2799           width = graphic_info[graphic].width - width;
2800         }
2801         else
2802         {
2803           if (pos->direction == MV_DOWN)
2804           {
2805             src_y += height;
2806             dst_y += height;
2807           }
2808           else
2809           {
2810             dst_y = PANEL_YPOS(pos);
2811           }
2812
2813           height = graphic_info[graphic].height - height;
2814         }
2815
2816         if (draw_masked)
2817           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2818                            dst_x, dst_y);
2819         else
2820           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2821                      dst_x, dst_y);
2822       }
2823     }
2824     else if (type == TYPE_STRING)
2825     {
2826       boolean active = (value != 0);
2827       char *state_normal = "off";
2828       char *state_active = "on";
2829       char *state = (active ? state_active : state_normal);
2830       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2831                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2832                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2833                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2834
2835       if (nr == GAME_PANEL_GRAVITY_STATE)
2836       {
2837         int font1 = pos->font;          // (used for normal state)
2838         int font2 = pos->font_alt;      // (used for active state)
2839
2840         font = (active ? font2 : font1);
2841       }
2842
2843       if (s != NULL)
2844       {
2845         char *s_cut;
2846
2847         if (size <= 0)
2848         {
2849           // don't truncate output if "chars" is zero or less
2850           size = strlen(s);
2851
2852           // dynamically correct text alignment
2853           pos->width = size * getFontWidth(font);
2854         }
2855
2856         s_cut = getStringCopyN(s, size);
2857
2858         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2859                     s_cut, font, mask_mode);
2860
2861         free(s_cut);
2862       }
2863     }
2864
2865     redraw_mask |= REDRAW_DOOR_1;
2866   }
2867
2868   SetGameStatus(GAME_MODE_PLAYING);
2869 }
2870
2871 void UpdateAndDisplayGameControlValues(void)
2872 {
2873   if (tape.deactivate_display)
2874     return;
2875
2876   UpdateGameControlValues();
2877   DisplayGameControlValues();
2878 }
2879
2880 void UpdateGameDoorValues(void)
2881 {
2882   UpdateGameControlValues();
2883 }
2884
2885 void DrawGameDoorValues(void)
2886 {
2887   DisplayGameControlValues();
2888 }
2889
2890
2891 // ============================================================================
2892 // InitGameEngine()
2893 // ----------------------------------------------------------------------------
2894 // initialize game engine due to level / tape version number
2895 // ============================================================================
2896
2897 static void InitGameEngine(void)
2898 {
2899   int i, j, k, l, x, y;
2900
2901   // set game engine from tape file when re-playing, else from level file
2902   game.engine_version = (tape.playing ? tape.engine_version :
2903                          level.game_version);
2904
2905   // set single or multi-player game mode (needed for re-playing tapes)
2906   game.team_mode = setup.team_mode;
2907
2908   if (tape.playing)
2909   {
2910     int num_players = 0;
2911
2912     for (i = 0; i < MAX_PLAYERS; i++)
2913       if (tape.player_participates[i])
2914         num_players++;
2915
2916     // multi-player tapes contain input data for more than one player
2917     game.team_mode = (num_players > 1);
2918   }
2919
2920 #if 0
2921   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2922         level.game_version);
2923   Debug("game:init:level", "          tape.file_version   == %06d",
2924         tape.file_version);
2925   Debug("game:init:level", "          tape.game_version   == %06d",
2926         tape.game_version);
2927   Debug("game:init:level", "          tape.engine_version == %06d",
2928         tape.engine_version);
2929   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2930         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2931 #endif
2932
2933   // --------------------------------------------------------------------------
2934   // set flags for bugs and changes according to active game engine version
2935   // --------------------------------------------------------------------------
2936
2937   /*
2938     Summary of bugfix:
2939     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2940
2941     Bug was introduced in version:
2942     2.0.1
2943
2944     Bug was fixed in version:
2945     4.2.0.0
2946
2947     Description:
2948     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2949     but the property "can fall" was missing, which caused some levels to be
2950     unsolvable. This was fixed in version 4.2.0.0.
2951
2952     Affected levels/tapes:
2953     An example for a tape that was fixed by this bugfix is tape 029 from the
2954     level set "rnd_sam_bateman".
2955     The wrong behaviour will still be used for all levels or tapes that were
2956     created/recorded with it. An example for this is tape 023 from the level
2957     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2958   */
2959
2960   boolean use_amoeba_dropping_cannot_fall_bug =
2961     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2962       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2963      (tape.playing &&
2964       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2965       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2966
2967   /*
2968     Summary of bugfix/change:
2969     Fixed move speed of elements entering or leaving magic wall.
2970
2971     Fixed/changed in version:
2972     2.0.1
2973
2974     Description:
2975     Before 2.0.1, move speed of elements entering or leaving magic wall was
2976     twice as fast as it is now.
2977     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2978
2979     Affected levels/tapes:
2980     The first condition is generally needed for all levels/tapes before version
2981     2.0.1, which might use the old behaviour before it was changed; known tapes
2982     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2983     The second condition is an exception from the above case and is needed for
2984     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2985     above, but before it was known that this change would break tapes like the
2986     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2987     although the engine version while recording maybe was before 2.0.1. There
2988     are a lot of tapes that are affected by this exception, like tape 006 from
2989     the level set "rnd_conor_mancone".
2990   */
2991
2992   boolean use_old_move_stepsize_for_magic_wall =
2993     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2994      !(tape.playing &&
2995        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2996        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2997
2998   /*
2999     Summary of bugfix/change:
3000     Fixed handling for custom elements that change when pushed by the player.
3001
3002     Fixed/changed in version:
3003     3.1.0
3004
3005     Description:
3006     Before 3.1.0, custom elements that "change when pushing" changed directly
3007     after the player started pushing them (until then handled in "DigField()").
3008     Since 3.1.0, these custom elements are not changed until the "pushing"
3009     move of the element is finished (now handled in "ContinueMoving()").
3010
3011     Affected levels/tapes:
3012     The first condition is generally needed for all levels/tapes before version
3013     3.1.0, which might use the old behaviour before it was changed; known tapes
3014     that are affected are some tapes from the level set "Walpurgis Gardens" by
3015     Jamie Cullen.
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3018     above (including some development versions of 3.1.0), but before it was
3019     known that this change would break tapes like the above and was fixed in
3020     3.1.1, so that the changed behaviour was active although the engine version
3021     while recording maybe was before 3.1.0. There is at least one tape that is
3022     affected by this exception, which is the tape for the one-level set "Bug
3023     Machine" by Juergen Bonhagen.
3024   */
3025
3026   game.use_change_when_pushing_bug =
3027     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3028      !(tape.playing &&
3029        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3030        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3031
3032   /*
3033     Summary of bugfix/change:
3034     Fixed handling for blocking the field the player leaves when moving.
3035
3036     Fixed/changed in version:
3037     3.1.1
3038
3039     Description:
3040     Before 3.1.1, when "block last field when moving" was enabled, the field
3041     the player is leaving when moving was blocked for the time of the move,
3042     and was directly unblocked afterwards. This resulted in the last field
3043     being blocked for exactly one less than the number of frames of one player
3044     move. Additionally, even when blocking was disabled, the last field was
3045     blocked for exactly one frame.
3046     Since 3.1.1, due to changes in player movement handling, the last field
3047     is not blocked at all when blocking is disabled. When blocking is enabled,
3048     the last field is blocked for exactly the number of frames of one player
3049     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3050     last field is blocked for exactly one more than the number of frames of
3051     one player move.
3052
3053     Affected levels/tapes:
3054     (!!! yet to be determined -- probably many !!!)
3055   */
3056
3057   game.use_block_last_field_bug =
3058     (game.engine_version < VERSION_IDENT(3,1,1,0));
3059
3060   /* various special flags and settings for native Emerald Mine game engine */
3061
3062   game_em.use_single_button =
3063     (game.engine_version > VERSION_IDENT(4,0,0,2));
3064
3065   game_em.use_snap_key_bug =
3066     (game.engine_version < VERSION_IDENT(4,0,1,0));
3067
3068   game_em.use_random_bug =
3069     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3070
3071   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3072
3073   game_em.use_old_explosions            = use_old_em_engine;
3074   game_em.use_old_android               = use_old_em_engine;
3075   game_em.use_old_push_elements         = use_old_em_engine;
3076   game_em.use_old_push_into_acid        = use_old_em_engine;
3077
3078   game_em.use_wrap_around               = !use_old_em_engine;
3079
3080   // --------------------------------------------------------------------------
3081
3082   // set maximal allowed number of custom element changes per game frame
3083   game.max_num_changes_per_frame = 1;
3084
3085   // default scan direction: scan playfield from top/left to bottom/right
3086   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3087
3088   // dynamically adjust element properties according to game engine version
3089   InitElementPropertiesEngine(game.engine_version);
3090
3091   // ---------- initialize special element properties -------------------------
3092
3093   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3094   if (use_amoeba_dropping_cannot_fall_bug)
3095     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3096
3097   // ---------- initialize player's initial move delay ------------------------
3098
3099   // dynamically adjust player properties according to level information
3100   for (i = 0; i < MAX_PLAYERS; i++)
3101     game.initial_move_delay_value[i] =
3102       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3103
3104   // dynamically adjust player properties according to game engine version
3105   for (i = 0; i < MAX_PLAYERS; i++)
3106     game.initial_move_delay[i] =
3107       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3108        game.initial_move_delay_value[i] : 0);
3109
3110   // ---------- initialize player's initial push delay ------------------------
3111
3112   // dynamically adjust player properties according to game engine version
3113   game.initial_push_delay_value =
3114     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3115
3116   // ---------- initialize changing elements ----------------------------------
3117
3118   // initialize changing elements information
3119   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3120   {
3121     struct ElementInfo *ei = &element_info[i];
3122
3123     // this pointer might have been changed in the level editor
3124     ei->change = &ei->change_page[0];
3125
3126     if (!IS_CUSTOM_ELEMENT(i))
3127     {
3128       ei->change->target_element = EL_EMPTY_SPACE;
3129       ei->change->delay_fixed = 0;
3130       ei->change->delay_random = 0;
3131       ei->change->delay_frames = 1;
3132     }
3133
3134     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3135     {
3136       ei->has_change_event[j] = FALSE;
3137
3138       ei->event_page_nr[j] = 0;
3139       ei->event_page[j] = &ei->change_page[0];
3140     }
3141   }
3142
3143   // add changing elements from pre-defined list
3144   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3145   {
3146     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3147     struct ElementInfo *ei = &element_info[ch_delay->element];
3148
3149     ei->change->target_element       = ch_delay->target_element;
3150     ei->change->delay_fixed          = ch_delay->change_delay;
3151
3152     ei->change->pre_change_function  = ch_delay->pre_change_function;
3153     ei->change->change_function      = ch_delay->change_function;
3154     ei->change->post_change_function = ch_delay->post_change_function;
3155
3156     ei->change->can_change = TRUE;
3157     ei->change->can_change_or_has_action = TRUE;
3158
3159     ei->has_change_event[CE_DELAY] = TRUE;
3160
3161     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3162     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3163   }
3164
3165   // ---------- initialize internal run-time variables ------------------------
3166
3167   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3168   {
3169     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3170
3171     for (j = 0; j < ei->num_change_pages; j++)
3172     {
3173       ei->change_page[j].can_change_or_has_action =
3174         (ei->change_page[j].can_change |
3175          ei->change_page[j].has_action);
3176     }
3177   }
3178
3179   // add change events from custom element configuration
3180   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3181   {
3182     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3183
3184     for (j = 0; j < ei->num_change_pages; j++)
3185     {
3186       if (!ei->change_page[j].can_change_or_has_action)
3187         continue;
3188
3189       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3190       {
3191         // only add event page for the first page found with this event
3192         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3193         {
3194           ei->has_change_event[k] = TRUE;
3195
3196           ei->event_page_nr[k] = j;
3197           ei->event_page[k] = &ei->change_page[j];
3198         }
3199       }
3200     }
3201   }
3202
3203   // ---------- initialize reference elements in change conditions ------------
3204
3205   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3206   {
3207     int element = EL_CUSTOM_START + i;
3208     struct ElementInfo *ei = &element_info[element];
3209
3210     for (j = 0; j < ei->num_change_pages; j++)
3211     {
3212       int trigger_element = ei->change_page[j].initial_trigger_element;
3213
3214       if (trigger_element >= EL_PREV_CE_8 &&
3215           trigger_element <= EL_NEXT_CE_8)
3216         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3217
3218       ei->change_page[j].trigger_element = trigger_element;
3219     }
3220   }
3221
3222   // ---------- initialize run-time trigger player and element ----------------
3223
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3231       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3232       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3233       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3234       ei->change_page[j].actual_trigger_ce_value = 0;
3235       ei->change_page[j].actual_trigger_ce_score = 0;
3236     }
3237   }
3238
3239   // ---------- initialize trigger events -------------------------------------
3240
3241   // initialize trigger events information
3242   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3243     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3244       trigger_events[i][j] = FALSE;
3245
3246   // add trigger events from element change event properties
3247   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3248   {
3249     struct ElementInfo *ei = &element_info[i];
3250
3251     for (j = 0; j < ei->num_change_pages; j++)
3252     {
3253       if (!ei->change_page[j].can_change_or_has_action)
3254         continue;
3255
3256       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3257       {
3258         int trigger_element = ei->change_page[j].trigger_element;
3259
3260         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3261         {
3262           if (ei->change_page[j].has_event[k])
3263           {
3264             if (IS_GROUP_ELEMENT(trigger_element))
3265             {
3266               struct ElementGroupInfo *group =
3267                 element_info[trigger_element].group;
3268
3269               for (l = 0; l < group->num_elements_resolved; l++)
3270                 trigger_events[group->element_resolved[l]][k] = TRUE;
3271             }
3272             else if (trigger_element == EL_ANY_ELEMENT)
3273               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3274                 trigger_events[l][k] = TRUE;
3275             else
3276               trigger_events[trigger_element][k] = TRUE;
3277           }
3278         }
3279       }
3280     }
3281   }
3282
3283   // ---------- initialize push delay -----------------------------------------
3284
3285   // initialize push delay values to default
3286   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3287   {
3288     if (!IS_CUSTOM_ELEMENT(i))
3289     {
3290       // set default push delay values (corrected since version 3.0.7-1)
3291       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3292       {
3293         element_info[i].push_delay_fixed = 2;
3294         element_info[i].push_delay_random = 8;
3295       }
3296       else
3297       {
3298         element_info[i].push_delay_fixed = 8;
3299         element_info[i].push_delay_random = 8;
3300       }
3301     }
3302   }
3303
3304   // set push delay value for certain elements from pre-defined list
3305   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3306   {
3307     int e = push_delay_list[i].element;
3308
3309     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3310     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3311   }
3312
3313   // set push delay value for Supaplex elements for newer engine versions
3314   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3315   {
3316     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3317     {
3318       if (IS_SP_ELEMENT(i))
3319       {
3320         // set SP push delay to just enough to push under a falling zonk
3321         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3322
3323         element_info[i].push_delay_fixed  = delay;
3324         element_info[i].push_delay_random = 0;
3325       }
3326     }
3327   }
3328
3329   // ---------- initialize move stepsize --------------------------------------
3330
3331   // initialize move stepsize values to default
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333     if (!IS_CUSTOM_ELEMENT(i))
3334       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3335
3336   // set move stepsize value for certain elements from pre-defined list
3337   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3338   {
3339     int e = move_stepsize_list[i].element;
3340
3341     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3342
3343     // set move stepsize value for certain elements for older engine versions
3344     if (use_old_move_stepsize_for_magic_wall)
3345     {
3346       if (e == EL_MAGIC_WALL_FILLING ||
3347           e == EL_MAGIC_WALL_EMPTYING ||
3348           e == EL_BD_MAGIC_WALL_FILLING ||
3349           e == EL_BD_MAGIC_WALL_EMPTYING)
3350         element_info[e].move_stepsize *= 2;
3351     }
3352   }
3353
3354   // ---------- initialize collect score --------------------------------------
3355
3356   // initialize collect score values for custom elements from initial value
3357   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3358     if (IS_CUSTOM_ELEMENT(i))
3359       element_info[i].collect_score = element_info[i].collect_score_initial;
3360
3361   // ---------- initialize collect count --------------------------------------
3362
3363   // initialize collect count values for non-custom elements
3364   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     if (!IS_CUSTOM_ELEMENT(i))
3366       element_info[i].collect_count_initial = 0;
3367
3368   // add collect count values for all elements from pre-defined list
3369   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3370     element_info[collect_count_list[i].element].collect_count_initial =
3371       collect_count_list[i].count;
3372
3373   // ---------- initialize access direction -----------------------------------
3374
3375   // initialize access direction values to default (access from every side)
3376   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3377     if (!IS_CUSTOM_ELEMENT(i))
3378       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3379
3380   // set access direction value for certain elements from pre-defined list
3381   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3382     element_info[access_direction_list[i].element].access_direction =
3383       access_direction_list[i].direction;
3384
3385   // ---------- initialize explosion content ----------------------------------
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387   {
3388     if (IS_CUSTOM_ELEMENT(i))
3389       continue;
3390
3391     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3392     {
3393       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3394
3395       element_info[i].content.e[x][y] =
3396         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3397          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3398          i == EL_PLAYER_3 ? EL_EMERALD :
3399          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3400          i == EL_MOLE ? EL_EMERALD_RED :
3401          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3402          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3403          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3404          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3405          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3406          i == EL_WALL_EMERALD ? EL_EMERALD :
3407          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3408          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3409          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3410          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3411          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3412          i == EL_WALL_PEARL ? EL_PEARL :
3413          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3414          EL_EMPTY);
3415     }
3416   }
3417
3418   // ---------- initialize recursion detection --------------------------------
3419   recursion_loop_depth = 0;
3420   recursion_loop_detected = FALSE;
3421   recursion_loop_element = EL_UNDEFINED;
3422
3423   // ---------- initialize graphics engine ------------------------------------
3424   game.scroll_delay_value =
3425     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3426      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3427      !setup.forced_scroll_delay           ? 0 :
3428      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3429   game.scroll_delay_value =
3430     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3431
3432   // ---------- initialize game engine snapshots ------------------------------
3433   for (i = 0; i < MAX_PLAYERS; i++)
3434     game.snapshot.last_action[i] = 0;
3435   game.snapshot.changed_action = FALSE;
3436   game.snapshot.collected_item = FALSE;
3437   game.snapshot.mode =
3438     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3439      SNAPSHOT_MODE_EVERY_STEP :
3440      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3441      SNAPSHOT_MODE_EVERY_MOVE :
3442      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3443      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3444   game.snapshot.save_snapshot = FALSE;
3445
3446   // ---------- initialize level time for Supaplex engine ---------------------
3447   // Supaplex levels with time limit currently unsupported -- should be added
3448   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3449     level.time = 0;
3450
3451   // ---------- initialize flags for handling game actions --------------------
3452
3453   // set flags for game actions to default values
3454   game.use_key_actions = TRUE;
3455   game.use_mouse_actions = FALSE;
3456
3457   // when using Mirror Magic game engine, handle mouse events only
3458   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3459   {
3460     game.use_key_actions = FALSE;
3461     game.use_mouse_actions = TRUE;
3462   }
3463
3464   // check for custom elements with mouse click events
3465   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3466   {
3467     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3468     {
3469       int element = EL_CUSTOM_START + i;
3470
3471       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3472           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3473           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3474           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3475         game.use_mouse_actions = TRUE;
3476     }
3477   }
3478 }
3479
3480 static int get_num_special_action(int element, int action_first,
3481                                   int action_last)
3482 {
3483   int num_special_action = 0;
3484   int i, j;
3485
3486   for (i = action_first; i <= action_last; i++)
3487   {
3488     boolean found = FALSE;
3489
3490     for (j = 0; j < NUM_DIRECTIONS; j++)
3491       if (el_act_dir2img(element, i, j) !=
3492           el_act_dir2img(element, ACTION_DEFAULT, j))
3493         found = TRUE;
3494
3495     if (found)
3496       num_special_action++;
3497     else
3498       break;
3499   }
3500
3501   return num_special_action;
3502 }
3503
3504
3505 // ============================================================================
3506 // InitGame()
3507 // ----------------------------------------------------------------------------
3508 // initialize and start new game
3509 // ============================================================================
3510
3511 #if DEBUG_INIT_PLAYER
3512 static void DebugPrintPlayerStatus(char *message)
3513 {
3514   int i;
3515
3516   if (!options.debug)
3517     return;
3518
3519   Debug("game:init:player", "%s:", message);
3520
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     Debug("game:init:player",
3526           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3527           i + 1,
3528           player->present,
3529           player->connected,
3530           player->connected_locally,
3531           player->connected_network,
3532           player->active,
3533           (local_player == player ? " (local player)" : ""));
3534   }
3535 }
3536 #endif
3537
3538 void InitGame(void)
3539 {
3540   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3541   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3542   int fade_mask = REDRAW_FIELD;
3543
3544   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3545   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3546   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3547   int initial_move_dir = MV_DOWN;
3548   int i, j, x, y;
3549
3550   // required here to update video display before fading (FIX THIS)
3551   DrawMaskedBorder(REDRAW_DOOR_2);
3552
3553   if (!game.restart_level)
3554     CloseDoor(DOOR_CLOSE_1);
3555
3556   SetGameStatus(GAME_MODE_PLAYING);
3557
3558   if (level_editor_test_game)
3559     FadeSkipNextFadeOut();
3560   else
3561     FadeSetEnterScreen();
3562
3563   if (CheckFadeAll())
3564     fade_mask = REDRAW_ALL;
3565
3566   FadeLevelSoundsAndMusic();
3567
3568   ExpireSoundLoops(TRUE);
3569
3570   FadeOut(fade_mask);
3571
3572   if (level_editor_test_game)
3573     FadeSkipNextFadeIn();
3574
3575   // needed if different viewport properties defined for playing
3576   ChangeViewportPropertiesIfNeeded();
3577
3578   ClearField();
3579
3580   DrawCompleteVideoDisplay();
3581
3582   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3583
3584   InitGameEngine();
3585   InitGameControlValues();
3586
3587   if (tape.recording)
3588   {
3589     // initialize tape actions from game when recording tape
3590     tape.use_key_actions   = game.use_key_actions;
3591     tape.use_mouse_actions = game.use_mouse_actions;
3592
3593     // initialize visible playfield size when recording tape (for team mode)
3594     tape.scr_fieldx = SCR_FIELDX;
3595     tape.scr_fieldy = SCR_FIELDY;
3596   }
3597
3598   // don't play tapes over network
3599   network_playing = (network.enabled && !tape.playing);
3600
3601   for (i = 0; i < MAX_PLAYERS; i++)
3602   {
3603     struct PlayerInfo *player = &stored_player[i];
3604
3605     player->index_nr = i;
3606     player->index_bit = (1 << i);
3607     player->element_nr = EL_PLAYER_1 + i;
3608
3609     player->present = FALSE;
3610     player->active = FALSE;
3611     player->mapped = FALSE;
3612
3613     player->killed = FALSE;
3614     player->reanimated = FALSE;
3615     player->buried = FALSE;
3616
3617     player->action = 0;
3618     player->effective_action = 0;
3619     player->programmed_action = 0;
3620     player->snap_action = 0;
3621
3622     player->mouse_action.lx = 0;
3623     player->mouse_action.ly = 0;
3624     player->mouse_action.button = 0;
3625     player->mouse_action.button_hint = 0;
3626
3627     player->effective_mouse_action.lx = 0;
3628     player->effective_mouse_action.ly = 0;
3629     player->effective_mouse_action.button = 0;
3630     player->effective_mouse_action.button_hint = 0;
3631
3632     for (j = 0; j < MAX_NUM_KEYS; j++)
3633       player->key[j] = FALSE;
3634
3635     player->num_white_keys = 0;
3636
3637     player->dynabomb_count = 0;
3638     player->dynabomb_size = 1;
3639     player->dynabombs_left = 0;
3640     player->dynabomb_xl = FALSE;
3641
3642     player->MovDir = initial_move_dir;
3643     player->MovPos = 0;
3644     player->GfxPos = 0;
3645     player->GfxDir = initial_move_dir;
3646     player->GfxAction = ACTION_DEFAULT;
3647     player->Frame = 0;
3648     player->StepFrame = 0;
3649
3650     player->initial_element = player->element_nr;
3651     player->artwork_element =
3652       (level.use_artwork_element[i] ? level.artwork_element[i] :
3653        player->element_nr);
3654     player->use_murphy = FALSE;
3655
3656     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3657     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3658
3659     player->gravity = level.initial_player_gravity[i];
3660
3661     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3662
3663     player->actual_frame_counter = 0;
3664
3665     player->step_counter = 0;
3666
3667     player->last_move_dir = initial_move_dir;
3668
3669     player->is_active = FALSE;
3670
3671     player->is_waiting = FALSE;
3672     player->is_moving = FALSE;
3673     player->is_auto_moving = FALSE;
3674     player->is_digging = FALSE;
3675     player->is_snapping = FALSE;
3676     player->is_collecting = FALSE;
3677     player->is_pushing = FALSE;
3678     player->is_switching = FALSE;
3679     player->is_dropping = FALSE;
3680     player->is_dropping_pressed = FALSE;
3681
3682     player->is_bored = FALSE;
3683     player->is_sleeping = FALSE;
3684
3685     player->was_waiting = TRUE;
3686     player->was_moving = FALSE;
3687     player->was_snapping = FALSE;
3688     player->was_dropping = FALSE;
3689
3690     player->force_dropping = FALSE;
3691
3692     player->frame_counter_bored = -1;
3693     player->frame_counter_sleeping = -1;
3694
3695     player->anim_delay_counter = 0;
3696     player->post_delay_counter = 0;
3697
3698     player->dir_waiting = initial_move_dir;
3699     player->action_waiting = ACTION_DEFAULT;
3700     player->last_action_waiting = ACTION_DEFAULT;
3701     player->special_action_bored = ACTION_DEFAULT;
3702     player->special_action_sleeping = ACTION_DEFAULT;
3703
3704     player->switch_x = -1;
3705     player->switch_y = -1;
3706
3707     player->drop_x = -1;
3708     player->drop_y = -1;
3709
3710     player->show_envelope = 0;
3711
3712     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3713
3714     player->push_delay       = -1;      // initialized when pushing starts
3715     player->push_delay_value = game.initial_push_delay_value;
3716
3717     player->drop_delay = 0;
3718     player->drop_pressed_delay = 0;
3719
3720     player->last_jx = -1;
3721     player->last_jy = -1;
3722     player->jx = -1;
3723     player->jy = -1;
3724
3725     player->shield_normal_time_left = 0;
3726     player->shield_deadly_time_left = 0;
3727
3728     player->last_removed_element = EL_UNDEFINED;
3729
3730     player->inventory_infinite_element = EL_UNDEFINED;
3731     player->inventory_size = 0;
3732
3733     if (level.use_initial_inventory[i])
3734     {
3735       for (j = 0; j < level.initial_inventory_size[i]; j++)
3736       {
3737         int element = level.initial_inventory_content[i][j];
3738         int collect_count = element_info[element].collect_count_initial;
3739         int k;
3740
3741         if (!IS_CUSTOM_ELEMENT(element))
3742           collect_count = 1;
3743
3744         if (collect_count == 0)
3745           player->inventory_infinite_element = element;
3746         else
3747           for (k = 0; k < collect_count; k++)
3748             if (player->inventory_size < MAX_INVENTORY_SIZE)
3749               player->inventory_element[player->inventory_size++] = element;
3750       }
3751     }
3752
3753     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3754     SnapField(player, 0, 0);
3755
3756     map_player_action[i] = i;
3757   }
3758
3759   network_player_action_received = FALSE;
3760
3761   // initial null action
3762   if (network_playing)
3763     SendToServer_MovePlayer(MV_NONE);
3764
3765   FrameCounter = 0;
3766   TimeFrames = 0;
3767   TimePlayed = 0;
3768   TimeLeft = level.time;
3769   TapeTime = 0;
3770
3771   ScreenMovDir = MV_NONE;
3772   ScreenMovPos = 0;
3773   ScreenGfxPos = 0;
3774
3775   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3776
3777   game.robot_wheel_x = -1;
3778   game.robot_wheel_y = -1;
3779
3780   game.exit_x = -1;
3781   game.exit_y = -1;
3782
3783   game.all_players_gone = FALSE;
3784
3785   game.LevelSolved = FALSE;
3786   game.GameOver = FALSE;
3787
3788   game.GamePlayed = !tape.playing;
3789
3790   game.LevelSolved_GameWon = FALSE;
3791   game.LevelSolved_GameEnd = FALSE;
3792   game.LevelSolved_SaveTape = FALSE;
3793   game.LevelSolved_SaveScore = FALSE;
3794
3795   game.LevelSolved_CountingTime = 0;
3796   game.LevelSolved_CountingScore = 0;
3797   game.LevelSolved_CountingHealth = 0;
3798
3799   game.panel.active = TRUE;
3800
3801   game.no_time_limit = (level.time == 0);
3802
3803   game.yamyam_content_nr = 0;
3804   game.robot_wheel_active = FALSE;
3805   game.magic_wall_active = FALSE;
3806   game.magic_wall_time_left = 0;
3807   game.light_time_left = 0;
3808   game.timegate_time_left = 0;
3809   game.switchgate_pos = 0;
3810   game.wind_direction = level.wind_direction_initial;
3811
3812   game.time_final = 0;
3813   game.score_time_final = 0;
3814
3815   game.score = 0;
3816   game.score_final = 0;
3817
3818   game.health = MAX_HEALTH;
3819   game.health_final = MAX_HEALTH;
3820
3821   game.gems_still_needed = level.gems_needed;
3822   game.sokoban_fields_still_needed = 0;
3823   game.sokoban_objects_still_needed = 0;
3824   game.lights_still_needed = 0;
3825   game.players_still_needed = 0;
3826   game.friends_still_needed = 0;
3827
3828   game.lenses_time_left = 0;
3829   game.magnify_time_left = 0;
3830
3831   game.ball_active = level.ball_active_initial;
3832   game.ball_content_nr = 0;
3833
3834   game.explosions_delayed = TRUE;
3835
3836   game.envelope_active = FALSE;
3837
3838   for (i = 0; i < NUM_BELTS; i++)
3839   {
3840     game.belt_dir[i] = MV_NONE;
3841     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3842   }
3843
3844   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3845     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3846
3847 #if DEBUG_INIT_PLAYER
3848   DebugPrintPlayerStatus("Player status at level initialization");
3849 #endif
3850
3851   SCAN_PLAYFIELD(x, y)
3852   {
3853     Tile[x][y] = Last[x][y] = level.field[x][y];
3854     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3855     ChangeDelay[x][y] = 0;
3856     ChangePage[x][y] = -1;
3857     CustomValue[x][y] = 0;              // initialized in InitField()
3858     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3859     AmoebaNr[x][y] = 0;
3860     WasJustMoving[x][y] = 0;
3861     WasJustFalling[x][y] = 0;
3862     CheckCollision[x][y] = 0;
3863     CheckImpact[x][y] = 0;
3864     Stop[x][y] = FALSE;
3865     Pushed[x][y] = FALSE;
3866
3867     ChangeCount[x][y] = 0;
3868     ChangeEvent[x][y] = -1;
3869
3870     ExplodePhase[x][y] = 0;
3871     ExplodeDelay[x][y] = 0;
3872     ExplodeField[x][y] = EX_TYPE_NONE;
3873
3874     RunnerVisit[x][y] = 0;
3875     PlayerVisit[x][y] = 0;
3876
3877     GfxFrame[x][y] = 0;
3878     GfxRandom[x][y] = INIT_GFX_RANDOM();
3879     GfxElement[x][y] = EL_UNDEFINED;
3880     GfxAction[x][y] = ACTION_DEFAULT;
3881     GfxDir[x][y] = MV_NONE;
3882     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3883   }
3884
3885   SCAN_PLAYFIELD(x, y)
3886   {
3887     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3888       emulate_bd = FALSE;
3889     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3890       emulate_sb = FALSE;
3891     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3892       emulate_sp = FALSE;
3893
3894     InitField(x, y, TRUE);
3895
3896     ResetGfxAnimation(x, y);
3897   }
3898
3899   InitBeltMovement();
3900
3901   for (i = 0; i < MAX_PLAYERS; i++)
3902   {
3903     struct PlayerInfo *player = &stored_player[i];
3904
3905     // set number of special actions for bored and sleeping animation
3906     player->num_special_action_bored =
3907       get_num_special_action(player->artwork_element,
3908                              ACTION_BORING_1, ACTION_BORING_LAST);
3909     player->num_special_action_sleeping =
3910       get_num_special_action(player->artwork_element,
3911                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3912   }
3913
3914   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3915                     emulate_sb ? EMU_SOKOBAN :
3916                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3917
3918   // initialize type of slippery elements
3919   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3920   {
3921     if (!IS_CUSTOM_ELEMENT(i))
3922     {
3923       // default: elements slip down either to the left or right randomly
3924       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3925
3926       // SP style elements prefer to slip down on the left side
3927       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3928         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3929
3930       // BD style elements prefer to slip down on the left side
3931       if (game.emulation == EMU_BOULDERDASH)
3932         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3933     }
3934   }
3935
3936   // initialize explosion and ignition delay
3937   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3938   {
3939     if (!IS_CUSTOM_ELEMENT(i))
3940     {
3941       int num_phase = 8;
3942       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3943                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3944                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3945       int last_phase = (num_phase + 1) * delay;
3946       int half_phase = (num_phase / 2) * delay;
3947
3948       element_info[i].explosion_delay = last_phase - 1;
3949       element_info[i].ignition_delay = half_phase;
3950
3951       if (i == EL_BLACK_ORB)
3952         element_info[i].ignition_delay = 1;
3953     }
3954   }
3955
3956   // correct non-moving belts to start moving left
3957   for (i = 0; i < NUM_BELTS; i++)
3958     if (game.belt_dir[i] == MV_NONE)
3959       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3960
3961 #if USE_NEW_PLAYER_ASSIGNMENTS
3962   // use preferred player also in local single-player mode
3963   if (!network.enabled && !game.team_mode)
3964   {
3965     int new_index_nr = setup.network_player_nr;
3966
3967     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3968     {
3969       for (i = 0; i < MAX_PLAYERS; i++)
3970         stored_player[i].connected_locally = FALSE;
3971
3972       stored_player[new_index_nr].connected_locally = TRUE;
3973     }
3974   }
3975
3976   for (i = 0; i < MAX_PLAYERS; i++)
3977   {
3978     stored_player[i].connected = FALSE;
3979
3980     // in network game mode, the local player might not be the first player
3981     if (stored_player[i].connected_locally)
3982       local_player = &stored_player[i];
3983   }
3984
3985   if (!network.enabled)
3986     local_player->connected = TRUE;
3987
3988   if (tape.playing)
3989   {
3990     for (i = 0; i < MAX_PLAYERS; i++)
3991       stored_player[i].connected = tape.player_participates[i];
3992   }
3993   else if (network.enabled)
3994   {
3995     // add team mode players connected over the network (needed for correct
3996     // assignment of player figures from level to locally playing players)
3997
3998     for (i = 0; i < MAX_PLAYERS; i++)
3999       if (stored_player[i].connected_network)
4000         stored_player[i].connected = TRUE;
4001   }
4002   else if (game.team_mode)
4003   {
4004     // try to guess locally connected team mode players (needed for correct
4005     // assignment of player figures from level to locally playing players)
4006
4007     for (i = 0; i < MAX_PLAYERS; i++)
4008       if (setup.input[i].use_joystick ||
4009           setup.input[i].key.left != KSYM_UNDEFINED)
4010         stored_player[i].connected = TRUE;
4011   }
4012
4013 #if DEBUG_INIT_PLAYER
4014   DebugPrintPlayerStatus("Player status after level initialization");
4015 #endif
4016
4017 #if DEBUG_INIT_PLAYER
4018   Debug("game:init:player", "Reassigning players ...");
4019 #endif
4020
4021   // check if any connected player was not found in playfield
4022   for (i = 0; i < MAX_PLAYERS; i++)
4023   {
4024     struct PlayerInfo *player = &stored_player[i];
4025
4026     if (player->connected && !player->present)
4027     {
4028       struct PlayerInfo *field_player = NULL;
4029
4030 #if DEBUG_INIT_PLAYER
4031       Debug("game:init:player",
4032             "- looking for field player for player %d ...", i + 1);
4033 #endif
4034
4035       // assign first free player found that is present in the playfield
4036
4037       // first try: look for unmapped playfield player that is not connected
4038       for (j = 0; j < MAX_PLAYERS; j++)
4039         if (field_player == NULL &&
4040             stored_player[j].present &&
4041             !stored_player[j].mapped &&
4042             !stored_player[j].connected)
4043           field_player = &stored_player[j];
4044
4045       // second try: look for *any* unmapped playfield player
4046       for (j = 0; j < MAX_PLAYERS; j++)
4047         if (field_player == NULL &&
4048             stored_player[j].present &&
4049             !stored_player[j].mapped)
4050           field_player = &stored_player[j];
4051
4052       if (field_player != NULL)
4053       {
4054         int jx = field_player->jx, jy = field_player->jy;
4055
4056 #if DEBUG_INIT_PLAYER
4057         Debug("game:init:player", "- found player %d",
4058               field_player->index_nr + 1);
4059 #endif
4060
4061         player->present = FALSE;
4062         player->active = FALSE;
4063
4064         field_player->present = TRUE;
4065         field_player->active = TRUE;
4066
4067         /*
4068         player->initial_element = field_player->initial_element;
4069         player->artwork_element = field_player->artwork_element;
4070
4071         player->block_last_field       = field_player->block_last_field;
4072         player->block_delay_adjustment = field_player->block_delay_adjustment;
4073         */
4074
4075         StorePlayer[jx][jy] = field_player->element_nr;
4076
4077         field_player->jx = field_player->last_jx = jx;
4078         field_player->jy = field_player->last_jy = jy;
4079
4080         if (local_player == player)
4081           local_player = field_player;
4082
4083         map_player_action[field_player->index_nr] = i;
4084
4085         field_player->mapped = TRUE;
4086
4087 #if DEBUG_INIT_PLAYER
4088         Debug("game:init:player", "- map_player_action[%d] == %d",
4089               field_player->index_nr + 1, i + 1);
4090 #endif
4091       }
4092     }
4093
4094     if (player->connected && player->present)
4095       player->mapped = TRUE;
4096   }
4097
4098 #if DEBUG_INIT_PLAYER
4099   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4100 #endif
4101
4102 #else
4103
4104   // check if any connected player was not found in playfield
4105   for (i = 0; i < MAX_PLAYERS; i++)
4106   {
4107     struct PlayerInfo *player = &stored_player[i];
4108
4109     if (player->connected && !player->present)
4110     {
4111       for (j = 0; j < MAX_PLAYERS; j++)
4112       {
4113         struct PlayerInfo *field_player = &stored_player[j];
4114         int jx = field_player->jx, jy = field_player->jy;
4115
4116         // assign first free player found that is present in the playfield
4117         if (field_player->present && !field_player->connected)
4118         {
4119           player->present = TRUE;
4120           player->active = TRUE;
4121
4122           field_player->present = FALSE;
4123           field_player->active = FALSE;
4124
4125           player->initial_element = field_player->initial_element;
4126           player->artwork_element = field_player->artwork_element;
4127
4128           player->block_last_field       = field_player->block_last_field;
4129           player->block_delay_adjustment = field_player->block_delay_adjustment;
4130
4131           StorePlayer[jx][jy] = player->element_nr;
4132
4133           player->jx = player->last_jx = jx;
4134           player->jy = player->last_jy = jy;
4135
4136           break;
4137         }
4138       }
4139     }
4140   }
4141 #endif
4142
4143 #if 0
4144   Debug("game:init:player", "local_player->present == %d",
4145         local_player->present);
4146 #endif
4147
4148   // set focus to local player for network games, else to all players
4149   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4150   game.centered_player_nr_next = game.centered_player_nr;
4151   game.set_centered_player = FALSE;
4152   game.set_centered_player_wrap = FALSE;
4153
4154   if (network_playing && tape.recording)
4155   {
4156     // store client dependent player focus when recording network games
4157     tape.centered_player_nr_next = game.centered_player_nr_next;
4158     tape.set_centered_player = TRUE;
4159   }
4160
4161   if (tape.playing)
4162   {
4163     // when playing a tape, eliminate all players who do not participate
4164
4165 #if USE_NEW_PLAYER_ASSIGNMENTS
4166
4167     if (!game.team_mode)
4168     {
4169       for (i = 0; i < MAX_PLAYERS; i++)
4170       {
4171         if (stored_player[i].active &&
4172             !tape.player_participates[map_player_action[i]])
4173         {
4174           struct PlayerInfo *player = &stored_player[i];
4175           int jx = player->jx, jy = player->jy;
4176
4177 #if DEBUG_INIT_PLAYER
4178           Debug("game:init:player", "Removing player %d at (%d, %d)",
4179                 i + 1, jx, jy);
4180 #endif
4181
4182           player->active = FALSE;
4183           StorePlayer[jx][jy] = 0;
4184           Tile[jx][jy] = EL_EMPTY;
4185         }
4186       }
4187     }
4188
4189 #else
4190
4191     for (i = 0; i < MAX_PLAYERS; i++)
4192     {
4193       if (stored_player[i].active &&
4194           !tape.player_participates[i])
4195       {
4196         struct PlayerInfo *player = &stored_player[i];
4197         int jx = player->jx, jy = player->jy;
4198
4199         player->active = FALSE;
4200         StorePlayer[jx][jy] = 0;
4201         Tile[jx][jy] = EL_EMPTY;
4202       }
4203     }
4204 #endif
4205   }
4206   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4207   {
4208     // when in single player mode, eliminate all but the local player
4209
4210     for (i = 0; i < MAX_PLAYERS; i++)
4211     {
4212       struct PlayerInfo *player = &stored_player[i];
4213
4214       if (player->active && player != local_player)
4215       {
4216         int jx = player->jx, jy = player->jy;
4217
4218         player->active = FALSE;
4219         player->present = FALSE;
4220
4221         StorePlayer[jx][jy] = 0;
4222         Tile[jx][jy] = EL_EMPTY;
4223       }
4224     }
4225   }
4226
4227   for (i = 0; i < MAX_PLAYERS; i++)
4228     if (stored_player[i].active)
4229       game.players_still_needed++;
4230
4231   if (level.solved_by_one_player)
4232     game.players_still_needed = 1;
4233
4234   // when recording the game, store which players take part in the game
4235   if (tape.recording)
4236   {
4237 #if USE_NEW_PLAYER_ASSIGNMENTS
4238     for (i = 0; i < MAX_PLAYERS; i++)
4239       if (stored_player[i].connected)
4240         tape.player_participates[i] = TRUE;
4241 #else
4242     for (i = 0; i < MAX_PLAYERS; i++)
4243       if (stored_player[i].active)
4244         tape.player_participates[i] = TRUE;
4245 #endif
4246   }
4247
4248 #if DEBUG_INIT_PLAYER
4249   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4250 #endif
4251
4252   if (BorderElement == EL_EMPTY)
4253   {
4254     SBX_Left = 0;
4255     SBX_Right = lev_fieldx - SCR_FIELDX;
4256     SBY_Upper = 0;
4257     SBY_Lower = lev_fieldy - SCR_FIELDY;
4258   }
4259   else
4260   {
4261     SBX_Left = -1;
4262     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4263     SBY_Upper = -1;
4264     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4265   }
4266
4267   if (full_lev_fieldx <= SCR_FIELDX)
4268     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4269   if (full_lev_fieldy <= SCR_FIELDY)
4270     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4271
4272   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4273     SBX_Left--;
4274   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4275     SBY_Upper--;
4276
4277   // if local player not found, look for custom element that might create
4278   // the player (make some assumptions about the right custom element)
4279   if (!local_player->present)
4280   {
4281     int start_x = 0, start_y = 0;
4282     int found_rating = 0;
4283     int found_element = EL_UNDEFINED;
4284     int player_nr = local_player->index_nr;
4285
4286     SCAN_PLAYFIELD(x, y)
4287     {
4288       int element = Tile[x][y];
4289       int content;
4290       int xx, yy;
4291       boolean is_player;
4292
4293       if (level.use_start_element[player_nr] &&
4294           level.start_element[player_nr] == element &&
4295           found_rating < 4)
4296       {
4297         start_x = x;
4298         start_y = y;
4299
4300         found_rating = 4;
4301         found_element = element;
4302       }
4303
4304       if (!IS_CUSTOM_ELEMENT(element))
4305         continue;
4306
4307       if (CAN_CHANGE(element))
4308       {
4309         for (i = 0; i < element_info[element].num_change_pages; i++)
4310         {
4311           // check for player created from custom element as single target
4312           content = element_info[element].change_page[i].target_element;
4313           is_player = ELEM_IS_PLAYER(content);
4314
4315           if (is_player && (found_rating < 3 ||
4316                             (found_rating == 3 && element < found_element)))
4317           {
4318             start_x = x;
4319             start_y = y;
4320
4321             found_rating = 3;
4322             found_element = element;
4323           }
4324         }
4325       }
4326
4327       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4328       {
4329         // check for player created from custom element as explosion content
4330         content = element_info[element].content.e[xx][yy];
4331         is_player = ELEM_IS_PLAYER(content);
4332
4333         if (is_player && (found_rating < 2 ||
4334                           (found_rating == 2 && element < found_element)))
4335         {
4336           start_x = x + xx - 1;
4337           start_y = y + yy - 1;
4338
4339           found_rating = 2;
4340           found_element = element;
4341         }
4342
4343         if (!CAN_CHANGE(element))
4344           continue;
4345
4346         for (i = 0; i < element_info[element].num_change_pages; i++)
4347         {
4348           // check for player created from custom element as extended target
4349           content =
4350             element_info[element].change_page[i].target_content.e[xx][yy];
4351
4352           is_player = ELEM_IS_PLAYER(content);
4353
4354           if (is_player && (found_rating < 1 ||
4355                             (found_rating == 1 && element < found_element)))
4356           {
4357             start_x = x + xx - 1;
4358             start_y = y + yy - 1;
4359
4360             found_rating = 1;
4361             found_element = element;
4362           }
4363         }
4364       }
4365     }
4366
4367     scroll_x = SCROLL_POSITION_X(start_x);
4368     scroll_y = SCROLL_POSITION_Y(start_y);
4369   }
4370   else
4371   {
4372     scroll_x = SCROLL_POSITION_X(local_player->jx);
4373     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4374   }
4375
4376   // !!! FIX THIS (START) !!!
4377   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4378   {
4379     InitGameEngine_EM();
4380   }
4381   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4382   {
4383     InitGameEngine_SP();
4384   }
4385   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4386   {
4387     InitGameEngine_MM();
4388   }
4389   else
4390   {
4391     DrawLevel(REDRAW_FIELD);
4392     DrawAllPlayers();
4393
4394     // after drawing the level, correct some elements
4395     if (game.timegate_time_left == 0)
4396       CloseAllOpenTimegates();
4397   }
4398
4399   // blit playfield from scroll buffer to normal back buffer for fading in
4400   BlitScreenToBitmap(backbuffer);
4401   // !!! FIX THIS (END) !!!
4402
4403   DrawMaskedBorder(fade_mask);
4404
4405   FadeIn(fade_mask);
4406
4407 #if 1
4408   // full screen redraw is required at this point in the following cases:
4409   // - special editor door undrawn when game was started from level editor
4410   // - drawing area (playfield) was changed and has to be removed completely
4411   redraw_mask = REDRAW_ALL;
4412   BackToFront();
4413 #endif
4414
4415   if (!game.restart_level)
4416   {
4417     // copy default game door content to main double buffer
4418
4419     // !!! CHECK AGAIN !!!
4420     SetPanelBackground();
4421     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4422     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4423   }
4424
4425   SetPanelBackground();
4426   SetDrawBackgroundMask(REDRAW_DOOR_1);
4427
4428   UpdateAndDisplayGameControlValues();
4429
4430   if (!game.restart_level)
4431   {
4432     UnmapGameButtons();
4433     UnmapTapeButtons();
4434
4435     FreeGameButtons();
4436     CreateGameButtons();
4437
4438     MapGameButtons();
4439     MapTapeButtons();
4440
4441     // copy actual game door content to door double buffer for OpenDoor()
4442     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4443
4444     OpenDoor(DOOR_OPEN_ALL);
4445
4446     KeyboardAutoRepeatOffUnlessAutoplay();
4447
4448 #if DEBUG_INIT_PLAYER
4449     DebugPrintPlayerStatus("Player status (final)");
4450 #endif
4451   }
4452
4453   UnmapAllGadgets();
4454
4455   MapGameButtons();
4456   MapTapeButtons();
4457
4458   if (!game.restart_level && !tape.playing)
4459   {
4460     LevelStats_incPlayed(level_nr);
4461
4462     SaveLevelSetup_SeriesInfo();
4463   }
4464
4465   game.restart_level = FALSE;
4466   game.restart_game_message = NULL;
4467
4468   game.request_active = FALSE;
4469   game.request_active_or_moving = FALSE;
4470
4471   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4472     InitGameActions_MM();
4473
4474   SaveEngineSnapshotToListInitial();
4475
4476   if (!game.restart_level)
4477   {
4478     PlaySound(SND_GAME_STARTING);
4479
4480     if (setup.sound_music)
4481       PlayLevelMusic();
4482   }
4483 }
4484
4485 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4486                         int actual_player_x, int actual_player_y)
4487 {
4488   // this is used for non-R'n'D game engines to update certain engine values
4489
4490   // needed to determine if sounds are played within the visible screen area
4491   scroll_x = actual_scroll_x;
4492   scroll_y = actual_scroll_y;
4493
4494   // needed to get player position for "follow finger" playing input method
4495   local_player->jx = actual_player_x;
4496   local_player->jy = actual_player_y;
4497 }
4498
4499 void InitMovDir(int x, int y)
4500 {
4501   int i, element = Tile[x][y];
4502   static int xy[4][2] =
4503   {
4504     {  0, +1 },
4505     { +1,  0 },
4506     {  0, -1 },
4507     { -1,  0 }
4508   };
4509   static int direction[3][4] =
4510   {
4511     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4512     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4513     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4514   };
4515
4516   switch (element)
4517   {
4518     case EL_BUG_RIGHT:
4519     case EL_BUG_UP:
4520     case EL_BUG_LEFT:
4521     case EL_BUG_DOWN:
4522       Tile[x][y] = EL_BUG;
4523       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4524       break;
4525
4526     case EL_SPACESHIP_RIGHT:
4527     case EL_SPACESHIP_UP:
4528     case EL_SPACESHIP_LEFT:
4529     case EL_SPACESHIP_DOWN:
4530       Tile[x][y] = EL_SPACESHIP;
4531       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4532       break;
4533
4534     case EL_BD_BUTTERFLY_RIGHT:
4535     case EL_BD_BUTTERFLY_UP:
4536     case EL_BD_BUTTERFLY_LEFT:
4537     case EL_BD_BUTTERFLY_DOWN:
4538       Tile[x][y] = EL_BD_BUTTERFLY;
4539       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4540       break;
4541
4542     case EL_BD_FIREFLY_RIGHT:
4543     case EL_BD_FIREFLY_UP:
4544     case EL_BD_FIREFLY_LEFT:
4545     case EL_BD_FIREFLY_DOWN:
4546       Tile[x][y] = EL_BD_FIREFLY;
4547       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4548       break;
4549
4550     case EL_PACMAN_RIGHT:
4551     case EL_PACMAN_UP:
4552     case EL_PACMAN_LEFT:
4553     case EL_PACMAN_DOWN:
4554       Tile[x][y] = EL_PACMAN;
4555       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4556       break;
4557
4558     case EL_YAMYAM_LEFT:
4559     case EL_YAMYAM_RIGHT:
4560     case EL_YAMYAM_UP:
4561     case EL_YAMYAM_DOWN:
4562       Tile[x][y] = EL_YAMYAM;
4563       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4564       break;
4565
4566     case EL_SP_SNIKSNAK:
4567       MovDir[x][y] = MV_UP;
4568       break;
4569
4570     case EL_SP_ELECTRON:
4571       MovDir[x][y] = MV_LEFT;
4572       break;
4573
4574     case EL_MOLE_LEFT:
4575     case EL_MOLE_RIGHT:
4576     case EL_MOLE_UP:
4577     case EL_MOLE_DOWN:
4578       Tile[x][y] = EL_MOLE;
4579       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4580       break;
4581
4582     case EL_SPRING_LEFT:
4583     case EL_SPRING_RIGHT:
4584       Tile[x][y] = EL_SPRING;
4585       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4586       break;
4587
4588     default:
4589       if (IS_CUSTOM_ELEMENT(element))
4590       {
4591         struct ElementInfo *ei = &element_info[element];
4592         int move_direction_initial = ei->move_direction_initial;
4593         int move_pattern = ei->move_pattern;
4594
4595         if (move_direction_initial == MV_START_PREVIOUS)
4596         {
4597           if (MovDir[x][y] != MV_NONE)
4598             return;
4599
4600           move_direction_initial = MV_START_AUTOMATIC;
4601         }
4602
4603         if (move_direction_initial == MV_START_RANDOM)
4604           MovDir[x][y] = 1 << RND(4);
4605         else if (move_direction_initial & MV_ANY_DIRECTION)
4606           MovDir[x][y] = move_direction_initial;
4607         else if (move_pattern == MV_ALL_DIRECTIONS ||
4608                  move_pattern == MV_TURNING_LEFT ||
4609                  move_pattern == MV_TURNING_RIGHT ||
4610                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4611                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4612                  move_pattern == MV_TURNING_RANDOM)
4613           MovDir[x][y] = 1 << RND(4);
4614         else if (move_pattern == MV_HORIZONTAL)
4615           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4616         else if (move_pattern == MV_VERTICAL)
4617           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4618         else if (move_pattern & MV_ANY_DIRECTION)
4619           MovDir[x][y] = element_info[element].move_pattern;
4620         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4621                  move_pattern == MV_ALONG_RIGHT_SIDE)
4622         {
4623           // use random direction as default start direction
4624           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4625             MovDir[x][y] = 1 << RND(4);
4626
4627           for (i = 0; i < NUM_DIRECTIONS; i++)
4628           {
4629             int x1 = x + xy[i][0];
4630             int y1 = y + xy[i][1];
4631
4632             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4633             {
4634               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4635                 MovDir[x][y] = direction[0][i];
4636               else
4637                 MovDir[x][y] = direction[1][i];
4638
4639               break;
4640             }
4641           }
4642         }                
4643       }
4644       else
4645       {
4646         MovDir[x][y] = 1 << RND(4);
4647
4648         if (element != EL_BUG &&
4649             element != EL_SPACESHIP &&
4650             element != EL_BD_BUTTERFLY &&
4651             element != EL_BD_FIREFLY)
4652           break;
4653
4654         for (i = 0; i < NUM_DIRECTIONS; i++)
4655         {
4656           int x1 = x + xy[i][0];
4657           int y1 = y + xy[i][1];
4658
4659           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4660           {
4661             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4662             {
4663               MovDir[x][y] = direction[0][i];
4664               break;
4665             }
4666             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4667                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4668             {
4669               MovDir[x][y] = direction[1][i];
4670               break;
4671             }
4672           }
4673         }
4674       }
4675       break;
4676   }
4677
4678   GfxDir[x][y] = MovDir[x][y];
4679 }
4680
4681 void InitAmoebaNr(int x, int y)
4682 {
4683   int i;
4684   int group_nr = AmoebaNeighbourNr(x, y);
4685
4686   if (group_nr == 0)
4687   {
4688     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4689     {
4690       if (AmoebaCnt[i] == 0)
4691       {
4692         group_nr = i;
4693         break;
4694       }
4695     }
4696   }
4697
4698   AmoebaNr[x][y] = group_nr;
4699   AmoebaCnt[group_nr]++;
4700   AmoebaCnt2[group_nr]++;
4701 }
4702
4703 static void LevelSolved(void)
4704 {
4705   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4706       game.players_still_needed > 0)
4707     return;
4708
4709   game.LevelSolved = TRUE;
4710   game.GameOver = TRUE;
4711 }
4712
4713 void GameWon(void)
4714 {
4715   static int time_count_steps;
4716   static int time, time_final;
4717   static float score, score_final; // needed for time score < 10 for 10 seconds
4718   static int health, health_final;
4719   static int game_over_delay_1 = 0;
4720   static int game_over_delay_2 = 0;
4721   static int game_over_delay_3 = 0;
4722   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4723   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4724
4725   if (!game.LevelSolved_GameWon)
4726   {
4727     int i;
4728
4729     // do not start end game actions before the player stops moving (to exit)
4730     if (local_player->active && local_player->MovPos)
4731       return;
4732
4733     game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4734     game.score_time_final = (level.use_step_counter ? TimePlayed :
4735                              TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4736
4737     game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4738                         game_em.lev->score :
4739                         level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4740                         game_mm.score :
4741                         game.score);
4742
4743     game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4744                          MM_HEALTH(game_mm.laser_overload_value) :
4745                          game.health);
4746
4747     game.LevelSolved_CountingTime = game.time_final;
4748     game.LevelSolved_CountingScore = game.score_final;
4749     game.LevelSolved_CountingHealth = game.health_final;
4750
4751     game.LevelSolved_GameWon = TRUE;
4752     game.LevelSolved_SaveTape = tape.recording;
4753     game.LevelSolved_SaveScore = !tape.playing;
4754
4755     if (!tape.playing)
4756     {
4757       LevelStats_incSolved(level_nr);
4758
4759       SaveLevelSetup_SeriesInfo();
4760     }
4761
4762     if (tape.auto_play)         // tape might already be stopped here
4763       tape.auto_play_level_solved = TRUE;
4764
4765     TapeStop();
4766
4767     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4768     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4769     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4770
4771     time = time_final = game.time_final;
4772     score = score_final = game.score_final;
4773     health = health_final = game.health_final;
4774
4775     if (time_score > 0)
4776     {
4777       int time_final_max = 999;
4778       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4779       int time_frames = 0;
4780       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4781       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4782
4783       if (TimeLeft > 0)
4784       {
4785         time_final = 0;
4786         time_frames = time_frames_left;
4787       }
4788       else if (game.no_time_limit && TimePlayed < time_final_max)
4789       {
4790         time_final = time_final_max;
4791         time_frames = time_frames_final_max - time_frames_played;
4792       }
4793
4794       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4795
4796       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4797
4798       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4799       {
4800         health_final = 0;
4801         score_final += health * time_score;
4802       }
4803
4804       game.score_final = score_final;
4805       game.health_final = health_final;
4806     }
4807
4808     if (level_editor_test_game || !setup.count_score_after_game)
4809     {
4810       time = time_final;
4811       score = score_final;
4812
4813       game.LevelSolved_CountingTime = time;
4814       game.LevelSolved_CountingScore = score;
4815
4816       game_panel_controls[GAME_PANEL_TIME].value = time;
4817       game_panel_controls[GAME_PANEL_SCORE].value = score;
4818
4819       DisplayGameControlValues();
4820     }
4821
4822     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4823     {
4824       // check if last player has left the level
4825       if (game.exit_x >= 0 &&
4826           game.exit_y >= 0)
4827       {
4828         int x = game.exit_x;
4829         int y = game.exit_y;
4830         int element = Tile[x][y];
4831
4832         // close exit door after last player
4833         if ((game.all_players_gone &&
4834              (element == EL_EXIT_OPEN ||
4835               element == EL_SP_EXIT_OPEN ||
4836               element == EL_STEEL_EXIT_OPEN)) ||
4837             element == EL_EM_EXIT_OPEN ||
4838             element == EL_EM_STEEL_EXIT_OPEN)
4839         {
4840
4841           Tile[x][y] =
4842             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4843              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4844              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4845              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4846              EL_EM_STEEL_EXIT_CLOSING);
4847
4848           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4849         }
4850
4851         // player disappears
4852         DrawLevelField(x, y);
4853       }
4854
4855       for (i = 0; i < MAX_PLAYERS; i++)
4856       {
4857         struct PlayerInfo *player = &stored_player[i];
4858
4859         if (player->present)
4860         {
4861           RemovePlayer(player);
4862
4863           // player disappears
4864           DrawLevelField(player->jx, player->jy);
4865         }
4866       }
4867     }
4868
4869     PlaySound(SND_GAME_WINNING);
4870   }
4871
4872   if (setup.count_score_after_game)
4873   {
4874     if (time != time_final)
4875     {
4876       if (game_over_delay_1 > 0)
4877       {
4878         game_over_delay_1--;
4879
4880         return;
4881       }
4882
4883       int time_to_go = ABS(time_final - time);
4884       int time_count_dir = (time < time_final ? +1 : -1);
4885
4886       if (time_to_go < time_count_steps)
4887         time_count_steps = 1;
4888
4889       time  += time_count_steps * time_count_dir;
4890       score += time_count_steps * time_score;
4891
4892       // set final score to correct rounding differences after counting score
4893       if (time == time_final)
4894         score = score_final;
4895
4896       game.LevelSolved_CountingTime = time;
4897       game.LevelSolved_CountingScore = score;
4898
4899       game_panel_controls[GAME_PANEL_TIME].value = time;
4900       game_panel_controls[GAME_PANEL_SCORE].value = score;
4901
4902       DisplayGameControlValues();
4903
4904       if (time == time_final)
4905         StopSound(SND_GAME_LEVELTIME_BONUS);
4906       else if (setup.sound_loops)
4907         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4908       else
4909         PlaySound(SND_GAME_LEVELTIME_BONUS);
4910
4911       return;
4912     }
4913
4914     if (health != health_final)
4915     {
4916       if (game_over_delay_2 > 0)
4917       {
4918         game_over_delay_2--;
4919
4920         return;
4921       }
4922
4923       int health_count_dir = (health < health_final ? +1 : -1);
4924
4925       health += health_count_dir;
4926       score  += time_score;
4927
4928       game.LevelSolved_CountingHealth = health;
4929       game.LevelSolved_CountingScore = score;
4930
4931       game_panel_controls[GAME_PANEL_HEALTH].value = health;
4932       game_panel_controls[GAME_PANEL_SCORE].value = score;
4933
4934       DisplayGameControlValues();
4935
4936       if (health == health_final)
4937         StopSound(SND_GAME_LEVELTIME_BONUS);
4938       else if (setup.sound_loops)
4939         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4940       else
4941         PlaySound(SND_GAME_LEVELTIME_BONUS);
4942
4943       return;
4944     }
4945   }
4946
4947   game.panel.active = FALSE;
4948
4949   if (game_over_delay_3 > 0)
4950   {
4951     game_over_delay_3--;
4952
4953     return;
4954   }
4955
4956   GameEnd();
4957 }
4958
4959 void GameEnd(void)
4960 {
4961   // used instead of "level_nr" (needed for network games)
4962   int last_level_nr = levelset.level_nr;
4963
4964   game.LevelSolved_GameEnd = TRUE;
4965
4966   if (game.LevelSolved_SaveTape)
4967   {
4968     // make sure that request dialog to save tape does not open door again
4969     if (!global.use_envelope_request)
4970       CloseDoor(DOOR_CLOSE_1);
4971
4972     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4973
4974     // set unique basename for score tape (also saved in high score table)
4975     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
4976   }
4977
4978   // if no tape is to be saved, close both doors simultaneously
4979   CloseDoor(DOOR_CLOSE_ALL);
4980
4981   if (level_editor_test_game)
4982   {
4983     SetGameStatus(GAME_MODE_MAIN);
4984
4985     DrawMainMenu();
4986
4987     return;
4988   }
4989
4990   if (!game.LevelSolved_SaveScore)
4991   {
4992     SetGameStatus(GAME_MODE_MAIN);
4993
4994     DrawMainMenu();
4995
4996     return;
4997   }
4998
4999   if (level_nr == leveldir_current->handicap_level)
5000   {
5001     leveldir_current->handicap_level++;
5002
5003     SaveLevelSetup_SeriesInfo();
5004   }
5005
5006   // save score and score tape before potentially erasing tape below
5007   NewHighScore(last_level_nr);
5008
5009   if (setup.increment_levels &&
5010       level_nr < leveldir_current->last_level &&
5011       !network_playing)
5012   {
5013     level_nr++;         // advance to next level
5014     TapeErase();        // start with empty tape
5015
5016     if (setup.auto_play_next_level)
5017     {
5018       LoadLevel(level_nr);
5019
5020       SaveLevelSetup_SeriesInfo();
5021     }
5022   }
5023
5024   if (scores.last_added >= 0 && setup.show_scores_after_game)
5025   {
5026     SetGameStatus(GAME_MODE_SCORES);
5027
5028     DrawHallOfFame(last_level_nr);
5029   }
5030   else if (setup.auto_play_next_level && setup.increment_levels &&
5031            last_level_nr < leveldir_current->last_level &&
5032            !network_playing)
5033   {
5034     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5035   }
5036   else
5037   {
5038     SetGameStatus(GAME_MODE_MAIN);
5039
5040     DrawMainMenu();
5041   }
5042 }
5043
5044 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry)
5045 {
5046   boolean one_score_entry_per_name = !program.many_scores_per_name;
5047   int i;
5048
5049   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME))
5050     return -1;
5051
5052   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5053   {
5054     struct ScoreEntry *entry = &list->entry[i];
5055     boolean score_is_better = (new_entry->score >  entry->score);
5056     boolean score_is_equal  = (new_entry->score == entry->score);
5057     boolean time_is_better  = (new_entry->time  <  entry->time);
5058     boolean time_is_equal   = (new_entry->time  == entry->time);
5059     boolean better_by_score = (score_is_better ||
5060                                (score_is_equal && time_is_better));
5061     boolean better_by_time  = (time_is_better ||
5062                                (time_is_equal && score_is_better));
5063     boolean is_better = (level.rate_time_over_score ? better_by_time :
5064                          better_by_score);
5065     boolean entry_is_empty = (entry->score == 0 &&
5066                               entry->time == 0);
5067
5068     // prevent adding server score entries if also existing in local score file
5069     if (strEqual(new_entry->tape_basename, entry->tape_basename))
5070       return -1;
5071
5072     if (is_better || entry_is_empty)
5073     {
5074       // player has made it to the hall of fame
5075
5076       if (i < MAX_SCORE_ENTRIES - 1)
5077       {
5078         int m = MAX_SCORE_ENTRIES - 1;
5079         int l;
5080
5081         if (one_score_entry_per_name)
5082         {
5083           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5084             if (strEqual(list->entry[l].name, setup.player_name))
5085               m = l;
5086
5087           if (m == i)   // player's new highscore overwrites his old one
5088             goto put_into_list;
5089         }
5090
5091         for (l = m; l > i; l--)
5092           list->entry[l] = list->entry[l - 1];
5093       }
5094
5095       put_into_list:
5096
5097       *entry = *new_entry;
5098
5099       return i;
5100     }
5101     else if (one_score_entry_per_name &&
5102              strEqual(entry->name, setup.player_name))
5103     {
5104       // player already in high score list with better score or time
5105
5106       return -1;
5107     }
5108   }
5109
5110   return -1;
5111 }
5112
5113 void NewHighScore(int level_nr)
5114 {
5115   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5116
5117   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5118   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5119
5120   new_entry.score = game.score_final;
5121   new_entry.time = game.score_time_final;
5122
5123   LoadScore(level_nr);
5124
5125   scores.last_added = addScoreEntry(&scores, &new_entry);
5126
5127   if (scores.last_added >= 0)
5128   {
5129     SaveScore(level_nr);
5130
5131     if (game.LevelSolved_SaveTape)
5132     {
5133       SaveScoreTape(level_nr);
5134       SaveServerScore(level_nr);
5135     }
5136
5137     // store last added local score entry (before merging server scores)
5138     scores.last_added_local = scores.last_added;
5139   }
5140 }
5141
5142 void MergeServerScore(void)
5143 {
5144   int i;
5145
5146   for (i = 0; i < server_scores.num_entries; i++)
5147   {
5148     int pos = addScoreEntry(&scores, &server_scores.entry[i]);
5149
5150     if (pos >= 0 && pos <= scores.last_added)
5151       scores.last_added++;
5152   }
5153
5154   if (scores.last_added >= MAX_SCORE_ENTRIES)
5155     scores.last_added = -1;
5156 }
5157
5158 static int getElementMoveStepsizeExt(int x, int y, int direction)
5159 {
5160   int element = Tile[x][y];
5161   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5162   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5163   int horiz_move = (dx != 0);
5164   int sign = (horiz_move ? dx : dy);
5165   int step = sign * element_info[element].move_stepsize;
5166
5167   // special values for move stepsize for spring and things on conveyor belt
5168   if (horiz_move)
5169   {
5170     if (CAN_FALL(element) &&
5171         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5172       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5173     else if (element == EL_SPRING)
5174       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5175   }
5176
5177   return step;
5178 }
5179
5180 static int getElementMoveStepsize(int x, int y)
5181 {
5182   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5183 }
5184
5185 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5186 {
5187   if (player->GfxAction != action || player->GfxDir != dir)
5188   {
5189     player->GfxAction = action;
5190     player->GfxDir = dir;
5191     player->Frame = 0;
5192     player->StepFrame = 0;
5193   }
5194 }
5195
5196 static void ResetGfxFrame(int x, int y)
5197 {
5198   // profiling showed that "autotest" spends 10~20% of its time in this function
5199   if (DrawingDeactivatedField())
5200     return;
5201
5202   int element = Tile[x][y];
5203   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5204
5205   if (graphic_info[graphic].anim_global_sync)
5206     GfxFrame[x][y] = FrameCounter;
5207   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5208     GfxFrame[x][y] = CustomValue[x][y];
5209   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5210     GfxFrame[x][y] = element_info[element].collect_score;
5211   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5212     GfxFrame[x][y] = ChangeDelay[x][y];
5213 }
5214
5215 static void ResetGfxAnimation(int x, int y)
5216 {
5217   GfxAction[x][y] = ACTION_DEFAULT;
5218   GfxDir[x][y] = MovDir[x][y];
5219   GfxFrame[x][y] = 0;
5220
5221   ResetGfxFrame(x, y);
5222 }
5223
5224 static void ResetRandomAnimationValue(int x, int y)
5225 {
5226   GfxRandom[x][y] = INIT_GFX_RANDOM();
5227 }
5228
5229 static void InitMovingField(int x, int y, int direction)
5230 {
5231   int element = Tile[x][y];
5232   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5233   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5234   int newx = x + dx;
5235   int newy = y + dy;
5236   boolean is_moving_before, is_moving_after;
5237
5238   // check if element was/is moving or being moved before/after mode change
5239   is_moving_before = (WasJustMoving[x][y] != 0);
5240   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5241
5242   // reset animation only for moving elements which change direction of moving
5243   // or which just started or stopped moving
5244   // (else CEs with property "can move" / "not moving" are reset each frame)
5245   if (is_moving_before != is_moving_after ||
5246       direction != MovDir[x][y])
5247     ResetGfxAnimation(x, y);
5248
5249   MovDir[x][y] = direction;
5250   GfxDir[x][y] = direction;
5251
5252   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5253                      direction == MV_DOWN && CAN_FALL(element) ?
5254                      ACTION_FALLING : ACTION_MOVING);
5255
5256   // this is needed for CEs with property "can move" / "not moving"
5257
5258   if (is_moving_after)
5259   {
5260     if (Tile[newx][newy] == EL_EMPTY)
5261       Tile[newx][newy] = EL_BLOCKED;
5262
5263     MovDir[newx][newy] = MovDir[x][y];
5264
5265     CustomValue[newx][newy] = CustomValue[x][y];
5266
5267     GfxFrame[newx][newy] = GfxFrame[x][y];
5268     GfxRandom[newx][newy] = GfxRandom[x][y];
5269     GfxAction[newx][newy] = GfxAction[x][y];
5270     GfxDir[newx][newy] = GfxDir[x][y];
5271   }
5272 }
5273
5274 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5275 {
5276   int direction = MovDir[x][y];
5277   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5278   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5279
5280   *goes_to_x = newx;
5281   *goes_to_y = newy;
5282 }
5283
5284 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5285 {
5286   int oldx = x, oldy = y;
5287   int direction = MovDir[x][y];
5288
5289   if (direction == MV_LEFT)
5290     oldx++;
5291   else if (direction == MV_RIGHT)
5292     oldx--;
5293   else if (direction == MV_UP)
5294     oldy++;
5295   else if (direction == MV_DOWN)
5296     oldy--;
5297
5298   *comes_from_x = oldx;
5299   *comes_from_y = oldy;
5300 }
5301
5302 static int MovingOrBlocked2Element(int x, int y)
5303 {
5304   int element = Tile[x][y];
5305
5306   if (element == EL_BLOCKED)
5307   {
5308     int oldx, oldy;
5309
5310     Blocked2Moving(x, y, &oldx, &oldy);
5311     return Tile[oldx][oldy];
5312   }
5313   else
5314     return element;
5315 }
5316
5317 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5318 {
5319   // like MovingOrBlocked2Element(), but if element is moving
5320   // and (x,y) is the field the moving element is just leaving,
5321   // return EL_BLOCKED instead of the element value
5322   int element = Tile[x][y];
5323
5324   if (IS_MOVING(x, y))
5325   {
5326     if (element == EL_BLOCKED)
5327     {
5328       int oldx, oldy;
5329
5330       Blocked2Moving(x, y, &oldx, &oldy);
5331       return Tile[oldx][oldy];
5332     }
5333     else
5334       return EL_BLOCKED;
5335   }
5336   else
5337     return element;
5338 }
5339
5340 static void RemoveField(int x, int y)
5341 {
5342   Tile[x][y] = EL_EMPTY;
5343
5344   MovPos[x][y] = 0;
5345   MovDir[x][y] = 0;
5346   MovDelay[x][y] = 0;
5347
5348   CustomValue[x][y] = 0;
5349
5350   AmoebaNr[x][y] = 0;
5351   ChangeDelay[x][y] = 0;
5352   ChangePage[x][y] = -1;
5353   Pushed[x][y] = FALSE;
5354
5355   GfxElement[x][y] = EL_UNDEFINED;
5356   GfxAction[x][y] = ACTION_DEFAULT;
5357   GfxDir[x][y] = MV_NONE;
5358 }
5359
5360 static void RemoveMovingField(int x, int y)
5361 {
5362   int oldx = x, oldy = y, newx = x, newy = y;
5363   int element = Tile[x][y];
5364   int next_element = EL_UNDEFINED;
5365
5366   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5367     return;
5368
5369   if (IS_MOVING(x, y))
5370   {
5371     Moving2Blocked(x, y, &newx, &newy);
5372
5373     if (Tile[newx][newy] != EL_BLOCKED)
5374     {
5375       // element is moving, but target field is not free (blocked), but
5376       // already occupied by something different (example: acid pool);
5377       // in this case, only remove the moving field, but not the target
5378
5379       RemoveField(oldx, oldy);
5380
5381       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5382
5383       TEST_DrawLevelField(oldx, oldy);
5384
5385       return;
5386     }
5387   }
5388   else if (element == EL_BLOCKED)
5389   {
5390     Blocked2Moving(x, y, &oldx, &oldy);
5391     if (!IS_MOVING(oldx, oldy))
5392       return;
5393   }
5394
5395   if (element == EL_BLOCKED &&
5396       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5397        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5398        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5399        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5400        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5401        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5402     next_element = get_next_element(Tile[oldx][oldy]);
5403
5404   RemoveField(oldx, oldy);
5405   RemoveField(newx, newy);
5406
5407   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5408
5409   if (next_element != EL_UNDEFINED)
5410     Tile[oldx][oldy] = next_element;
5411
5412   TEST_DrawLevelField(oldx, oldy);
5413   TEST_DrawLevelField(newx, newy);
5414 }
5415
5416 void DrawDynamite(int x, int y)
5417 {
5418   int sx = SCREENX(x), sy = SCREENY(y);
5419   int graphic = el2img(Tile[x][y]);
5420   int frame;
5421
5422   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5423     return;
5424
5425   if (IS_WALKABLE_INSIDE(Back[x][y]))
5426     return;
5427
5428   if (Back[x][y])
5429     DrawLevelElement(x, y, Back[x][y]);
5430   else if (Store[x][y])
5431     DrawLevelElement(x, y, Store[x][y]);
5432   else if (game.use_masked_elements)
5433     DrawLevelElement(x, y, EL_EMPTY);
5434
5435   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5436
5437   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5438     DrawGraphicThruMask(sx, sy, graphic, frame);
5439   else
5440     DrawGraphic(sx, sy, graphic, frame);
5441 }
5442
5443 static void CheckDynamite(int x, int y)
5444 {
5445   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5446   {
5447     MovDelay[x][y]--;
5448
5449     if (MovDelay[x][y] != 0)
5450     {
5451       DrawDynamite(x, y);
5452       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5453
5454       return;
5455     }
5456   }
5457
5458   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5459
5460   Bang(x, y);
5461 }
5462
5463 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5464 {
5465   boolean num_checked_players = 0;
5466   int i;
5467
5468   for (i = 0; i < MAX_PLAYERS; i++)
5469   {
5470     if (stored_player[i].active)
5471     {
5472       int sx = stored_player[i].jx;
5473       int sy = stored_player[i].jy;
5474
5475       if (num_checked_players == 0)
5476       {
5477         *sx1 = *sx2 = sx;
5478         *sy1 = *sy2 = sy;
5479       }
5480       else
5481       {
5482         *sx1 = MIN(*sx1, sx);
5483         *sy1 = MIN(*sy1, sy);
5484         *sx2 = MAX(*sx2, sx);
5485         *sy2 = MAX(*sy2, sy);
5486       }
5487
5488       num_checked_players++;
5489     }
5490   }
5491 }
5492
5493 static boolean checkIfAllPlayersFitToScreen_RND(void)
5494 {
5495   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5496
5497   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5498
5499   return (sx2 - sx1 < SCR_FIELDX &&
5500           sy2 - sy1 < SCR_FIELDY);
5501 }
5502
5503 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5504 {
5505   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5506
5507   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5508
5509   *sx = (sx1 + sx2) / 2;
5510   *sy = (sy1 + sy2) / 2;
5511 }
5512
5513 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5514                                boolean center_screen, boolean quick_relocation)
5515 {
5516   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5517   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5518   boolean no_delay = (tape.warp_forward);
5519   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5520   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5521   int new_scroll_x, new_scroll_y;
5522
5523   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5524   {
5525     // case 1: quick relocation inside visible screen (without scrolling)
5526
5527     RedrawPlayfield();
5528
5529     return;
5530   }
5531
5532   if (!level.shifted_relocation || center_screen)
5533   {
5534     // relocation _with_ centering of screen
5535
5536     new_scroll_x = SCROLL_POSITION_X(x);
5537     new_scroll_y = SCROLL_POSITION_Y(y);
5538   }
5539   else
5540   {
5541     // relocation _without_ centering of screen
5542
5543     int center_scroll_x = SCROLL_POSITION_X(old_x);
5544     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5545     int offset_x = x + (scroll_x - center_scroll_x);
5546     int offset_y = y + (scroll_y - center_scroll_y);
5547
5548     // for new screen position, apply previous offset to center position
5549     new_scroll_x = SCROLL_POSITION_X(offset_x);
5550     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5551   }
5552
5553   if (quick_relocation)
5554   {
5555     // case 2: quick relocation (redraw without visible scrolling)
5556
5557     scroll_x = new_scroll_x;
5558     scroll_y = new_scroll_y;
5559
5560     RedrawPlayfield();
5561
5562     return;
5563   }
5564
5565   // case 3: visible relocation (with scrolling to new position)
5566
5567   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5568
5569   SetVideoFrameDelay(wait_delay_value);
5570
5571   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5572   {
5573     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5574     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5575
5576     if (dx == 0 && dy == 0)             // no scrolling needed at all
5577       break;
5578
5579     scroll_x -= dx;
5580     scroll_y -= dy;
5581
5582     // set values for horizontal/vertical screen scrolling (half tile size)
5583     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5584     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5585     int pos_x = dx * TILEX / 2;
5586     int pos_y = dy * TILEY / 2;
5587     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5588     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5589
5590     ScrollLevel(dx, dy);
5591     DrawAllPlayers();
5592
5593     // scroll in two steps of half tile size to make things smoother
5594     BlitScreenToBitmapExt_RND(window, fx, fy);
5595
5596     // scroll second step to align at full tile size
5597     BlitScreenToBitmap(window);
5598   }
5599
5600   DrawAllPlayers();
5601   BackToFront();
5602
5603   SetVideoFrameDelay(frame_delay_value_old);
5604 }
5605
5606 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5607 {
5608   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5609   int player_nr = GET_PLAYER_NR(el_player);
5610   struct PlayerInfo *player = &stored_player[player_nr];
5611   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5612   boolean no_delay = (tape.warp_forward);
5613   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5614   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5615   int old_jx = player->jx;
5616   int old_jy = player->jy;
5617   int old_element = Tile[old_jx][old_jy];
5618   int element = Tile[jx][jy];
5619   boolean player_relocated = (old_jx != jx || old_jy != jy);
5620
5621   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5622   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5623   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5624   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5625   int leave_side_horiz = move_dir_horiz;
5626   int leave_side_vert  = move_dir_vert;
5627   int enter_side = enter_side_horiz | enter_side_vert;
5628   int leave_side = leave_side_horiz | leave_side_vert;
5629
5630   if (player->buried)           // do not reanimate dead player
5631     return;
5632
5633   if (!player_relocated)        // no need to relocate the player
5634     return;
5635
5636   if (IS_PLAYER(jx, jy))        // player already placed at new position
5637   {
5638     RemoveField(jx, jy);        // temporarily remove newly placed player
5639     DrawLevelField(jx, jy);
5640   }
5641
5642   if (player->present)
5643   {
5644     while (player->MovPos)
5645     {
5646       ScrollPlayer(player, SCROLL_GO_ON);
5647       ScrollScreen(NULL, SCROLL_GO_ON);
5648
5649       AdvanceFrameAndPlayerCounters(player->index_nr);
5650
5651       DrawPlayer(player);
5652
5653       BackToFront_WithFrameDelay(wait_delay_value);
5654     }
5655
5656     DrawPlayer(player);         // needed here only to cleanup last field
5657     DrawLevelField(player->jx, player->jy);     // remove player graphic
5658
5659     player->is_moving = FALSE;
5660   }
5661
5662   if (IS_CUSTOM_ELEMENT(old_element))
5663     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5664                                CE_LEFT_BY_PLAYER,
5665                                player->index_bit, leave_side);
5666
5667   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5668                                       CE_PLAYER_LEAVES_X,
5669                                       player->index_bit, leave_side);
5670
5671   Tile[jx][jy] = el_player;
5672   InitPlayerField(jx, jy, el_player, TRUE);
5673
5674   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5675      possible that the relocation target field did not contain a player element,
5676      but a walkable element, to which the new player was relocated -- in this
5677      case, restore that (already initialized!) element on the player field */
5678   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5679   {
5680     Tile[jx][jy] = element;     // restore previously existing element
5681   }
5682
5683   // only visually relocate centered player
5684   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5685                      FALSE, level.instant_relocation);
5686
5687   TestIfPlayerTouchesBadThing(jx, jy);
5688   TestIfPlayerTouchesCustomElement(jx, jy);
5689
5690   if (IS_CUSTOM_ELEMENT(element))
5691     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5692                                player->index_bit, enter_side);
5693
5694   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5695                                       player->index_bit, enter_side);
5696
5697   if (player->is_switching)
5698   {
5699     /* ensure that relocation while still switching an element does not cause
5700        a new element to be treated as also switched directly after relocation
5701        (this is important for teleporter switches that teleport the player to
5702        a place where another teleporter switch is in the same direction, which
5703        would then incorrectly be treated as immediately switched before the
5704        direction key that caused the switch was released) */
5705
5706     player->switch_x += jx - old_jx;
5707     player->switch_y += jy - old_jy;
5708   }
5709 }
5710
5711 static void Explode(int ex, int ey, int phase, int mode)
5712 {
5713   int x, y;
5714   int last_phase;
5715   int border_element;
5716
5717   // !!! eliminate this variable !!!
5718   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5719
5720   if (game.explosions_delayed)
5721   {
5722     ExplodeField[ex][ey] = mode;
5723     return;
5724   }
5725
5726   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5727   {
5728     int center_element = Tile[ex][ey];
5729     int artwork_element, explosion_element;     // set these values later
5730
5731     // remove things displayed in background while burning dynamite
5732     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5733       Back[ex][ey] = 0;
5734
5735     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5736     {
5737       // put moving element to center field (and let it explode there)
5738       center_element = MovingOrBlocked2Element(ex, ey);
5739       RemoveMovingField(ex, ey);
5740       Tile[ex][ey] = center_element;
5741     }
5742
5743     // now "center_element" is finally determined -- set related values now
5744     artwork_element = center_element;           // for custom player artwork
5745     explosion_element = center_element;         // for custom player artwork
5746
5747     if (IS_PLAYER(ex, ey))
5748     {
5749       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5750
5751       artwork_element = stored_player[player_nr].artwork_element;
5752
5753       if (level.use_explosion_element[player_nr])
5754       {
5755         explosion_element = level.explosion_element[player_nr];
5756         artwork_element = explosion_element;
5757       }
5758     }
5759
5760     if (mode == EX_TYPE_NORMAL ||
5761         mode == EX_TYPE_CENTER ||
5762         mode == EX_TYPE_CROSS)
5763       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5764
5765     last_phase = element_info[explosion_element].explosion_delay + 1;
5766
5767     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5768     {
5769       int xx = x - ex + 1;
5770       int yy = y - ey + 1;
5771       int element;
5772
5773       if (!IN_LEV_FIELD(x, y) ||
5774           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5775           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5776         continue;
5777
5778       element = Tile[x][y];
5779
5780       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5781       {
5782         element = MovingOrBlocked2Element(x, y);
5783
5784         if (!IS_EXPLOSION_PROOF(element))
5785           RemoveMovingField(x, y);
5786       }
5787
5788       // indestructible elements can only explode in center (but not flames)
5789       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5790                                            mode == EX_TYPE_BORDER)) ||
5791           element == EL_FLAMES)
5792         continue;
5793
5794       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5795          behaviour, for example when touching a yamyam that explodes to rocks
5796          with active deadly shield, a rock is created under the player !!! */
5797       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5798 #if 0
5799       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5800           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5801            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5802 #else
5803       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5804 #endif
5805       {
5806         if (IS_ACTIVE_BOMB(element))
5807         {
5808           // re-activate things under the bomb like gate or penguin
5809           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5810           Back[x][y] = 0;
5811         }
5812
5813         continue;
5814       }
5815
5816       // save walkable background elements while explosion on same tile
5817       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5818           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5819         Back[x][y] = element;
5820
5821       // ignite explodable elements reached by other explosion
5822       if (element == EL_EXPLOSION)
5823         element = Store2[x][y];
5824
5825       if (AmoebaNr[x][y] &&
5826           (element == EL_AMOEBA_FULL ||
5827            element == EL_BD_AMOEBA ||
5828            element == EL_AMOEBA_GROWING))
5829       {
5830         AmoebaCnt[AmoebaNr[x][y]]--;
5831         AmoebaCnt2[AmoebaNr[x][y]]--;
5832       }
5833
5834       RemoveField(x, y);
5835
5836       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5837       {
5838         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5839
5840         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5841
5842         if (PLAYERINFO(ex, ey)->use_murphy)
5843           Store[x][y] = EL_EMPTY;
5844       }
5845
5846       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5847       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5848       else if (ELEM_IS_PLAYER(center_element))
5849         Store[x][y] = EL_EMPTY;
5850       else if (center_element == EL_YAMYAM)
5851         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5852       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5853         Store[x][y] = element_info[center_element].content.e[xx][yy];
5854 #if 1
5855       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5856       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5857       // otherwise) -- FIX THIS !!!
5858       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5859         Store[x][y] = element_info[element].content.e[1][1];
5860 #else
5861       else if (!CAN_EXPLODE(element))
5862         Store[x][y] = element_info[element].content.e[1][1];
5863 #endif
5864       else
5865         Store[x][y] = EL_EMPTY;
5866
5867       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5868           center_element == EL_AMOEBA_TO_DIAMOND)
5869         Store2[x][y] = element;
5870
5871       Tile[x][y] = EL_EXPLOSION;
5872       GfxElement[x][y] = artwork_element;
5873
5874       ExplodePhase[x][y] = 1;
5875       ExplodeDelay[x][y] = last_phase;
5876
5877       Stop[x][y] = TRUE;
5878     }
5879
5880     if (center_element == EL_YAMYAM)
5881       game.yamyam_content_nr =
5882         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5883
5884     return;
5885   }
5886
5887   if (Stop[ex][ey])
5888     return;
5889
5890   x = ex;
5891   y = ey;
5892
5893   if (phase == 1)
5894     GfxFrame[x][y] = 0;         // restart explosion animation
5895
5896   last_phase = ExplodeDelay[x][y];
5897
5898   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5899
5900   // this can happen if the player leaves an explosion just in time
5901   if (GfxElement[x][y] == EL_UNDEFINED)
5902     GfxElement[x][y] = EL_EMPTY;
5903
5904   border_element = Store2[x][y];
5905   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5906     border_element = StorePlayer[x][y];
5907
5908   if (phase == element_info[border_element].ignition_delay ||
5909       phase == last_phase)
5910   {
5911     boolean border_explosion = FALSE;
5912
5913     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5914         !PLAYER_EXPLOSION_PROTECTED(x, y))
5915     {
5916       KillPlayerUnlessExplosionProtected(x, y);
5917       border_explosion = TRUE;
5918     }
5919     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5920     {
5921       Tile[x][y] = Store2[x][y];
5922       Store2[x][y] = 0;
5923       Bang(x, y);
5924       border_explosion = TRUE;
5925     }
5926     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5927     {
5928       AmoebaToDiamond(x, y);
5929       Store2[x][y] = 0;
5930       border_explosion = TRUE;
5931     }
5932
5933     // if an element just explodes due to another explosion (chain-reaction),
5934     // do not immediately end the new explosion when it was the last frame of
5935     // the explosion (as it would be done in the following "if"-statement!)
5936     if (border_explosion && phase == last_phase)
5937       return;
5938   }
5939
5940   if (phase == last_phase)
5941   {
5942     int element;
5943
5944     element = Tile[x][y] = Store[x][y];
5945     Store[x][y] = Store2[x][y] = 0;
5946     GfxElement[x][y] = EL_UNDEFINED;
5947
5948     // player can escape from explosions and might therefore be still alive
5949     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5950         element <= EL_PLAYER_IS_EXPLODING_4)
5951     {
5952       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5953       int explosion_element = EL_PLAYER_1 + player_nr;
5954       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5955       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5956
5957       if (level.use_explosion_element[player_nr])
5958         explosion_element = level.explosion_element[player_nr];
5959
5960       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5961                     element_info[explosion_element].content.e[xx][yy]);
5962     }
5963
5964     // restore probably existing indestructible background element
5965     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5966       element = Tile[x][y] = Back[x][y];
5967     Back[x][y] = 0;
5968
5969     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5970     GfxDir[x][y] = MV_NONE;
5971     ChangeDelay[x][y] = 0;
5972     ChangePage[x][y] = -1;
5973
5974     CustomValue[x][y] = 0;
5975
5976     InitField_WithBug2(x, y, FALSE);
5977
5978     TEST_DrawLevelField(x, y);
5979
5980     TestIfElementTouchesCustomElement(x, y);
5981
5982     if (GFX_CRUMBLED(element))
5983       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5984
5985     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5986       StorePlayer[x][y] = 0;
5987
5988     if (ELEM_IS_PLAYER(element))
5989       RelocatePlayer(x, y, element);
5990   }
5991   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5992   {
5993     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5994     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5995
5996     if (phase == delay)
5997       TEST_DrawLevelFieldCrumbled(x, y);
5998
5999     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6000     {
6001       DrawLevelElement(x, y, Back[x][y]);
6002       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6003     }
6004     else if (IS_WALKABLE_UNDER(Back[x][y]))
6005     {
6006       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6007       DrawLevelElementThruMask(x, y, Back[x][y]);
6008     }
6009     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6010       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6011   }
6012 }
6013
6014 static void DynaExplode(int ex, int ey)
6015 {
6016   int i, j;
6017   int dynabomb_element = Tile[ex][ey];
6018   int dynabomb_size = 1;
6019   boolean dynabomb_xl = FALSE;
6020   struct PlayerInfo *player;
6021   static int xy[4][2] =
6022   {
6023     { 0, -1 },
6024     { -1, 0 },
6025     { +1, 0 },
6026     { 0, +1 }
6027   };
6028
6029   if (IS_ACTIVE_BOMB(dynabomb_element))
6030   {
6031     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6032     dynabomb_size = player->dynabomb_size;
6033     dynabomb_xl = player->dynabomb_xl;
6034     player->dynabombs_left++;
6035   }
6036
6037   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6038
6039   for (i = 0; i < NUM_DIRECTIONS; i++)
6040   {
6041     for (j = 1; j <= dynabomb_size; j++)
6042     {
6043       int x = ex + j * xy[i][0];
6044       int y = ey + j * xy[i][1];
6045       int element;
6046
6047       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6048         break;
6049
6050       element = Tile[x][y];
6051
6052       // do not restart explosions of fields with active bombs
6053       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6054         continue;
6055
6056       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6057
6058       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6059           !IS_DIGGABLE(element) && !dynabomb_xl)
6060         break;
6061     }
6062   }
6063 }
6064
6065 void Bang(int x, int y)
6066 {
6067   int element = MovingOrBlocked2Element(x, y);
6068   int explosion_type = EX_TYPE_NORMAL;
6069
6070   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6071   {
6072     struct PlayerInfo *player = PLAYERINFO(x, y);
6073
6074     element = Tile[x][y] = player->initial_element;
6075
6076     if (level.use_explosion_element[player->index_nr])
6077     {
6078       int explosion_element = level.explosion_element[player->index_nr];
6079
6080       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6081         explosion_type = EX_TYPE_CROSS;
6082       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6083         explosion_type = EX_TYPE_CENTER;
6084     }
6085   }
6086
6087   switch (element)
6088   {
6089     case EL_BUG:
6090     case EL_SPACESHIP:
6091     case EL_BD_BUTTERFLY:
6092     case EL_BD_FIREFLY:
6093     case EL_YAMYAM:
6094     case EL_DARK_YAMYAM:
6095     case EL_ROBOT:
6096     case EL_PACMAN:
6097     case EL_MOLE:
6098       RaiseScoreElement(element);
6099       break;
6100
6101     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6102     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6103     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6104     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6105     case EL_DYNABOMB_INCREASE_NUMBER:
6106     case EL_DYNABOMB_INCREASE_SIZE:
6107     case EL_DYNABOMB_INCREASE_POWER:
6108       explosion_type = EX_TYPE_DYNA;
6109       break;
6110
6111     case EL_DC_LANDMINE:
6112       explosion_type = EX_TYPE_CENTER;
6113       break;
6114
6115     case EL_PENGUIN:
6116     case EL_LAMP:
6117     case EL_LAMP_ACTIVE:
6118     case EL_AMOEBA_TO_DIAMOND:
6119       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6120         explosion_type = EX_TYPE_CENTER;
6121       break;
6122
6123     default:
6124       if (element_info[element].explosion_type == EXPLODES_CROSS)
6125         explosion_type = EX_TYPE_CROSS;
6126       else if (element_info[element].explosion_type == EXPLODES_1X1)
6127         explosion_type = EX_TYPE_CENTER;
6128       break;
6129   }
6130
6131   if (explosion_type == EX_TYPE_DYNA)
6132     DynaExplode(x, y);
6133   else
6134     Explode(x, y, EX_PHASE_START, explosion_type);
6135
6136   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6137 }
6138
6139 static void SplashAcid(int x, int y)
6140 {
6141   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6142       (!IN_LEV_FIELD(x - 1, y - 2) ||
6143        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6144     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6145
6146   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6147       (!IN_LEV_FIELD(x + 1, y - 2) ||
6148        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6149     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6150
6151   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6152 }
6153
6154 static void InitBeltMovement(void)
6155 {
6156   static int belt_base_element[4] =
6157   {
6158     EL_CONVEYOR_BELT_1_LEFT,
6159     EL_CONVEYOR_BELT_2_LEFT,
6160     EL_CONVEYOR_BELT_3_LEFT,
6161     EL_CONVEYOR_BELT_4_LEFT
6162   };
6163   static int belt_base_active_element[4] =
6164   {
6165     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6166     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6167     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6168     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6169   };
6170
6171   int x, y, i, j;
6172
6173   // set frame order for belt animation graphic according to belt direction
6174   for (i = 0; i < NUM_BELTS; i++)
6175   {
6176     int belt_nr = i;
6177
6178     for (j = 0; j < NUM_BELT_PARTS; j++)
6179     {
6180       int element = belt_base_active_element[belt_nr] + j;
6181       int graphic_1 = el2img(element);
6182       int graphic_2 = el2panelimg(element);
6183
6184       if (game.belt_dir[i] == MV_LEFT)
6185       {
6186         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6187         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6188       }
6189       else
6190       {
6191         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6192         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6193       }
6194     }
6195   }
6196
6197   SCAN_PLAYFIELD(x, y)
6198   {
6199     int element = Tile[x][y];
6200
6201     for (i = 0; i < NUM_BELTS; i++)
6202     {
6203       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6204       {
6205         int e_belt_nr = getBeltNrFromBeltElement(element);
6206         int belt_nr = i;
6207
6208         if (e_belt_nr == belt_nr)
6209         {
6210           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6211
6212           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6213         }
6214       }
6215     }
6216   }
6217 }
6218
6219 static void ToggleBeltSwitch(int x, int y)
6220 {
6221   static int belt_base_element[4] =
6222   {
6223     EL_CONVEYOR_BELT_1_LEFT,
6224     EL_CONVEYOR_BELT_2_LEFT,
6225     EL_CONVEYOR_BELT_3_LEFT,
6226     EL_CONVEYOR_BELT_4_LEFT
6227   };
6228   static int belt_base_active_element[4] =
6229   {
6230     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6231     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6232     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6233     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6234   };
6235   static int belt_base_switch_element[4] =
6236   {
6237     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6238     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6239     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6240     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6241   };
6242   static int belt_move_dir[4] =
6243   {
6244     MV_LEFT,
6245     MV_NONE,
6246     MV_RIGHT,
6247     MV_NONE,
6248   };
6249
6250   int element = Tile[x][y];
6251   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6252   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6253   int belt_dir = belt_move_dir[belt_dir_nr];
6254   int xx, yy, i;
6255
6256   if (!IS_BELT_SWITCH(element))
6257     return;
6258
6259   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6260   game.belt_dir[belt_nr] = belt_dir;
6261
6262   if (belt_dir_nr == 3)
6263     belt_dir_nr = 1;
6264
6265   // set frame order for belt animation graphic according to belt direction
6266   for (i = 0; i < NUM_BELT_PARTS; i++)
6267   {
6268     int element = belt_base_active_element[belt_nr] + i;
6269     int graphic_1 = el2img(element);
6270     int graphic_2 = el2panelimg(element);
6271
6272     if (belt_dir == MV_LEFT)
6273     {
6274       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6275       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6276     }
6277     else
6278     {
6279       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6280       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6281     }
6282   }
6283
6284   SCAN_PLAYFIELD(xx, yy)
6285   {
6286     int element = Tile[xx][yy];
6287
6288     if (IS_BELT_SWITCH(element))
6289     {
6290       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6291
6292       if (e_belt_nr == belt_nr)
6293       {
6294         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6295         TEST_DrawLevelField(xx, yy);
6296       }
6297     }
6298     else if (IS_BELT(element) && belt_dir != MV_NONE)
6299     {
6300       int e_belt_nr = getBeltNrFromBeltElement(element);
6301
6302       if (e_belt_nr == belt_nr)
6303       {
6304         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6305
6306         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6307         TEST_DrawLevelField(xx, yy);
6308       }
6309     }
6310     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6311     {
6312       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6313
6314       if (e_belt_nr == belt_nr)
6315       {
6316         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6317
6318         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6319         TEST_DrawLevelField(xx, yy);
6320       }
6321     }
6322   }
6323 }
6324
6325 static void ToggleSwitchgateSwitch(int x, int y)
6326 {
6327   int xx, yy;
6328
6329   game.switchgate_pos = !game.switchgate_pos;
6330
6331   SCAN_PLAYFIELD(xx, yy)
6332   {
6333     int element = Tile[xx][yy];
6334
6335     if (element == EL_SWITCHGATE_SWITCH_UP)
6336     {
6337       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6338       TEST_DrawLevelField(xx, yy);
6339     }
6340     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6341     {
6342       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6343       TEST_DrawLevelField(xx, yy);
6344     }
6345     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6346     {
6347       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6348       TEST_DrawLevelField(xx, yy);
6349     }
6350     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6351     {
6352       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6353       TEST_DrawLevelField(xx, yy);
6354     }
6355     else if (element == EL_SWITCHGATE_OPEN ||
6356              element == EL_SWITCHGATE_OPENING)
6357     {
6358       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6359
6360       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6361     }
6362     else if (element == EL_SWITCHGATE_CLOSED ||
6363              element == EL_SWITCHGATE_CLOSING)
6364     {
6365       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6366
6367       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6368     }
6369   }
6370 }
6371
6372 static int getInvisibleActiveFromInvisibleElement(int element)
6373 {
6374   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6375           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6376           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6377           element);
6378 }
6379
6380 static int getInvisibleFromInvisibleActiveElement(int element)
6381 {
6382   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6383           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6384           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6385           element);
6386 }
6387
6388 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6389 {
6390   int x, y;
6391
6392   SCAN_PLAYFIELD(x, y)
6393   {
6394     int element = Tile[x][y];
6395
6396     if (element == EL_LIGHT_SWITCH &&
6397         game.light_time_left > 0)
6398     {
6399       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6400       TEST_DrawLevelField(x, y);
6401     }
6402     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6403              game.light_time_left == 0)
6404     {
6405       Tile[x][y] = EL_LIGHT_SWITCH;
6406       TEST_DrawLevelField(x, y);
6407     }
6408     else if (element == EL_EMC_DRIPPER &&
6409              game.light_time_left > 0)
6410     {
6411       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6412       TEST_DrawLevelField(x, y);
6413     }
6414     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6415              game.light_time_left == 0)
6416     {
6417       Tile[x][y] = EL_EMC_DRIPPER;
6418       TEST_DrawLevelField(x, y);
6419     }
6420     else if (element == EL_INVISIBLE_STEELWALL ||
6421              element == EL_INVISIBLE_WALL ||
6422              element == EL_INVISIBLE_SAND)
6423     {
6424       if (game.light_time_left > 0)
6425         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6426
6427       TEST_DrawLevelField(x, y);
6428
6429       // uncrumble neighbour fields, if needed
6430       if (element == EL_INVISIBLE_SAND)
6431         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6432     }
6433     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6434              element == EL_INVISIBLE_WALL_ACTIVE ||
6435              element == EL_INVISIBLE_SAND_ACTIVE)
6436     {
6437       if (game.light_time_left == 0)
6438         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6439
6440       TEST_DrawLevelField(x, y);
6441
6442       // re-crumble neighbour fields, if needed
6443       if (element == EL_INVISIBLE_SAND)
6444         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6445     }
6446   }
6447 }
6448
6449 static void RedrawAllInvisibleElementsForLenses(void)
6450 {
6451   int x, y;
6452
6453   SCAN_PLAYFIELD(x, y)
6454   {
6455     int element = Tile[x][y];
6456
6457     if (element == EL_EMC_DRIPPER &&
6458         game.lenses_time_left > 0)
6459     {
6460       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6461       TEST_DrawLevelField(x, y);
6462     }
6463     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6464              game.lenses_time_left == 0)
6465     {
6466       Tile[x][y] = EL_EMC_DRIPPER;
6467       TEST_DrawLevelField(x, y);
6468     }
6469     else if (element == EL_INVISIBLE_STEELWALL ||
6470              element == EL_INVISIBLE_WALL ||
6471              element == EL_INVISIBLE_SAND)
6472     {
6473       if (game.lenses_time_left > 0)
6474         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6475
6476       TEST_DrawLevelField(x, y);
6477
6478       // uncrumble neighbour fields, if needed
6479       if (element == EL_INVISIBLE_SAND)
6480         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6481     }
6482     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6483              element == EL_INVISIBLE_WALL_ACTIVE ||
6484              element == EL_INVISIBLE_SAND_ACTIVE)
6485     {
6486       if (game.lenses_time_left == 0)
6487         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6488
6489       TEST_DrawLevelField(x, y);
6490
6491       // re-crumble neighbour fields, if needed
6492       if (element == EL_INVISIBLE_SAND)
6493         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6494     }
6495   }
6496 }
6497
6498 static void RedrawAllInvisibleElementsForMagnifier(void)
6499 {
6500   int x, y;
6501
6502   SCAN_PLAYFIELD(x, y)
6503   {
6504     int element = Tile[x][y];
6505
6506     if (element == EL_EMC_FAKE_GRASS &&
6507         game.magnify_time_left > 0)
6508     {
6509       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6510       TEST_DrawLevelField(x, y);
6511     }
6512     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6513              game.magnify_time_left == 0)
6514     {
6515       Tile[x][y] = EL_EMC_FAKE_GRASS;
6516       TEST_DrawLevelField(x, y);
6517     }
6518     else if (IS_GATE_GRAY(element) &&
6519              game.magnify_time_left > 0)
6520     {
6521       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6522                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6523                     IS_EM_GATE_GRAY(element) ?
6524                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6525                     IS_EMC_GATE_GRAY(element) ?
6526                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6527                     IS_DC_GATE_GRAY(element) ?
6528                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6529                     element);
6530       TEST_DrawLevelField(x, y);
6531     }
6532     else if (IS_GATE_GRAY_ACTIVE(element) &&
6533              game.magnify_time_left == 0)
6534     {
6535       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6536                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6537                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6538                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6539                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6540                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6541                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6542                     EL_DC_GATE_WHITE_GRAY :
6543                     element);
6544       TEST_DrawLevelField(x, y);
6545     }
6546   }
6547 }
6548
6549 static void ToggleLightSwitch(int x, int y)
6550 {
6551   int element = Tile[x][y];
6552
6553   game.light_time_left =
6554     (element == EL_LIGHT_SWITCH ?
6555      level.time_light * FRAMES_PER_SECOND : 0);
6556
6557   RedrawAllLightSwitchesAndInvisibleElements();
6558 }
6559
6560 static void ActivateTimegateSwitch(int x, int y)
6561 {
6562   int xx, yy;
6563
6564   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6565
6566   SCAN_PLAYFIELD(xx, yy)
6567   {
6568     int element = Tile[xx][yy];
6569
6570     if (element == EL_TIMEGATE_CLOSED ||
6571         element == EL_TIMEGATE_CLOSING)
6572     {
6573       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6574       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6575     }
6576
6577     /*
6578     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6579     {
6580       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6581       TEST_DrawLevelField(xx, yy);
6582     }
6583     */
6584
6585   }
6586
6587   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6588                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6589 }
6590
6591 static void Impact(int x, int y)
6592 {
6593   boolean last_line = (y == lev_fieldy - 1);
6594   boolean object_hit = FALSE;
6595   boolean impact = (last_line || object_hit);
6596   int element = Tile[x][y];
6597   int smashed = EL_STEELWALL;
6598
6599   if (!last_line)       // check if element below was hit
6600   {
6601     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6602       return;
6603
6604     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6605                                          MovDir[x][y + 1] != MV_DOWN ||
6606                                          MovPos[x][y + 1] <= TILEY / 2));
6607
6608     // do not smash moving elements that left the smashed field in time
6609     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6610         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6611       object_hit = FALSE;
6612
6613 #if USE_QUICKSAND_IMPACT_BUGFIX
6614     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6615     {
6616       RemoveMovingField(x, y + 1);
6617       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6618       Tile[x][y + 2] = EL_ROCK;
6619       TEST_DrawLevelField(x, y + 2);
6620
6621       object_hit = TRUE;
6622     }
6623
6624     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6625     {
6626       RemoveMovingField(x, y + 1);
6627       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6628       Tile[x][y + 2] = EL_ROCK;
6629       TEST_DrawLevelField(x, y + 2);
6630
6631       object_hit = TRUE;
6632     }
6633 #endif
6634
6635     if (object_hit)
6636       smashed = MovingOrBlocked2Element(x, y + 1);
6637
6638     impact = (last_line || object_hit);
6639   }
6640
6641   if (!last_line && smashed == EL_ACID) // element falls into acid
6642   {
6643     SplashAcid(x, y + 1);
6644     return;
6645   }
6646
6647   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6648   // only reset graphic animation if graphic really changes after impact
6649   if (impact &&
6650       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6651   {
6652     ResetGfxAnimation(x, y);
6653     TEST_DrawLevelField(x, y);
6654   }
6655
6656   if (impact && CAN_EXPLODE_IMPACT(element))
6657   {
6658     Bang(x, y);
6659     return;
6660   }
6661   else if (impact && element == EL_PEARL &&
6662            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6663   {
6664     ResetGfxAnimation(x, y);
6665
6666     Tile[x][y] = EL_PEARL_BREAKING;
6667     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6668     return;
6669   }
6670   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6671   {
6672     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6673
6674     return;
6675   }
6676
6677   if (impact && element == EL_AMOEBA_DROP)
6678   {
6679     if (object_hit && IS_PLAYER(x, y + 1))
6680       KillPlayerUnlessEnemyProtected(x, y + 1);
6681     else if (object_hit && smashed == EL_PENGUIN)
6682       Bang(x, y + 1);
6683     else
6684     {
6685       Tile[x][y] = EL_AMOEBA_GROWING;
6686       Store[x][y] = EL_AMOEBA_WET;
6687
6688       ResetRandomAnimationValue(x, y);
6689     }
6690     return;
6691   }
6692
6693   if (object_hit)               // check which object was hit
6694   {
6695     if ((CAN_PASS_MAGIC_WALL(element) && 
6696          (smashed == EL_MAGIC_WALL ||
6697           smashed == EL_BD_MAGIC_WALL)) ||
6698         (CAN_PASS_DC_MAGIC_WALL(element) &&
6699          smashed == EL_DC_MAGIC_WALL))
6700     {
6701       int xx, yy;
6702       int activated_magic_wall =
6703         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6704          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6705          EL_DC_MAGIC_WALL_ACTIVE);
6706
6707       // activate magic wall / mill
6708       SCAN_PLAYFIELD(xx, yy)
6709       {
6710         if (Tile[xx][yy] == smashed)
6711           Tile[xx][yy] = activated_magic_wall;
6712       }
6713
6714       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6715       game.magic_wall_active = TRUE;
6716
6717       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6718                             SND_MAGIC_WALL_ACTIVATING :
6719                             smashed == EL_BD_MAGIC_WALL ?
6720                             SND_BD_MAGIC_WALL_ACTIVATING :
6721                             SND_DC_MAGIC_WALL_ACTIVATING));
6722     }
6723
6724     if (IS_PLAYER(x, y + 1))
6725     {
6726       if (CAN_SMASH_PLAYER(element))
6727       {
6728         KillPlayerUnlessEnemyProtected(x, y + 1);
6729         return;
6730       }
6731     }
6732     else if (smashed == EL_PENGUIN)
6733     {
6734       if (CAN_SMASH_PLAYER(element))
6735       {
6736         Bang(x, y + 1);
6737         return;
6738       }
6739     }
6740     else if (element == EL_BD_DIAMOND)
6741     {
6742       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6743       {
6744         Bang(x, y + 1);
6745         return;
6746       }
6747     }
6748     else if (((element == EL_SP_INFOTRON ||
6749                element == EL_SP_ZONK) &&
6750               (smashed == EL_SP_SNIKSNAK ||
6751                smashed == EL_SP_ELECTRON ||
6752                smashed == EL_SP_DISK_ORANGE)) ||
6753              (element == EL_SP_INFOTRON &&
6754               smashed == EL_SP_DISK_YELLOW))
6755     {
6756       Bang(x, y + 1);
6757       return;
6758     }
6759     else if (CAN_SMASH_EVERYTHING(element))
6760     {
6761       if (IS_CLASSIC_ENEMY(smashed) ||
6762           CAN_EXPLODE_SMASHED(smashed))
6763       {
6764         Bang(x, y + 1);
6765         return;
6766       }
6767       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6768       {
6769         if (smashed == EL_LAMP ||
6770             smashed == EL_LAMP_ACTIVE)
6771         {
6772           Bang(x, y + 1);
6773           return;
6774         }
6775         else if (smashed == EL_NUT)
6776         {
6777           Tile[x][y + 1] = EL_NUT_BREAKING;
6778           PlayLevelSound(x, y, SND_NUT_BREAKING);
6779           RaiseScoreElement(EL_NUT);
6780           return;
6781         }
6782         else if (smashed == EL_PEARL)
6783         {
6784           ResetGfxAnimation(x, y);
6785
6786           Tile[x][y + 1] = EL_PEARL_BREAKING;
6787           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6788           return;
6789         }
6790         else if (smashed == EL_DIAMOND)
6791         {
6792           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6793           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6794           return;
6795         }
6796         else if (IS_BELT_SWITCH(smashed))
6797         {
6798           ToggleBeltSwitch(x, y + 1);
6799         }
6800         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6801                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6802                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6803                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6804         {
6805           ToggleSwitchgateSwitch(x, y + 1);
6806         }
6807         else if (smashed == EL_LIGHT_SWITCH ||
6808                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6809         {
6810           ToggleLightSwitch(x, y + 1);
6811         }
6812         else
6813         {
6814           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6815
6816           CheckElementChangeBySide(x, y + 1, smashed, element,
6817                                    CE_SWITCHED, CH_SIDE_TOP);
6818           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6819                                             CH_SIDE_TOP);
6820         }
6821       }
6822       else
6823       {
6824         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6825       }
6826     }
6827   }
6828
6829   // play sound of magic wall / mill
6830   if (!last_line &&
6831       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6832        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6833        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6834   {
6835     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6836       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6837     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6838       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6839     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6840       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6841
6842     return;
6843   }
6844
6845   // play sound of object that hits the ground
6846   if (last_line || object_hit)
6847     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6848 }
6849
6850 static void TurnRoundExt(int x, int y)
6851 {
6852   static struct
6853   {
6854     int dx, dy;
6855   } move_xy[] =
6856   {
6857     {  0,  0 },
6858     { -1,  0 },
6859     { +1,  0 },
6860     {  0,  0 },
6861     {  0, -1 },
6862     {  0,  0 }, { 0, 0 }, { 0, 0 },
6863     {  0, +1 }
6864   };
6865   static struct
6866   {
6867     int left, right, back;
6868   } turn[] =
6869   {
6870     { 0,        0,              0        },
6871     { MV_DOWN,  MV_UP,          MV_RIGHT },
6872     { MV_UP,    MV_DOWN,        MV_LEFT  },
6873     { 0,        0,              0        },
6874     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6875     { 0,        0,              0        },
6876     { 0,        0,              0        },
6877     { 0,        0,              0        },
6878     { MV_RIGHT, MV_LEFT,        MV_UP    }
6879   };
6880
6881   int element = Tile[x][y];
6882   int move_pattern = element_info[element].move_pattern;
6883
6884   int old_move_dir = MovDir[x][y];
6885   int left_dir  = turn[old_move_dir].left;
6886   int right_dir = turn[old_move_dir].right;
6887   int back_dir  = turn[old_move_dir].back;
6888
6889   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6890   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6891   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6892   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6893
6894   int left_x  = x + left_dx,  left_y  = y + left_dy;
6895   int right_x = x + right_dx, right_y = y + right_dy;
6896   int move_x  = x + move_dx,  move_y  = y + move_dy;
6897
6898   int xx, yy;
6899
6900   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6901   {
6902     TestIfBadThingTouchesOtherBadThing(x, y);
6903
6904     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6905       MovDir[x][y] = right_dir;
6906     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6907       MovDir[x][y] = left_dir;
6908
6909     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6910       MovDelay[x][y] = 9;
6911     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6912       MovDelay[x][y] = 1;
6913   }
6914   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6915   {
6916     TestIfBadThingTouchesOtherBadThing(x, y);
6917
6918     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6919       MovDir[x][y] = left_dir;
6920     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6921       MovDir[x][y] = right_dir;
6922
6923     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6924       MovDelay[x][y] = 9;
6925     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6926       MovDelay[x][y] = 1;
6927   }
6928   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6929   {
6930     TestIfBadThingTouchesOtherBadThing(x, y);
6931
6932     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6933       MovDir[x][y] = left_dir;
6934     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6935       MovDir[x][y] = right_dir;
6936
6937     if (MovDir[x][y] != old_move_dir)
6938       MovDelay[x][y] = 9;
6939   }
6940   else if (element == EL_YAMYAM)
6941   {
6942     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6943     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6944
6945     if (can_turn_left && can_turn_right)
6946       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6947     else if (can_turn_left)
6948       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6949     else if (can_turn_right)
6950       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6951     else
6952       MovDir[x][y] = back_dir;
6953
6954     MovDelay[x][y] = 16 + 16 * RND(3);
6955   }
6956   else if (element == EL_DARK_YAMYAM)
6957   {
6958     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6959                                                          left_x, left_y);
6960     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6961                                                          right_x, right_y);
6962
6963     if (can_turn_left && can_turn_right)
6964       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6965     else if (can_turn_left)
6966       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6967     else if (can_turn_right)
6968       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6969     else
6970       MovDir[x][y] = back_dir;
6971
6972     MovDelay[x][y] = 16 + 16 * RND(3);
6973   }
6974   else if (element == EL_PACMAN)
6975   {
6976     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6977     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6978
6979     if (can_turn_left && can_turn_right)
6980       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6981     else if (can_turn_left)
6982       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6983     else if (can_turn_right)
6984       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6985     else
6986       MovDir[x][y] = back_dir;
6987
6988     MovDelay[x][y] = 6 + RND(40);
6989   }
6990   else if (element == EL_PIG)
6991   {
6992     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6993     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6994     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6995     boolean should_turn_left, should_turn_right, should_move_on;
6996     int rnd_value = 24;
6997     int rnd = RND(rnd_value);
6998
6999     should_turn_left = (can_turn_left &&
7000                         (!can_move_on ||
7001                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7002                                                    y + back_dy + left_dy)));
7003     should_turn_right = (can_turn_right &&
7004                          (!can_move_on ||
7005                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7006                                                     y + back_dy + right_dy)));
7007     should_move_on = (can_move_on &&
7008                       (!can_turn_left ||
7009                        !can_turn_right ||
7010                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7011                                                  y + move_dy + left_dy) ||
7012                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7013                                                  y + move_dy + right_dy)));
7014
7015     if (should_turn_left || should_turn_right || should_move_on)
7016     {
7017       if (should_turn_left && should_turn_right && should_move_on)
7018         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7019                         rnd < 2 * rnd_value / 3 ? right_dir :
7020                         old_move_dir);
7021       else if (should_turn_left && should_turn_right)
7022         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7023       else if (should_turn_left && should_move_on)
7024         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7025       else if (should_turn_right && should_move_on)
7026         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7027       else if (should_turn_left)
7028         MovDir[x][y] = left_dir;
7029       else if (should_turn_right)
7030         MovDir[x][y] = right_dir;
7031       else if (should_move_on)
7032         MovDir[x][y] = old_move_dir;
7033     }
7034     else if (can_move_on && rnd > rnd_value / 8)
7035       MovDir[x][y] = old_move_dir;
7036     else if (can_turn_left && can_turn_right)
7037       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7038     else if (can_turn_left && rnd > rnd_value / 8)
7039       MovDir[x][y] = left_dir;
7040     else if (can_turn_right && rnd > rnd_value/8)
7041       MovDir[x][y] = right_dir;
7042     else
7043       MovDir[x][y] = back_dir;
7044
7045     xx = x + move_xy[MovDir[x][y]].dx;
7046     yy = y + move_xy[MovDir[x][y]].dy;
7047
7048     if (!IN_LEV_FIELD(xx, yy) ||
7049         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7050       MovDir[x][y] = old_move_dir;
7051
7052     MovDelay[x][y] = 0;
7053   }
7054   else if (element == EL_DRAGON)
7055   {
7056     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7057     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7058     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7059     int rnd_value = 24;
7060     int rnd = RND(rnd_value);
7061
7062     if (can_move_on && rnd > rnd_value / 8)
7063       MovDir[x][y] = old_move_dir;
7064     else if (can_turn_left && can_turn_right)
7065       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7066     else if (can_turn_left && rnd > rnd_value / 8)
7067       MovDir[x][y] = left_dir;
7068     else if (can_turn_right && rnd > rnd_value / 8)
7069       MovDir[x][y] = right_dir;
7070     else
7071       MovDir[x][y] = back_dir;
7072
7073     xx = x + move_xy[MovDir[x][y]].dx;
7074     yy = y + move_xy[MovDir[x][y]].dy;
7075
7076     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7077       MovDir[x][y] = old_move_dir;
7078
7079     MovDelay[x][y] = 0;
7080   }
7081   else if (element == EL_MOLE)
7082   {
7083     boolean can_move_on =
7084       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7085                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7086                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7087     if (!can_move_on)
7088     {
7089       boolean can_turn_left =
7090         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7091                               IS_AMOEBOID(Tile[left_x][left_y])));
7092
7093       boolean can_turn_right =
7094         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7095                               IS_AMOEBOID(Tile[right_x][right_y])));
7096
7097       if (can_turn_left && can_turn_right)
7098         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7099       else if (can_turn_left)
7100         MovDir[x][y] = left_dir;
7101       else
7102         MovDir[x][y] = right_dir;
7103     }
7104
7105     if (MovDir[x][y] != old_move_dir)
7106       MovDelay[x][y] = 9;
7107   }
7108   else if (element == EL_BALLOON)
7109   {
7110     MovDir[x][y] = game.wind_direction;
7111     MovDelay[x][y] = 0;
7112   }
7113   else if (element == EL_SPRING)
7114   {
7115     if (MovDir[x][y] & MV_HORIZONTAL)
7116     {
7117       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7118           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7119       {
7120         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7121         ResetGfxAnimation(move_x, move_y);
7122         TEST_DrawLevelField(move_x, move_y);
7123
7124         MovDir[x][y] = back_dir;
7125       }
7126       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7127                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7128         MovDir[x][y] = MV_NONE;
7129     }
7130
7131     MovDelay[x][y] = 0;
7132   }
7133   else if (element == EL_ROBOT ||
7134            element == EL_SATELLITE ||
7135            element == EL_PENGUIN ||
7136            element == EL_EMC_ANDROID)
7137   {
7138     int attr_x = -1, attr_y = -1;
7139
7140     if (game.all_players_gone)
7141     {
7142       attr_x = game.exit_x;
7143       attr_y = game.exit_y;
7144     }
7145     else
7146     {
7147       int i;
7148
7149       for (i = 0; i < MAX_PLAYERS; i++)
7150       {
7151         struct PlayerInfo *player = &stored_player[i];
7152         int jx = player->jx, jy = player->jy;
7153
7154         if (!player->active)
7155           continue;
7156
7157         if (attr_x == -1 ||
7158             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7159         {
7160           attr_x = jx;
7161           attr_y = jy;
7162         }
7163       }
7164     }
7165
7166     if (element == EL_ROBOT &&
7167         game.robot_wheel_x >= 0 &&
7168         game.robot_wheel_y >= 0 &&
7169         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7170          game.engine_version < VERSION_IDENT(3,1,0,0)))
7171     {
7172       attr_x = game.robot_wheel_x;
7173       attr_y = game.robot_wheel_y;
7174     }
7175
7176     if (element == EL_PENGUIN)
7177     {
7178       int i;
7179       static int xy[4][2] =
7180       {
7181         { 0, -1 },
7182         { -1, 0 },
7183         { +1, 0 },
7184         { 0, +1 }
7185       };
7186
7187       for (i = 0; i < NUM_DIRECTIONS; i++)
7188       {
7189         int ex = x + xy[i][0];
7190         int ey = y + xy[i][1];
7191
7192         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7193                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7194                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7195                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7196         {
7197           attr_x = ex;
7198           attr_y = ey;
7199           break;
7200         }
7201       }
7202     }
7203
7204     MovDir[x][y] = MV_NONE;
7205     if (attr_x < x)
7206       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7207     else if (attr_x > x)
7208       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7209     if (attr_y < y)
7210       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7211     else if (attr_y > y)
7212       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7213
7214     if (element == EL_ROBOT)
7215     {
7216       int newx, newy;
7217
7218       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7219         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7220       Moving2Blocked(x, y, &newx, &newy);
7221
7222       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7223         MovDelay[x][y] = 8 + 8 * !RND(3);
7224       else
7225         MovDelay[x][y] = 16;
7226     }
7227     else if (element == EL_PENGUIN)
7228     {
7229       int newx, newy;
7230
7231       MovDelay[x][y] = 1;
7232
7233       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7234       {
7235         boolean first_horiz = RND(2);
7236         int new_move_dir = MovDir[x][y];
7237
7238         MovDir[x][y] =
7239           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7240         Moving2Blocked(x, y, &newx, &newy);
7241
7242         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7243           return;
7244
7245         MovDir[x][y] =
7246           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7247         Moving2Blocked(x, y, &newx, &newy);
7248
7249         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7250           return;
7251
7252         MovDir[x][y] = old_move_dir;
7253         return;
7254       }
7255     }
7256     else if (element == EL_SATELLITE)
7257     {
7258       int newx, newy;
7259
7260       MovDelay[x][y] = 1;
7261
7262       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7263       {
7264         boolean first_horiz = RND(2);
7265         int new_move_dir = MovDir[x][y];
7266
7267         MovDir[x][y] =
7268           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7269         Moving2Blocked(x, y, &newx, &newy);
7270
7271         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7272           return;
7273
7274         MovDir[x][y] =
7275           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7276         Moving2Blocked(x, y, &newx, &newy);
7277
7278         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7279           return;
7280
7281         MovDir[x][y] = old_move_dir;
7282         return;
7283       }
7284     }
7285     else if (element == EL_EMC_ANDROID)
7286     {
7287       static int check_pos[16] =
7288       {
7289         -1,             //  0 => (invalid)
7290         7,              //  1 => MV_LEFT
7291         3,              //  2 => MV_RIGHT
7292         -1,             //  3 => (invalid)
7293         1,              //  4 =>            MV_UP
7294         0,              //  5 => MV_LEFT  | MV_UP
7295         2,              //  6 => MV_RIGHT | MV_UP
7296         -1,             //  7 => (invalid)
7297         5,              //  8 =>            MV_DOWN
7298         6,              //  9 => MV_LEFT  | MV_DOWN
7299         4,              // 10 => MV_RIGHT | MV_DOWN
7300         -1,             // 11 => (invalid)
7301         -1,             // 12 => (invalid)
7302         -1,             // 13 => (invalid)
7303         -1,             // 14 => (invalid)
7304         -1,             // 15 => (invalid)
7305       };
7306       static struct
7307       {
7308         int dx, dy;
7309         int dir;
7310       } check_xy[8] =
7311       {
7312         { -1, -1,       MV_LEFT  | MV_UP   },
7313         {  0, -1,                  MV_UP   },
7314         { +1, -1,       MV_RIGHT | MV_UP   },
7315         { +1,  0,       MV_RIGHT           },
7316         { +1, +1,       MV_RIGHT | MV_DOWN },
7317         {  0, +1,                  MV_DOWN },
7318         { -1, +1,       MV_LEFT  | MV_DOWN },
7319         { -1,  0,       MV_LEFT            },
7320       };
7321       int start_pos, check_order;
7322       boolean can_clone = FALSE;
7323       int i;
7324
7325       // check if there is any free field around current position
7326       for (i = 0; i < 8; i++)
7327       {
7328         int newx = x + check_xy[i].dx;
7329         int newy = y + check_xy[i].dy;
7330
7331         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7332         {
7333           can_clone = TRUE;
7334
7335           break;
7336         }
7337       }
7338
7339       if (can_clone)            // randomly find an element to clone
7340       {
7341         can_clone = FALSE;
7342
7343         start_pos = check_pos[RND(8)];
7344         check_order = (RND(2) ? -1 : +1);
7345
7346         for (i = 0; i < 8; i++)
7347         {
7348           int pos_raw = start_pos + i * check_order;
7349           int pos = (pos_raw + 8) % 8;
7350           int newx = x + check_xy[pos].dx;
7351           int newy = y + check_xy[pos].dy;
7352
7353           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7354           {
7355             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7356             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7357
7358             Store[x][y] = Tile[newx][newy];
7359
7360             can_clone = TRUE;
7361
7362             break;
7363           }
7364         }
7365       }
7366
7367       if (can_clone)            // randomly find a direction to move
7368       {
7369         can_clone = FALSE;
7370
7371         start_pos = check_pos[RND(8)];
7372         check_order = (RND(2) ? -1 : +1);
7373
7374         for (i = 0; i < 8; i++)
7375         {
7376           int pos_raw = start_pos + i * check_order;
7377           int pos = (pos_raw + 8) % 8;
7378           int newx = x + check_xy[pos].dx;
7379           int newy = y + check_xy[pos].dy;
7380           int new_move_dir = check_xy[pos].dir;
7381
7382           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7383           {
7384             MovDir[x][y] = new_move_dir;
7385             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7386
7387             can_clone = TRUE;
7388
7389             break;
7390           }
7391         }
7392       }
7393
7394       if (can_clone)            // cloning and moving successful
7395         return;
7396
7397       // cannot clone -- try to move towards player
7398
7399       start_pos = check_pos[MovDir[x][y] & 0x0f];
7400       check_order = (RND(2) ? -1 : +1);
7401
7402       for (i = 0; i < 3; i++)
7403       {
7404         // first check start_pos, then previous/next or (next/previous) pos
7405         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7406         int pos = (pos_raw + 8) % 8;
7407         int newx = x + check_xy[pos].dx;
7408         int newy = y + check_xy[pos].dy;
7409         int new_move_dir = check_xy[pos].dir;
7410
7411         if (IS_PLAYER(newx, newy))
7412           break;
7413
7414         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7415         {
7416           MovDir[x][y] = new_move_dir;
7417           MovDelay[x][y] = level.android_move_time * 8 + 1;
7418
7419           break;
7420         }
7421       }
7422     }
7423   }
7424   else if (move_pattern == MV_TURNING_LEFT ||
7425            move_pattern == MV_TURNING_RIGHT ||
7426            move_pattern == MV_TURNING_LEFT_RIGHT ||
7427            move_pattern == MV_TURNING_RIGHT_LEFT ||
7428            move_pattern == MV_TURNING_RANDOM ||
7429            move_pattern == MV_ALL_DIRECTIONS)
7430   {
7431     boolean can_turn_left =
7432       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7433     boolean can_turn_right =
7434       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7435
7436     if (element_info[element].move_stepsize == 0)       // "not moving"
7437       return;
7438
7439     if (move_pattern == MV_TURNING_LEFT)
7440       MovDir[x][y] = left_dir;
7441     else if (move_pattern == MV_TURNING_RIGHT)
7442       MovDir[x][y] = right_dir;
7443     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7444       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7445     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7446       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7447     else if (move_pattern == MV_TURNING_RANDOM)
7448       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7449                       can_turn_right && !can_turn_left ? right_dir :
7450                       RND(2) ? left_dir : right_dir);
7451     else if (can_turn_left && can_turn_right)
7452       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7453     else if (can_turn_left)
7454       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7455     else if (can_turn_right)
7456       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7457     else
7458       MovDir[x][y] = back_dir;
7459
7460     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7461   }
7462   else if (move_pattern == MV_HORIZONTAL ||
7463            move_pattern == MV_VERTICAL)
7464   {
7465     if (move_pattern & old_move_dir)
7466       MovDir[x][y] = back_dir;
7467     else if (move_pattern == MV_HORIZONTAL)
7468       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7469     else if (move_pattern == MV_VERTICAL)
7470       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7471
7472     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7473   }
7474   else if (move_pattern & MV_ANY_DIRECTION)
7475   {
7476     MovDir[x][y] = move_pattern;
7477     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7478   }
7479   else if (move_pattern & MV_WIND_DIRECTION)
7480   {
7481     MovDir[x][y] = game.wind_direction;
7482     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7483   }
7484   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7485   {
7486     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7487       MovDir[x][y] = left_dir;
7488     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7489       MovDir[x][y] = right_dir;
7490
7491     if (MovDir[x][y] != old_move_dir)
7492       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7493   }
7494   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7495   {
7496     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7497       MovDir[x][y] = right_dir;
7498     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7499       MovDir[x][y] = left_dir;
7500
7501     if (MovDir[x][y] != old_move_dir)
7502       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7503   }
7504   else if (move_pattern == MV_TOWARDS_PLAYER ||
7505            move_pattern == MV_AWAY_FROM_PLAYER)
7506   {
7507     int attr_x = -1, attr_y = -1;
7508     int newx, newy;
7509     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7510
7511     if (game.all_players_gone)
7512     {
7513       attr_x = game.exit_x;
7514       attr_y = game.exit_y;
7515     }
7516     else
7517     {
7518       int i;
7519
7520       for (i = 0; i < MAX_PLAYERS; i++)
7521       {
7522         struct PlayerInfo *player = &stored_player[i];
7523         int jx = player->jx, jy = player->jy;
7524
7525         if (!player->active)
7526           continue;
7527
7528         if (attr_x == -1 ||
7529             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7530         {
7531           attr_x = jx;
7532           attr_y = jy;
7533         }
7534       }
7535     }
7536
7537     MovDir[x][y] = MV_NONE;
7538     if (attr_x < x)
7539       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7540     else if (attr_x > x)
7541       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7542     if (attr_y < y)
7543       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7544     else if (attr_y > y)
7545       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7546
7547     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7548
7549     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7550     {
7551       boolean first_horiz = RND(2);
7552       int new_move_dir = MovDir[x][y];
7553
7554       if (element_info[element].move_stepsize == 0)     // "not moving"
7555       {
7556         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7557         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7558
7559         return;
7560       }
7561
7562       MovDir[x][y] =
7563         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7564       Moving2Blocked(x, y, &newx, &newy);
7565
7566       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7567         return;
7568
7569       MovDir[x][y] =
7570         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7571       Moving2Blocked(x, y, &newx, &newy);
7572
7573       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7574         return;
7575
7576       MovDir[x][y] = old_move_dir;
7577     }
7578   }
7579   else if (move_pattern == MV_WHEN_PUSHED ||
7580            move_pattern == MV_WHEN_DROPPED)
7581   {
7582     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7583       MovDir[x][y] = MV_NONE;
7584
7585     MovDelay[x][y] = 0;
7586   }
7587   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7588   {
7589     static int test_xy[7][2] =
7590     {
7591       { 0, -1 },
7592       { -1, 0 },
7593       { +1, 0 },
7594       { 0, +1 },
7595       { 0, -1 },
7596       { -1, 0 },
7597       { +1, 0 },
7598     };
7599     static int test_dir[7] =
7600     {
7601       MV_UP,
7602       MV_LEFT,
7603       MV_RIGHT,
7604       MV_DOWN,
7605       MV_UP,
7606       MV_LEFT,
7607       MV_RIGHT,
7608     };
7609     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7610     int move_preference = -1000000;     // start with very low preference
7611     int new_move_dir = MV_NONE;
7612     int start_test = RND(4);
7613     int i;
7614
7615     for (i = 0; i < NUM_DIRECTIONS; i++)
7616     {
7617       int move_dir = test_dir[start_test + i];
7618       int move_dir_preference;
7619
7620       xx = x + test_xy[start_test + i][0];
7621       yy = y + test_xy[start_test + i][1];
7622
7623       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7624           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7625       {
7626         new_move_dir = move_dir;
7627
7628         break;
7629       }
7630
7631       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7632         continue;
7633
7634       move_dir_preference = -1 * RunnerVisit[xx][yy];
7635       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7636         move_dir_preference = PlayerVisit[xx][yy];
7637
7638       if (move_dir_preference > move_preference)
7639       {
7640         // prefer field that has not been visited for the longest time
7641         move_preference = move_dir_preference;
7642         new_move_dir = move_dir;
7643       }
7644       else if (move_dir_preference == move_preference &&
7645                move_dir == old_move_dir)
7646       {
7647         // prefer last direction when all directions are preferred equally
7648         move_preference = move_dir_preference;
7649         new_move_dir = move_dir;
7650       }
7651     }
7652
7653     MovDir[x][y] = new_move_dir;
7654     if (old_move_dir != new_move_dir)
7655       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7656   }
7657 }
7658
7659 static void TurnRound(int x, int y)
7660 {
7661   int direction = MovDir[x][y];
7662
7663   TurnRoundExt(x, y);
7664
7665   GfxDir[x][y] = MovDir[x][y];
7666
7667   if (direction != MovDir[x][y])
7668     GfxFrame[x][y] = 0;
7669
7670   if (MovDelay[x][y])
7671     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7672
7673   ResetGfxFrame(x, y);
7674 }
7675
7676 static boolean JustBeingPushed(int x, int y)
7677 {
7678   int i;
7679
7680   for (i = 0; i < MAX_PLAYERS; i++)
7681   {
7682     struct PlayerInfo *player = &stored_player[i];
7683
7684     if (player->active && player->is_pushing && player->MovPos)
7685     {
7686       int next_jx = player->jx + (player->jx - player->last_jx);
7687       int next_jy = player->jy + (player->jy - player->last_jy);
7688
7689       if (x == next_jx && y == next_jy)
7690         return TRUE;
7691     }
7692   }
7693
7694   return FALSE;
7695 }
7696
7697 static void StartMoving(int x, int y)
7698 {
7699   boolean started_moving = FALSE;       // some elements can fall _and_ move
7700   int element = Tile[x][y];
7701
7702   if (Stop[x][y])
7703     return;
7704
7705   if (MovDelay[x][y] == 0)
7706     GfxAction[x][y] = ACTION_DEFAULT;
7707
7708   if (CAN_FALL(element) && y < lev_fieldy - 1)
7709   {
7710     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7711         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7712       if (JustBeingPushed(x, y))
7713         return;
7714
7715     if (element == EL_QUICKSAND_FULL)
7716     {
7717       if (IS_FREE(x, y + 1))
7718       {
7719         InitMovingField(x, y, MV_DOWN);
7720         started_moving = TRUE;
7721
7722         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7723 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7724         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7725           Store[x][y] = EL_ROCK;
7726 #else
7727         Store[x][y] = EL_ROCK;
7728 #endif
7729
7730         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7731       }
7732       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7733       {
7734         if (!MovDelay[x][y])
7735         {
7736           MovDelay[x][y] = TILEY + 1;
7737
7738           ResetGfxAnimation(x, y);
7739           ResetGfxAnimation(x, y + 1);
7740         }
7741
7742         if (MovDelay[x][y])
7743         {
7744           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7745           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7746
7747           MovDelay[x][y]--;
7748           if (MovDelay[x][y])
7749             return;
7750         }
7751
7752         Tile[x][y] = EL_QUICKSAND_EMPTY;
7753         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7754         Store[x][y + 1] = Store[x][y];
7755         Store[x][y] = 0;
7756
7757         PlayLevelSoundAction(x, y, ACTION_FILLING);
7758       }
7759       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7760       {
7761         if (!MovDelay[x][y])
7762         {
7763           MovDelay[x][y] = TILEY + 1;
7764
7765           ResetGfxAnimation(x, y);
7766           ResetGfxAnimation(x, y + 1);
7767         }
7768
7769         if (MovDelay[x][y])
7770         {
7771           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7772           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7773
7774           MovDelay[x][y]--;
7775           if (MovDelay[x][y])
7776             return;
7777         }
7778
7779         Tile[x][y] = EL_QUICKSAND_EMPTY;
7780         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7781         Store[x][y + 1] = Store[x][y];
7782         Store[x][y] = 0;
7783
7784         PlayLevelSoundAction(x, y, ACTION_FILLING);
7785       }
7786     }
7787     else if (element == EL_QUICKSAND_FAST_FULL)
7788     {
7789       if (IS_FREE(x, y + 1))
7790       {
7791         InitMovingField(x, y, MV_DOWN);
7792         started_moving = TRUE;
7793
7794         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7795 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7796         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7797           Store[x][y] = EL_ROCK;
7798 #else
7799         Store[x][y] = EL_ROCK;
7800 #endif
7801
7802         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7803       }
7804       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7805       {
7806         if (!MovDelay[x][y])
7807         {
7808           MovDelay[x][y] = TILEY + 1;
7809
7810           ResetGfxAnimation(x, y);
7811           ResetGfxAnimation(x, y + 1);
7812         }
7813
7814         if (MovDelay[x][y])
7815         {
7816           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7817           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7818
7819           MovDelay[x][y]--;
7820           if (MovDelay[x][y])
7821             return;
7822         }
7823
7824         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7825         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7826         Store[x][y + 1] = Store[x][y];
7827         Store[x][y] = 0;
7828
7829         PlayLevelSoundAction(x, y, ACTION_FILLING);
7830       }
7831       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7832       {
7833         if (!MovDelay[x][y])
7834         {
7835           MovDelay[x][y] = TILEY + 1;
7836
7837           ResetGfxAnimation(x, y);
7838           ResetGfxAnimation(x, y + 1);
7839         }
7840
7841         if (MovDelay[x][y])
7842         {
7843           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7844           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7845
7846           MovDelay[x][y]--;
7847           if (MovDelay[x][y])
7848             return;
7849         }
7850
7851         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7852         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7853         Store[x][y + 1] = Store[x][y];
7854         Store[x][y] = 0;
7855
7856         PlayLevelSoundAction(x, y, ACTION_FILLING);
7857       }
7858     }
7859     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7860              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7861     {
7862       InitMovingField(x, y, MV_DOWN);
7863       started_moving = TRUE;
7864
7865       Tile[x][y] = EL_QUICKSAND_FILLING;
7866       Store[x][y] = element;
7867
7868       PlayLevelSoundAction(x, y, ACTION_FILLING);
7869     }
7870     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7871              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7872     {
7873       InitMovingField(x, y, MV_DOWN);
7874       started_moving = TRUE;
7875
7876       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7877       Store[x][y] = element;
7878
7879       PlayLevelSoundAction(x, y, ACTION_FILLING);
7880     }
7881     else if (element == EL_MAGIC_WALL_FULL)
7882     {
7883       if (IS_FREE(x, y + 1))
7884       {
7885         InitMovingField(x, y, MV_DOWN);
7886         started_moving = TRUE;
7887
7888         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7889         Store[x][y] = EL_CHANGED(Store[x][y]);
7890       }
7891       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7892       {
7893         if (!MovDelay[x][y])
7894           MovDelay[x][y] = TILEY / 4 + 1;
7895
7896         if (MovDelay[x][y])
7897         {
7898           MovDelay[x][y]--;
7899           if (MovDelay[x][y])
7900             return;
7901         }
7902
7903         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7904         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7905         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7906         Store[x][y] = 0;
7907       }
7908     }
7909     else if (element == EL_BD_MAGIC_WALL_FULL)
7910     {
7911       if (IS_FREE(x, y + 1))
7912       {
7913         InitMovingField(x, y, MV_DOWN);
7914         started_moving = TRUE;
7915
7916         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7917         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7918       }
7919       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7920       {
7921         if (!MovDelay[x][y])
7922           MovDelay[x][y] = TILEY / 4 + 1;
7923
7924         if (MovDelay[x][y])
7925         {
7926           MovDelay[x][y]--;
7927           if (MovDelay[x][y])
7928             return;
7929         }
7930
7931         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7932         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7933         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7934         Store[x][y] = 0;
7935       }
7936     }
7937     else if (element == EL_DC_MAGIC_WALL_FULL)
7938     {
7939       if (IS_FREE(x, y + 1))
7940       {
7941         InitMovingField(x, y, MV_DOWN);
7942         started_moving = TRUE;
7943
7944         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7945         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7946       }
7947       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7948       {
7949         if (!MovDelay[x][y])
7950           MovDelay[x][y] = TILEY / 4 + 1;
7951
7952         if (MovDelay[x][y])
7953         {
7954           MovDelay[x][y]--;
7955           if (MovDelay[x][y])
7956             return;
7957         }
7958
7959         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7960         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7961         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7962         Store[x][y] = 0;
7963       }
7964     }
7965     else if ((CAN_PASS_MAGIC_WALL(element) &&
7966               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7967                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7968              (CAN_PASS_DC_MAGIC_WALL(element) &&
7969               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7970
7971     {
7972       InitMovingField(x, y, MV_DOWN);
7973       started_moving = TRUE;
7974
7975       Tile[x][y] =
7976         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7977          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7978          EL_DC_MAGIC_WALL_FILLING);
7979       Store[x][y] = element;
7980     }
7981     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7982     {
7983       SplashAcid(x, y + 1);
7984
7985       InitMovingField(x, y, MV_DOWN);
7986       started_moving = TRUE;
7987
7988       Store[x][y] = EL_ACID;
7989     }
7990     else if (
7991              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7992               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7993              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7994               CAN_FALL(element) && WasJustFalling[x][y] &&
7995               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7996
7997              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7998               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7999               (Tile[x][y + 1] == EL_BLOCKED)))
8000     {
8001       /* this is needed for a special case not covered by calling "Impact()"
8002          from "ContinueMoving()": if an element moves to a tile directly below
8003          another element which was just falling on that tile (which was empty
8004          in the previous frame), the falling element above would just stop
8005          instead of smashing the element below (in previous version, the above
8006          element was just checked for "moving" instead of "falling", resulting
8007          in incorrect smashes caused by horizontal movement of the above
8008          element; also, the case of the player being the element to smash was
8009          simply not covered here... :-/ ) */
8010
8011       CheckCollision[x][y] = 0;
8012       CheckImpact[x][y] = 0;
8013
8014       Impact(x, y);
8015     }
8016     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8017     {
8018       if (MovDir[x][y] == MV_NONE)
8019       {
8020         InitMovingField(x, y, MV_DOWN);
8021         started_moving = TRUE;
8022       }
8023     }
8024     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8025     {
8026       if (WasJustFalling[x][y]) // prevent animation from being restarted
8027         MovDir[x][y] = MV_DOWN;
8028
8029       InitMovingField(x, y, MV_DOWN);
8030       started_moving = TRUE;
8031     }
8032     else if (element == EL_AMOEBA_DROP)
8033     {
8034       Tile[x][y] = EL_AMOEBA_GROWING;
8035       Store[x][y] = EL_AMOEBA_WET;
8036     }
8037     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8038               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8039              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8040              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8041     {
8042       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8043                                 (IS_FREE(x - 1, y + 1) ||
8044                                  Tile[x - 1][y + 1] == EL_ACID));
8045       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8046                                 (IS_FREE(x + 1, y + 1) ||
8047                                  Tile[x + 1][y + 1] == EL_ACID));
8048       boolean can_fall_any  = (can_fall_left || can_fall_right);
8049       boolean can_fall_both = (can_fall_left && can_fall_right);
8050       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8051
8052       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8053       {
8054         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8055           can_fall_right = FALSE;
8056         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8057           can_fall_left = FALSE;
8058         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8059           can_fall_right = FALSE;
8060         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8061           can_fall_left = FALSE;
8062
8063         can_fall_any  = (can_fall_left || can_fall_right);
8064         can_fall_both = FALSE;
8065       }
8066
8067       if (can_fall_both)
8068       {
8069         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8070           can_fall_right = FALSE;       // slip down on left side
8071         else
8072           can_fall_left = !(can_fall_right = RND(2));
8073
8074         can_fall_both = FALSE;
8075       }
8076
8077       if (can_fall_any)
8078       {
8079         // if not determined otherwise, prefer left side for slipping down
8080         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8081         started_moving = TRUE;
8082       }
8083     }
8084     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8085     {
8086       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8087       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8088       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8089       int belt_dir = game.belt_dir[belt_nr];
8090
8091       if ((belt_dir == MV_LEFT  && left_is_free) ||
8092           (belt_dir == MV_RIGHT && right_is_free))
8093       {
8094         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8095
8096         InitMovingField(x, y, belt_dir);
8097         started_moving = TRUE;
8098
8099         Pushed[x][y] = TRUE;
8100         Pushed[nextx][y] = TRUE;
8101
8102         GfxAction[x][y] = ACTION_DEFAULT;
8103       }
8104       else
8105       {
8106         MovDir[x][y] = 0;       // if element was moving, stop it
8107       }
8108     }
8109   }
8110
8111   // not "else if" because of elements that can fall and move (EL_SPRING)
8112   if (CAN_MOVE(element) && !started_moving)
8113   {
8114     int move_pattern = element_info[element].move_pattern;
8115     int newx, newy;
8116
8117     Moving2Blocked(x, y, &newx, &newy);
8118
8119     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8120       return;
8121
8122     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8123         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8124     {
8125       WasJustMoving[x][y] = 0;
8126       CheckCollision[x][y] = 0;
8127
8128       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8129
8130       if (Tile[x][y] != element)        // element has changed
8131         return;
8132     }
8133
8134     if (!MovDelay[x][y])        // start new movement phase
8135     {
8136       // all objects that can change their move direction after each step
8137       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8138
8139       if (element != EL_YAMYAM &&
8140           element != EL_DARK_YAMYAM &&
8141           element != EL_PACMAN &&
8142           !(move_pattern & MV_ANY_DIRECTION) &&
8143           move_pattern != MV_TURNING_LEFT &&
8144           move_pattern != MV_TURNING_RIGHT &&
8145           move_pattern != MV_TURNING_LEFT_RIGHT &&
8146           move_pattern != MV_TURNING_RIGHT_LEFT &&
8147           move_pattern != MV_TURNING_RANDOM)
8148       {
8149         TurnRound(x, y);
8150
8151         if (MovDelay[x][y] && (element == EL_BUG ||
8152                                element == EL_SPACESHIP ||
8153                                element == EL_SP_SNIKSNAK ||
8154                                element == EL_SP_ELECTRON ||
8155                                element == EL_MOLE))
8156           TEST_DrawLevelField(x, y);
8157       }
8158     }
8159
8160     if (MovDelay[x][y])         // wait some time before next movement
8161     {
8162       MovDelay[x][y]--;
8163
8164       if (element == EL_ROBOT ||
8165           element == EL_YAMYAM ||
8166           element == EL_DARK_YAMYAM)
8167       {
8168         DrawLevelElementAnimationIfNeeded(x, y, element);
8169         PlayLevelSoundAction(x, y, ACTION_WAITING);
8170       }
8171       else if (element == EL_SP_ELECTRON)
8172         DrawLevelElementAnimationIfNeeded(x, y, element);
8173       else if (element == EL_DRAGON)
8174       {
8175         int i;
8176         int dir = MovDir[x][y];
8177         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8178         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8179         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8180                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8181                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8182                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8183         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8184
8185         GfxAction[x][y] = ACTION_ATTACKING;
8186
8187         if (IS_PLAYER(x, y))
8188           DrawPlayerField(x, y);
8189         else
8190           TEST_DrawLevelField(x, y);
8191
8192         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8193
8194         for (i = 1; i <= 3; i++)
8195         {
8196           int xx = x + i * dx;
8197           int yy = y + i * dy;
8198           int sx = SCREENX(xx);
8199           int sy = SCREENY(yy);
8200           int flame_graphic = graphic + (i - 1);
8201
8202           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8203             break;
8204
8205           if (MovDelay[x][y])
8206           {
8207             int flamed = MovingOrBlocked2Element(xx, yy);
8208
8209             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8210               Bang(xx, yy);
8211             else
8212               RemoveMovingField(xx, yy);
8213
8214             ChangeDelay[xx][yy] = 0;
8215
8216             Tile[xx][yy] = EL_FLAMES;
8217
8218             if (IN_SCR_FIELD(sx, sy))
8219             {
8220               TEST_DrawLevelFieldCrumbled(xx, yy);
8221               DrawGraphic(sx, sy, flame_graphic, frame);
8222             }
8223           }
8224           else
8225           {
8226             if (Tile[xx][yy] == EL_FLAMES)
8227               Tile[xx][yy] = EL_EMPTY;
8228             TEST_DrawLevelField(xx, yy);
8229           }
8230         }
8231       }
8232
8233       if (MovDelay[x][y])       // element still has to wait some time
8234       {
8235         PlayLevelSoundAction(x, y, ACTION_WAITING);
8236
8237         return;
8238       }
8239     }
8240
8241     // now make next step
8242
8243     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8244
8245     if (DONT_COLLIDE_WITH(element) &&
8246         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8247         !PLAYER_ENEMY_PROTECTED(newx, newy))
8248     {
8249       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8250
8251       return;
8252     }
8253
8254     else if (CAN_MOVE_INTO_ACID(element) &&
8255              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8256              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8257              (MovDir[x][y] == MV_DOWN ||
8258               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8259     {
8260       SplashAcid(newx, newy);
8261       Store[x][y] = EL_ACID;
8262     }
8263     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8264     {
8265       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8266           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8267           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8268           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8269       {
8270         RemoveField(x, y);
8271         TEST_DrawLevelField(x, y);
8272
8273         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8274         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8275           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8276
8277         game.friends_still_needed--;
8278         if (!game.friends_still_needed &&
8279             !game.GameOver &&
8280             game.all_players_gone)
8281           LevelSolved();
8282
8283         return;
8284       }
8285       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8286       {
8287         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8288           TEST_DrawLevelField(newx, newy);
8289         else
8290           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8291       }
8292       else if (!IS_FREE(newx, newy))
8293       {
8294         GfxAction[x][y] = ACTION_WAITING;
8295
8296         if (IS_PLAYER(x, y))
8297           DrawPlayerField(x, y);
8298         else
8299           TEST_DrawLevelField(x, y);
8300
8301         return;
8302       }
8303     }
8304     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8305     {
8306       if (IS_FOOD_PIG(Tile[newx][newy]))
8307       {
8308         if (IS_MOVING(newx, newy))
8309           RemoveMovingField(newx, newy);
8310         else
8311         {
8312           Tile[newx][newy] = EL_EMPTY;
8313           TEST_DrawLevelField(newx, newy);
8314         }
8315
8316         PlayLevelSound(x, y, SND_PIG_DIGGING);
8317       }
8318       else if (!IS_FREE(newx, newy))
8319       {
8320         if (IS_PLAYER(x, y))
8321           DrawPlayerField(x, y);
8322         else
8323           TEST_DrawLevelField(x, y);
8324
8325         return;
8326       }
8327     }
8328     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8329     {
8330       if (Store[x][y] != EL_EMPTY)
8331       {
8332         boolean can_clone = FALSE;
8333         int xx, yy;
8334
8335         // check if element to clone is still there
8336         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8337         {
8338           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8339           {
8340             can_clone = TRUE;
8341
8342             break;
8343           }
8344         }
8345
8346         // cannot clone or target field not free anymore -- do not clone
8347         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8348           Store[x][y] = EL_EMPTY;
8349       }
8350
8351       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8352       {
8353         if (IS_MV_DIAGONAL(MovDir[x][y]))
8354         {
8355           int diagonal_move_dir = MovDir[x][y];
8356           int stored = Store[x][y];
8357           int change_delay = 8;
8358           int graphic;
8359
8360           // android is moving diagonally
8361
8362           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8363
8364           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8365           GfxElement[x][y] = EL_EMC_ANDROID;
8366           GfxAction[x][y] = ACTION_SHRINKING;
8367           GfxDir[x][y] = diagonal_move_dir;
8368           ChangeDelay[x][y] = change_delay;
8369
8370           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8371                                    GfxDir[x][y]);
8372
8373           DrawLevelGraphicAnimation(x, y, graphic);
8374           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8375
8376           if (Tile[newx][newy] == EL_ACID)
8377           {
8378             SplashAcid(newx, newy);
8379
8380             return;
8381           }
8382
8383           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8384
8385           Store[newx][newy] = EL_EMC_ANDROID;
8386           GfxElement[newx][newy] = EL_EMC_ANDROID;
8387           GfxAction[newx][newy] = ACTION_GROWING;
8388           GfxDir[newx][newy] = diagonal_move_dir;
8389           ChangeDelay[newx][newy] = change_delay;
8390
8391           graphic = el_act_dir2img(GfxElement[newx][newy],
8392                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8393
8394           DrawLevelGraphicAnimation(newx, newy, graphic);
8395           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8396
8397           return;
8398         }
8399         else
8400         {
8401           Tile[newx][newy] = EL_EMPTY;
8402           TEST_DrawLevelField(newx, newy);
8403
8404           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8405         }
8406       }
8407       else if (!IS_FREE(newx, newy))
8408       {
8409         return;
8410       }
8411     }
8412     else if (IS_CUSTOM_ELEMENT(element) &&
8413              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8414     {
8415       if (!DigFieldByCE(newx, newy, element))
8416         return;
8417
8418       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8419       {
8420         RunnerVisit[x][y] = FrameCounter;
8421         PlayerVisit[x][y] /= 8;         // expire player visit path
8422       }
8423     }
8424     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8425     {
8426       if (!IS_FREE(newx, newy))
8427       {
8428         if (IS_PLAYER(x, y))
8429           DrawPlayerField(x, y);
8430         else
8431           TEST_DrawLevelField(x, y);
8432
8433         return;
8434       }
8435       else
8436       {
8437         boolean wanna_flame = !RND(10);
8438         int dx = newx - x, dy = newy - y;
8439         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8440         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8441         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8442                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8443         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8444                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8445
8446         if ((wanna_flame ||
8447              IS_CLASSIC_ENEMY(element1) ||
8448              IS_CLASSIC_ENEMY(element2)) &&
8449             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8450             element1 != EL_FLAMES && element2 != EL_FLAMES)
8451         {
8452           ResetGfxAnimation(x, y);
8453           GfxAction[x][y] = ACTION_ATTACKING;
8454
8455           if (IS_PLAYER(x, y))
8456             DrawPlayerField(x, y);
8457           else
8458             TEST_DrawLevelField(x, y);
8459
8460           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8461
8462           MovDelay[x][y] = 50;
8463
8464           Tile[newx][newy] = EL_FLAMES;
8465           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8466             Tile[newx1][newy1] = EL_FLAMES;
8467           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8468             Tile[newx2][newy2] = EL_FLAMES;
8469
8470           return;
8471         }
8472       }
8473     }
8474     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8475              Tile[newx][newy] == EL_DIAMOND)
8476     {
8477       if (IS_MOVING(newx, newy))
8478         RemoveMovingField(newx, newy);
8479       else
8480       {
8481         Tile[newx][newy] = EL_EMPTY;
8482         TEST_DrawLevelField(newx, newy);
8483       }
8484
8485       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8486     }
8487     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8488              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8489     {
8490       if (AmoebaNr[newx][newy])
8491       {
8492         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8493         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8494             Tile[newx][newy] == EL_BD_AMOEBA)
8495           AmoebaCnt[AmoebaNr[newx][newy]]--;
8496       }
8497
8498       if (IS_MOVING(newx, newy))
8499       {
8500         RemoveMovingField(newx, newy);
8501       }
8502       else
8503       {
8504         Tile[newx][newy] = EL_EMPTY;
8505         TEST_DrawLevelField(newx, newy);
8506       }
8507
8508       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8509     }
8510     else if ((element == EL_PACMAN || element == EL_MOLE)
8511              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8512     {
8513       if (AmoebaNr[newx][newy])
8514       {
8515         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8516         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8517             Tile[newx][newy] == EL_BD_AMOEBA)
8518           AmoebaCnt[AmoebaNr[newx][newy]]--;
8519       }
8520
8521       if (element == EL_MOLE)
8522       {
8523         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8524         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8525
8526         ResetGfxAnimation(x, y);
8527         GfxAction[x][y] = ACTION_DIGGING;
8528         TEST_DrawLevelField(x, y);
8529
8530         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8531
8532         return;                         // wait for shrinking amoeba
8533       }
8534       else      // element == EL_PACMAN
8535       {
8536         Tile[newx][newy] = EL_EMPTY;
8537         TEST_DrawLevelField(newx, newy);
8538         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8539       }
8540     }
8541     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8542              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8543               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8544     {
8545       // wait for shrinking amoeba to completely disappear
8546       return;
8547     }
8548     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8549     {
8550       // object was running against a wall
8551
8552       TurnRound(x, y);
8553
8554       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8555         DrawLevelElementAnimation(x, y, element);
8556
8557       if (DONT_TOUCH(element))
8558         TestIfBadThingTouchesPlayer(x, y);
8559
8560       return;
8561     }
8562
8563     InitMovingField(x, y, MovDir[x][y]);
8564
8565     PlayLevelSoundAction(x, y, ACTION_MOVING);
8566   }
8567
8568   if (MovDir[x][y])
8569     ContinueMoving(x, y);
8570 }
8571
8572 void ContinueMoving(int x, int y)
8573 {
8574   int element = Tile[x][y];
8575   struct ElementInfo *ei = &element_info[element];
8576   int direction = MovDir[x][y];
8577   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8578   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8579   int newx = x + dx, newy = y + dy;
8580   int stored = Store[x][y];
8581   int stored_new = Store[newx][newy];
8582   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8583   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8584   boolean last_line = (newy == lev_fieldy - 1);
8585   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8586
8587   if (pushed_by_player)         // special case: moving object pushed by player
8588   {
8589     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8590   }
8591   else if (use_step_delay)      // special case: moving object has step delay
8592   {
8593     if (!MovDelay[x][y])
8594       MovPos[x][y] += getElementMoveStepsize(x, y);
8595
8596     if (MovDelay[x][y])
8597       MovDelay[x][y]--;
8598     else
8599       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8600
8601     if (MovDelay[x][y])
8602     {
8603       TEST_DrawLevelField(x, y);
8604
8605       return;   // element is still waiting
8606     }
8607   }
8608   else                          // normal case: generically moving object
8609   {
8610     MovPos[x][y] += getElementMoveStepsize(x, y);
8611   }
8612
8613   if (ABS(MovPos[x][y]) < TILEX)
8614   {
8615     TEST_DrawLevelField(x, y);
8616
8617     return;     // element is still moving
8618   }
8619
8620   // element reached destination field
8621
8622   Tile[x][y] = EL_EMPTY;
8623   Tile[newx][newy] = element;
8624   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8625
8626   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8627   {
8628     element = Tile[newx][newy] = EL_ACID;
8629   }
8630   else if (element == EL_MOLE)
8631   {
8632     Tile[x][y] = EL_SAND;
8633
8634     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8635   }
8636   else if (element == EL_QUICKSAND_FILLING)
8637   {
8638     element = Tile[newx][newy] = get_next_element(element);
8639     Store[newx][newy] = Store[x][y];
8640   }
8641   else if (element == EL_QUICKSAND_EMPTYING)
8642   {
8643     Tile[x][y] = get_next_element(element);
8644     element = Tile[newx][newy] = Store[x][y];
8645   }
8646   else if (element == EL_QUICKSAND_FAST_FILLING)
8647   {
8648     element = Tile[newx][newy] = get_next_element(element);
8649     Store[newx][newy] = Store[x][y];
8650   }
8651   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8652   {
8653     Tile[x][y] = get_next_element(element);
8654     element = Tile[newx][newy] = Store[x][y];
8655   }
8656   else if (element == EL_MAGIC_WALL_FILLING)
8657   {
8658     element = Tile[newx][newy] = get_next_element(element);
8659     if (!game.magic_wall_active)
8660       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8661     Store[newx][newy] = Store[x][y];
8662   }
8663   else if (element == EL_MAGIC_WALL_EMPTYING)
8664   {
8665     Tile[x][y] = get_next_element(element);
8666     if (!game.magic_wall_active)
8667       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8668     element = Tile[newx][newy] = Store[x][y];
8669
8670     InitField(newx, newy, FALSE);
8671   }
8672   else if (element == EL_BD_MAGIC_WALL_FILLING)
8673   {
8674     element = Tile[newx][newy] = get_next_element(element);
8675     if (!game.magic_wall_active)
8676       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8677     Store[newx][newy] = Store[x][y];
8678   }
8679   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8680   {
8681     Tile[x][y] = get_next_element(element);
8682     if (!game.magic_wall_active)
8683       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8684     element = Tile[newx][newy] = Store[x][y];
8685
8686     InitField(newx, newy, FALSE);
8687   }
8688   else if (element == EL_DC_MAGIC_WALL_FILLING)
8689   {
8690     element = Tile[newx][newy] = get_next_element(element);
8691     if (!game.magic_wall_active)
8692       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8693     Store[newx][newy] = Store[x][y];
8694   }
8695   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8696   {
8697     Tile[x][y] = get_next_element(element);
8698     if (!game.magic_wall_active)
8699       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8700     element = Tile[newx][newy] = Store[x][y];
8701
8702     InitField(newx, newy, FALSE);
8703   }
8704   else if (element == EL_AMOEBA_DROPPING)
8705   {
8706     Tile[x][y] = get_next_element(element);
8707     element = Tile[newx][newy] = Store[x][y];
8708   }
8709   else if (element == EL_SOKOBAN_OBJECT)
8710   {
8711     if (Back[x][y])
8712       Tile[x][y] = Back[x][y];
8713
8714     if (Back[newx][newy])
8715       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8716
8717     Back[x][y] = Back[newx][newy] = 0;
8718   }
8719
8720   Store[x][y] = EL_EMPTY;
8721   MovPos[x][y] = 0;
8722   MovDir[x][y] = 0;
8723   MovDelay[x][y] = 0;
8724
8725   MovDelay[newx][newy] = 0;
8726
8727   if (CAN_CHANGE_OR_HAS_ACTION(element))
8728   {
8729     // copy element change control values to new field
8730     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8731     ChangePage[newx][newy]  = ChangePage[x][y];
8732     ChangeCount[newx][newy] = ChangeCount[x][y];
8733     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8734   }
8735
8736   CustomValue[newx][newy] = CustomValue[x][y];
8737
8738   ChangeDelay[x][y] = 0;
8739   ChangePage[x][y] = -1;
8740   ChangeCount[x][y] = 0;
8741   ChangeEvent[x][y] = -1;
8742
8743   CustomValue[x][y] = 0;
8744
8745   // copy animation control values to new field
8746   GfxFrame[newx][newy]  = GfxFrame[x][y];
8747   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8748   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8749   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8750
8751   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8752
8753   // some elements can leave other elements behind after moving
8754   if (ei->move_leave_element != EL_EMPTY &&
8755       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8756       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8757   {
8758     int move_leave_element = ei->move_leave_element;
8759
8760     // this makes it possible to leave the removed element again
8761     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8762       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8763
8764     Tile[x][y] = move_leave_element;
8765
8766     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8767       MovDir[x][y] = direction;
8768
8769     InitField(x, y, FALSE);
8770
8771     if (GFX_CRUMBLED(Tile[x][y]))
8772       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8773
8774     if (ELEM_IS_PLAYER(move_leave_element))
8775       RelocatePlayer(x, y, move_leave_element);
8776   }
8777
8778   // do this after checking for left-behind element
8779   ResetGfxAnimation(x, y);      // reset animation values for old field
8780
8781   if (!CAN_MOVE(element) ||
8782       (CAN_FALL(element) && direction == MV_DOWN &&
8783        (element == EL_SPRING ||
8784         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8785         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8786     GfxDir[x][y] = MovDir[newx][newy] = 0;
8787
8788   TEST_DrawLevelField(x, y);
8789   TEST_DrawLevelField(newx, newy);
8790
8791   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8792
8793   // prevent pushed element from moving on in pushed direction
8794   if (pushed_by_player && CAN_MOVE(element) &&
8795       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8796       !(element_info[element].move_pattern & direction))
8797     TurnRound(newx, newy);
8798
8799   // prevent elements on conveyor belt from moving on in last direction
8800   if (pushed_by_conveyor && CAN_FALL(element) &&
8801       direction & MV_HORIZONTAL)
8802     MovDir[newx][newy] = 0;
8803
8804   if (!pushed_by_player)
8805   {
8806     int nextx = newx + dx, nexty = newy + dy;
8807     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8808
8809     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8810
8811     if (CAN_FALL(element) && direction == MV_DOWN)
8812       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8813
8814     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8815       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8816
8817     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8818       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8819   }
8820
8821   if (DONT_TOUCH(element))      // object may be nasty to player or others
8822   {
8823     TestIfBadThingTouchesPlayer(newx, newy);
8824     TestIfBadThingTouchesFriend(newx, newy);
8825
8826     if (!IS_CUSTOM_ELEMENT(element))
8827       TestIfBadThingTouchesOtherBadThing(newx, newy);
8828   }
8829   else if (element == EL_PENGUIN)
8830     TestIfFriendTouchesBadThing(newx, newy);
8831
8832   if (DONT_GET_HIT_BY(element))
8833   {
8834     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8835   }
8836
8837   // give the player one last chance (one more frame) to move away
8838   if (CAN_FALL(element) && direction == MV_DOWN &&
8839       (last_line || (!IS_FREE(x, newy + 1) &&
8840                      (!IS_PLAYER(x, newy + 1) ||
8841                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8842     Impact(x, newy);
8843
8844   if (pushed_by_player && !game.use_change_when_pushing_bug)
8845   {
8846     int push_side = MV_DIR_OPPOSITE(direction);
8847     struct PlayerInfo *player = PLAYERINFO(x, y);
8848
8849     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8850                                player->index_bit, push_side);
8851     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8852                                         player->index_bit, push_side);
8853   }
8854
8855   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8856     MovDelay[newx][newy] = 1;
8857
8858   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8859
8860   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8861   TestIfElementHitsCustomElement(newx, newy, direction);
8862   TestIfPlayerTouchesCustomElement(newx, newy);
8863   TestIfElementTouchesCustomElement(newx, newy);
8864
8865   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8866       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8867     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8868                              MV_DIR_OPPOSITE(direction));
8869 }
8870
8871 int AmoebaNeighbourNr(int ax, int ay)
8872 {
8873   int i;
8874   int element = Tile[ax][ay];
8875   int group_nr = 0;
8876   static int xy[4][2] =
8877   {
8878     { 0, -1 },
8879     { -1, 0 },
8880     { +1, 0 },
8881     { 0, +1 }
8882   };
8883
8884   for (i = 0; i < NUM_DIRECTIONS; i++)
8885   {
8886     int x = ax + xy[i][0];
8887     int y = ay + xy[i][1];
8888
8889     if (!IN_LEV_FIELD(x, y))
8890       continue;
8891
8892     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8893       group_nr = AmoebaNr[x][y];
8894   }
8895
8896   return group_nr;
8897 }
8898
8899 static void AmoebaMerge(int ax, int ay)
8900 {
8901   int i, x, y, xx, yy;
8902   int new_group_nr = AmoebaNr[ax][ay];
8903   static int xy[4][2] =
8904   {
8905     { 0, -1 },
8906     { -1, 0 },
8907     { +1, 0 },
8908     { 0, +1 }
8909   };
8910
8911   if (new_group_nr == 0)
8912     return;
8913
8914   for (i = 0; i < NUM_DIRECTIONS; i++)
8915   {
8916     x = ax + xy[i][0];
8917     y = ay + xy[i][1];
8918
8919     if (!IN_LEV_FIELD(x, y))
8920       continue;
8921
8922     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8923          Tile[x][y] == EL_BD_AMOEBA ||
8924          Tile[x][y] == EL_AMOEBA_DEAD) &&
8925         AmoebaNr[x][y] != new_group_nr)
8926     {
8927       int old_group_nr = AmoebaNr[x][y];
8928
8929       if (old_group_nr == 0)
8930         return;
8931
8932       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8933       AmoebaCnt[old_group_nr] = 0;
8934       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8935       AmoebaCnt2[old_group_nr] = 0;
8936
8937       SCAN_PLAYFIELD(xx, yy)
8938       {
8939         if (AmoebaNr[xx][yy] == old_group_nr)
8940           AmoebaNr[xx][yy] = new_group_nr;
8941       }
8942     }
8943   }
8944 }
8945
8946 void AmoebaToDiamond(int ax, int ay)
8947 {
8948   int i, x, y;
8949
8950   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8951   {
8952     int group_nr = AmoebaNr[ax][ay];
8953
8954 #ifdef DEBUG
8955     if (group_nr == 0)
8956     {
8957       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8958       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8959
8960       return;
8961     }
8962 #endif
8963
8964     SCAN_PLAYFIELD(x, y)
8965     {
8966       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8967       {
8968         AmoebaNr[x][y] = 0;
8969         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8970       }
8971     }
8972
8973     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8974                             SND_AMOEBA_TURNING_TO_GEM :
8975                             SND_AMOEBA_TURNING_TO_ROCK));
8976     Bang(ax, ay);
8977   }
8978   else
8979   {
8980     static int xy[4][2] =
8981     {
8982       { 0, -1 },
8983       { -1, 0 },
8984       { +1, 0 },
8985       { 0, +1 }
8986     };
8987
8988     for (i = 0; i < NUM_DIRECTIONS; i++)
8989     {
8990       x = ax + xy[i][0];
8991       y = ay + xy[i][1];
8992
8993       if (!IN_LEV_FIELD(x, y))
8994         continue;
8995
8996       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8997       {
8998         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8999                               SND_AMOEBA_TURNING_TO_GEM :
9000                               SND_AMOEBA_TURNING_TO_ROCK));
9001         Bang(x, y);
9002       }
9003     }
9004   }
9005 }
9006
9007 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9008 {
9009   int x, y;
9010   int group_nr = AmoebaNr[ax][ay];
9011   boolean done = FALSE;
9012
9013 #ifdef DEBUG
9014   if (group_nr == 0)
9015   {
9016     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9017     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9018
9019     return;
9020   }
9021 #endif
9022
9023   SCAN_PLAYFIELD(x, y)
9024   {
9025     if (AmoebaNr[x][y] == group_nr &&
9026         (Tile[x][y] == EL_AMOEBA_DEAD ||
9027          Tile[x][y] == EL_BD_AMOEBA ||
9028          Tile[x][y] == EL_AMOEBA_GROWING))
9029     {
9030       AmoebaNr[x][y] = 0;
9031       Tile[x][y] = new_element;
9032       InitField(x, y, FALSE);
9033       TEST_DrawLevelField(x, y);
9034       done = TRUE;
9035     }
9036   }
9037
9038   if (done)
9039     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9040                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9041                             SND_BD_AMOEBA_TURNING_TO_GEM));
9042 }
9043
9044 static void AmoebaGrowing(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 growing cycle
9050   {
9051     MovDelay[x][y] = 7;
9052
9053     if (DelayReached(&sound_delay, sound_delay_value))
9054     {
9055       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9056       sound_delay_value = 30;
9057     }
9058   }
9059
9060   if (MovDelay[x][y])           // wait some time before growing bigger
9061   {
9062     MovDelay[x][y]--;
9063     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9064     {
9065       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9066                                            6 - MovDelay[x][y]);
9067
9068       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9069     }
9070
9071     if (!MovDelay[x][y])
9072     {
9073       Tile[x][y] = Store[x][y];
9074       Store[x][y] = 0;
9075       TEST_DrawLevelField(x, y);
9076     }
9077   }
9078 }
9079
9080 static void AmoebaShrinking(int x, int y)
9081 {
9082   static unsigned int sound_delay = 0;
9083   static unsigned int sound_delay_value = 0;
9084
9085   if (!MovDelay[x][y])          // start new shrinking cycle
9086   {
9087     MovDelay[x][y] = 7;
9088
9089     if (DelayReached(&sound_delay, sound_delay_value))
9090       sound_delay_value = 30;
9091   }
9092
9093   if (MovDelay[x][y])           // wait some time before shrinking
9094   {
9095     MovDelay[x][y]--;
9096     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9097     {
9098       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9099                                            6 - MovDelay[x][y]);
9100
9101       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9102     }
9103
9104     if (!MovDelay[x][y])
9105     {
9106       Tile[x][y] = EL_EMPTY;
9107       TEST_DrawLevelField(x, y);
9108
9109       // don't let mole enter this field in this cycle;
9110       // (give priority to objects falling to this field from above)
9111       Stop[x][y] = TRUE;
9112     }
9113   }
9114 }
9115
9116 static void AmoebaReproduce(int ax, int ay)
9117 {
9118   int i;
9119   int element = Tile[ax][ay];
9120   int graphic = el2img(element);
9121   int newax = ax, neway = ay;
9122   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9123   static int xy[4][2] =
9124   {
9125     { 0, -1 },
9126     { -1, 0 },
9127     { +1, 0 },
9128     { 0, +1 }
9129   };
9130
9131   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9132   {
9133     Tile[ax][ay] = EL_AMOEBA_DEAD;
9134     TEST_DrawLevelField(ax, ay);
9135     return;
9136   }
9137
9138   if (IS_ANIMATED(graphic))
9139     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9140
9141   if (!MovDelay[ax][ay])        // start making new amoeba field
9142     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9143
9144   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9145   {
9146     MovDelay[ax][ay]--;
9147     if (MovDelay[ax][ay])
9148       return;
9149   }
9150
9151   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9152   {
9153     int start = RND(4);
9154     int x = ax + xy[start][0];
9155     int y = ay + xy[start][1];
9156
9157     if (!IN_LEV_FIELD(x, y))
9158       return;
9159
9160     if (IS_FREE(x, y) ||
9161         CAN_GROW_INTO(Tile[x][y]) ||
9162         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9163         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9164     {
9165       newax = x;
9166       neway = y;
9167     }
9168
9169     if (newax == ax && neway == ay)
9170       return;
9171   }
9172   else                          // normal or "filled" (BD style) amoeba
9173   {
9174     int start = RND(4);
9175     boolean waiting_for_player = FALSE;
9176
9177     for (i = 0; i < NUM_DIRECTIONS; i++)
9178     {
9179       int j = (start + i) % 4;
9180       int x = ax + xy[j][0];
9181       int y = ay + xy[j][1];
9182
9183       if (!IN_LEV_FIELD(x, y))
9184         continue;
9185
9186       if (IS_FREE(x, y) ||
9187           CAN_GROW_INTO(Tile[x][y]) ||
9188           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9189           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9190       {
9191         newax = x;
9192         neway = y;
9193         break;
9194       }
9195       else if (IS_PLAYER(x, y))
9196         waiting_for_player = TRUE;
9197     }
9198
9199     if (newax == ax && neway == ay)             // amoeba cannot grow
9200     {
9201       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9202       {
9203         Tile[ax][ay] = EL_AMOEBA_DEAD;
9204         TEST_DrawLevelField(ax, ay);
9205         AmoebaCnt[AmoebaNr[ax][ay]]--;
9206
9207         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9208         {
9209           if (element == EL_AMOEBA_FULL)
9210             AmoebaToDiamond(ax, ay);
9211           else if (element == EL_BD_AMOEBA)
9212             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9213         }
9214       }
9215       return;
9216     }
9217     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9218     {
9219       // amoeba gets larger by growing in some direction
9220
9221       int new_group_nr = AmoebaNr[ax][ay];
9222
9223 #ifdef DEBUG
9224   if (new_group_nr == 0)
9225   {
9226     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9227           newax, neway);
9228     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9229
9230     return;
9231   }
9232 #endif
9233
9234       AmoebaNr[newax][neway] = new_group_nr;
9235       AmoebaCnt[new_group_nr]++;
9236       AmoebaCnt2[new_group_nr]++;
9237
9238       // if amoeba touches other amoeba(s) after growing, unify them
9239       AmoebaMerge(newax, neway);
9240
9241       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9242       {
9243         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9244         return;
9245       }
9246     }
9247   }
9248
9249   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9250       (neway == lev_fieldy - 1 && newax != ax))
9251   {
9252     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9253     Store[newax][neway] = element;
9254   }
9255   else if (neway == ay || element == EL_EMC_DRIPPER)
9256   {
9257     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9258
9259     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9260   }
9261   else
9262   {
9263     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9264     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9265     Store[ax][ay] = EL_AMOEBA_DROP;
9266     ContinueMoving(ax, ay);
9267     return;
9268   }
9269
9270   TEST_DrawLevelField(newax, neway);
9271 }
9272
9273 static void Life(int ax, int ay)
9274 {
9275   int x1, y1, x2, y2;
9276   int life_time = 40;
9277   int element = Tile[ax][ay];
9278   int graphic = el2img(element);
9279   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9280                          level.biomaze);
9281   boolean changed = FALSE;
9282
9283   if (IS_ANIMATED(graphic))
9284     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9285
9286   if (Stop[ax][ay])
9287     return;
9288
9289   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9290     MovDelay[ax][ay] = life_time;
9291
9292   if (MovDelay[ax][ay])         // wait some time before next cycle
9293   {
9294     MovDelay[ax][ay]--;
9295     if (MovDelay[ax][ay])
9296       return;
9297   }
9298
9299   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9300   {
9301     int xx = ax+x1, yy = ay+y1;
9302     int old_element = Tile[xx][yy];
9303     int num_neighbours = 0;
9304
9305     if (!IN_LEV_FIELD(xx, yy))
9306       continue;
9307
9308     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9309     {
9310       int x = xx+x2, y = yy+y2;
9311
9312       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9313         continue;
9314
9315       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9316       boolean is_neighbour = FALSE;
9317
9318       if (level.use_life_bugs)
9319         is_neighbour =
9320           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9321            (IS_FREE(x, y)                             &&  Stop[x][y]));
9322       else
9323         is_neighbour =
9324           (Last[x][y] == element || is_player_cell);
9325
9326       if (is_neighbour)
9327         num_neighbours++;
9328     }
9329
9330     boolean is_free = FALSE;
9331
9332     if (level.use_life_bugs)
9333       is_free = (IS_FREE(xx, yy));
9334     else
9335       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9336
9337     if (xx == ax && yy == ay)           // field in the middle
9338     {
9339       if (num_neighbours < life_parameter[0] ||
9340           num_neighbours > life_parameter[1])
9341       {
9342         Tile[xx][yy] = EL_EMPTY;
9343         if (Tile[xx][yy] != old_element)
9344           TEST_DrawLevelField(xx, yy);
9345         Stop[xx][yy] = TRUE;
9346         changed = TRUE;
9347       }
9348     }
9349     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9350     {                                   // free border field
9351       if (num_neighbours >= life_parameter[2] &&
9352           num_neighbours <= life_parameter[3])
9353       {
9354         Tile[xx][yy] = element;
9355         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9356         if (Tile[xx][yy] != old_element)
9357           TEST_DrawLevelField(xx, yy);
9358         Stop[xx][yy] = TRUE;
9359         changed = TRUE;
9360       }
9361     }
9362   }
9363
9364   if (changed)
9365     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9366                    SND_GAME_OF_LIFE_GROWING);
9367 }
9368
9369 static void InitRobotWheel(int x, int y)
9370 {
9371   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9372 }
9373
9374 static void RunRobotWheel(int x, int y)
9375 {
9376   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9377 }
9378
9379 static void StopRobotWheel(int x, int y)
9380 {
9381   if (game.robot_wheel_x == x &&
9382       game.robot_wheel_y == y)
9383   {
9384     game.robot_wheel_x = -1;
9385     game.robot_wheel_y = -1;
9386     game.robot_wheel_active = FALSE;
9387   }
9388 }
9389
9390 static void InitTimegateWheel(int x, int y)
9391 {
9392   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9393 }
9394
9395 static void RunTimegateWheel(int x, int y)
9396 {
9397   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9398 }
9399
9400 static void InitMagicBallDelay(int x, int y)
9401 {
9402   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9403 }
9404
9405 static void ActivateMagicBall(int bx, int by)
9406 {
9407   int x, y;
9408
9409   if (level.ball_random)
9410   {
9411     int pos_border = RND(8);    // select one of the eight border elements
9412     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9413     int xx = pos_content % 3;
9414     int yy = pos_content / 3;
9415
9416     x = bx - 1 + xx;
9417     y = by - 1 + yy;
9418
9419     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9420       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9421   }
9422   else
9423   {
9424     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9425     {
9426       int xx = x - bx + 1;
9427       int yy = y - by + 1;
9428
9429       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9430         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9431     }
9432   }
9433
9434   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9435 }
9436
9437 static void CheckExit(int x, int y)
9438 {
9439   if (game.gems_still_needed > 0 ||
9440       game.sokoban_fields_still_needed > 0 ||
9441       game.sokoban_objects_still_needed > 0 ||
9442       game.lights_still_needed > 0)
9443   {
9444     int element = Tile[x][y];
9445     int graphic = el2img(element);
9446
9447     if (IS_ANIMATED(graphic))
9448       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9449
9450     return;
9451   }
9452
9453   // do not re-open exit door closed after last player
9454   if (game.all_players_gone)
9455     return;
9456
9457   Tile[x][y] = EL_EXIT_OPENING;
9458
9459   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9460 }
9461
9462 static void CheckExitEM(int x, int y)
9463 {
9464   if (game.gems_still_needed > 0 ||
9465       game.sokoban_fields_still_needed > 0 ||
9466       game.sokoban_objects_still_needed > 0 ||
9467       game.lights_still_needed > 0)
9468   {
9469     int element = Tile[x][y];
9470     int graphic = el2img(element);
9471
9472     if (IS_ANIMATED(graphic))
9473       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9474
9475     return;
9476   }
9477
9478   // do not re-open exit door closed after last player
9479   if (game.all_players_gone)
9480     return;
9481
9482   Tile[x][y] = EL_EM_EXIT_OPENING;
9483
9484   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9485 }
9486
9487 static void CheckExitSteel(int x, int y)
9488 {
9489   if (game.gems_still_needed > 0 ||
9490       game.sokoban_fields_still_needed > 0 ||
9491       game.sokoban_objects_still_needed > 0 ||
9492       game.lights_still_needed > 0)
9493   {
9494     int element = Tile[x][y];
9495     int graphic = el2img(element);
9496
9497     if (IS_ANIMATED(graphic))
9498       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9499
9500     return;
9501   }
9502
9503   // do not re-open exit door closed after last player
9504   if (game.all_players_gone)
9505     return;
9506
9507   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9508
9509   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9510 }
9511
9512 static void CheckExitSteelEM(int x, int y)
9513 {
9514   if (game.gems_still_needed > 0 ||
9515       game.sokoban_fields_still_needed > 0 ||
9516       game.sokoban_objects_still_needed > 0 ||
9517       game.lights_still_needed > 0)
9518   {
9519     int element = Tile[x][y];
9520     int graphic = el2img(element);
9521
9522     if (IS_ANIMATED(graphic))
9523       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9524
9525     return;
9526   }
9527
9528   // do not re-open exit door closed after last player
9529   if (game.all_players_gone)
9530     return;
9531
9532   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9533
9534   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9535 }
9536
9537 static void CheckExitSP(int x, int y)
9538 {
9539   if (game.gems_still_needed > 0)
9540   {
9541     int element = Tile[x][y];
9542     int graphic = el2img(element);
9543
9544     if (IS_ANIMATED(graphic))
9545       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9546
9547     return;
9548   }
9549
9550   // do not re-open exit door closed after last player
9551   if (game.all_players_gone)
9552     return;
9553
9554   Tile[x][y] = EL_SP_EXIT_OPENING;
9555
9556   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9557 }
9558
9559 static void CloseAllOpenTimegates(void)
9560 {
9561   int x, y;
9562
9563   SCAN_PLAYFIELD(x, y)
9564   {
9565     int element = Tile[x][y];
9566
9567     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9568     {
9569       Tile[x][y] = EL_TIMEGATE_CLOSING;
9570
9571       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9572     }
9573   }
9574 }
9575
9576 static void DrawTwinkleOnField(int x, int y)
9577 {
9578   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9579     return;
9580
9581   if (Tile[x][y] == EL_BD_DIAMOND)
9582     return;
9583
9584   if (MovDelay[x][y] == 0)      // next animation frame
9585     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9586
9587   if (MovDelay[x][y] != 0)      // wait some time before next frame
9588   {
9589     MovDelay[x][y]--;
9590
9591     DrawLevelElementAnimation(x, y, Tile[x][y]);
9592
9593     if (MovDelay[x][y] != 0)
9594     {
9595       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9596                                            10 - MovDelay[x][y]);
9597
9598       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9599     }
9600   }
9601 }
9602
9603 static void MauerWaechst(int x, int y)
9604 {
9605   int delay = 6;
9606
9607   if (!MovDelay[x][y])          // next animation frame
9608     MovDelay[x][y] = 3 * delay;
9609
9610   if (MovDelay[x][y])           // wait some time before next frame
9611   {
9612     MovDelay[x][y]--;
9613
9614     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9615     {
9616       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9617       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9618
9619       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9620     }
9621
9622     if (!MovDelay[x][y])
9623     {
9624       if (MovDir[x][y] == MV_LEFT)
9625       {
9626         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9627           TEST_DrawLevelField(x - 1, y);
9628       }
9629       else if (MovDir[x][y] == MV_RIGHT)
9630       {
9631         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9632           TEST_DrawLevelField(x + 1, y);
9633       }
9634       else if (MovDir[x][y] == MV_UP)
9635       {
9636         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9637           TEST_DrawLevelField(x, y - 1);
9638       }
9639       else
9640       {
9641         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9642           TEST_DrawLevelField(x, y + 1);
9643       }
9644
9645       Tile[x][y] = Store[x][y];
9646       Store[x][y] = 0;
9647       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9648       TEST_DrawLevelField(x, y);
9649     }
9650   }
9651 }
9652
9653 static void MauerAbleger(int ax, int ay)
9654 {
9655   int element = Tile[ax][ay];
9656   int graphic = el2img(element);
9657   boolean oben_frei = FALSE, unten_frei = FALSE;
9658   boolean links_frei = FALSE, rechts_frei = FALSE;
9659   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9660   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9661   boolean new_wall = FALSE;
9662
9663   if (IS_ANIMATED(graphic))
9664     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9665
9666   if (!MovDelay[ax][ay])        // start building new wall
9667     MovDelay[ax][ay] = 6;
9668
9669   if (MovDelay[ax][ay])         // wait some time before building new wall
9670   {
9671     MovDelay[ax][ay]--;
9672     if (MovDelay[ax][ay])
9673       return;
9674   }
9675
9676   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9677     oben_frei = TRUE;
9678   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9679     unten_frei = TRUE;
9680   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9681     links_frei = TRUE;
9682   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9683     rechts_frei = TRUE;
9684
9685   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9686       element == EL_EXPANDABLE_WALL_ANY)
9687   {
9688     if (oben_frei)
9689     {
9690       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9691       Store[ax][ay-1] = element;
9692       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9693       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9694         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9695                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9696       new_wall = TRUE;
9697     }
9698     if (unten_frei)
9699     {
9700       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9701       Store[ax][ay+1] = element;
9702       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9703       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9704         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9705                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9706       new_wall = TRUE;
9707     }
9708   }
9709
9710   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9711       element == EL_EXPANDABLE_WALL_ANY ||
9712       element == EL_EXPANDABLE_WALL ||
9713       element == EL_BD_EXPANDABLE_WALL)
9714   {
9715     if (links_frei)
9716     {
9717       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9718       Store[ax-1][ay] = element;
9719       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9720       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9721         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9722                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9723       new_wall = TRUE;
9724     }
9725
9726     if (rechts_frei)
9727     {
9728       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9729       Store[ax+1][ay] = element;
9730       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9731       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9732         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9733                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9734       new_wall = TRUE;
9735     }
9736   }
9737
9738   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9739     TEST_DrawLevelField(ax, ay);
9740
9741   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9742     oben_massiv = TRUE;
9743   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9744     unten_massiv = TRUE;
9745   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9746     links_massiv = TRUE;
9747   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9748     rechts_massiv = TRUE;
9749
9750   if (((oben_massiv && unten_massiv) ||
9751        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9752        element == EL_EXPANDABLE_WALL) &&
9753       ((links_massiv && rechts_massiv) ||
9754        element == EL_EXPANDABLE_WALL_VERTICAL))
9755     Tile[ax][ay] = EL_WALL;
9756
9757   if (new_wall)
9758     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9759 }
9760
9761 static void MauerAblegerStahl(int ax, int ay)
9762 {
9763   int element = Tile[ax][ay];
9764   int graphic = el2img(element);
9765   boolean oben_frei = FALSE, unten_frei = FALSE;
9766   boolean links_frei = FALSE, rechts_frei = FALSE;
9767   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9768   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9769   boolean new_wall = FALSE;
9770
9771   if (IS_ANIMATED(graphic))
9772     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9773
9774   if (!MovDelay[ax][ay])        // start building new wall
9775     MovDelay[ax][ay] = 6;
9776
9777   if (MovDelay[ax][ay])         // wait some time before building new wall
9778   {
9779     MovDelay[ax][ay]--;
9780     if (MovDelay[ax][ay])
9781       return;
9782   }
9783
9784   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9785     oben_frei = TRUE;
9786   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9787     unten_frei = TRUE;
9788   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9789     links_frei = TRUE;
9790   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9791     rechts_frei = TRUE;
9792
9793   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9794       element == EL_EXPANDABLE_STEELWALL_ANY)
9795   {
9796     if (oben_frei)
9797     {
9798       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9799       Store[ax][ay-1] = element;
9800       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9801       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9802         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9803                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9804       new_wall = TRUE;
9805     }
9806     if (unten_frei)
9807     {
9808       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9809       Store[ax][ay+1] = element;
9810       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9811       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9812         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9813                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9814       new_wall = TRUE;
9815     }
9816   }
9817
9818   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9819       element == EL_EXPANDABLE_STEELWALL_ANY)
9820   {
9821     if (links_frei)
9822     {
9823       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9824       Store[ax-1][ay] = element;
9825       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9826       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9827         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9828                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9829       new_wall = TRUE;
9830     }
9831
9832     if (rechts_frei)
9833     {
9834       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9835       Store[ax+1][ay] = element;
9836       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9837       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9838         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9839                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9840       new_wall = TRUE;
9841     }
9842   }
9843
9844   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9845     oben_massiv = TRUE;
9846   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9847     unten_massiv = TRUE;
9848   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9849     links_massiv = TRUE;
9850   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9851     rechts_massiv = TRUE;
9852
9853   if (((oben_massiv && unten_massiv) ||
9854        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9855       ((links_massiv && rechts_massiv) ||
9856        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9857     Tile[ax][ay] = EL_STEELWALL;
9858
9859   if (new_wall)
9860     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9861 }
9862
9863 static void CheckForDragon(int x, int y)
9864 {
9865   int i, j;
9866   boolean dragon_found = FALSE;
9867   static int xy[4][2] =
9868   {
9869     { 0, -1 },
9870     { -1, 0 },
9871     { +1, 0 },
9872     { 0, +1 }
9873   };
9874
9875   for (i = 0; i < NUM_DIRECTIONS; i++)
9876   {
9877     for (j = 0; j < 4; j++)
9878     {
9879       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9880
9881       if (IN_LEV_FIELD(xx, yy) &&
9882           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9883       {
9884         if (Tile[xx][yy] == EL_DRAGON)
9885           dragon_found = TRUE;
9886       }
9887       else
9888         break;
9889     }
9890   }
9891
9892   if (!dragon_found)
9893   {
9894     for (i = 0; i < NUM_DIRECTIONS; i++)
9895     {
9896       for (j = 0; j < 3; j++)
9897       {
9898         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9899   
9900         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9901         {
9902           Tile[xx][yy] = EL_EMPTY;
9903           TEST_DrawLevelField(xx, yy);
9904         }
9905         else
9906           break;
9907       }
9908     }
9909   }
9910 }
9911
9912 static void InitBuggyBase(int x, int y)
9913 {
9914   int element = Tile[x][y];
9915   int activating_delay = FRAMES_PER_SECOND / 4;
9916
9917   ChangeDelay[x][y] =
9918     (element == EL_SP_BUGGY_BASE ?
9919      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9920      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9921      activating_delay :
9922      element == EL_SP_BUGGY_BASE_ACTIVE ?
9923      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9924 }
9925
9926 static void WarnBuggyBase(int x, int y)
9927 {
9928   int i;
9929   static int xy[4][2] =
9930   {
9931     { 0, -1 },
9932     { -1, 0 },
9933     { +1, 0 },
9934     { 0, +1 }
9935   };
9936
9937   for (i = 0; i < NUM_DIRECTIONS; i++)
9938   {
9939     int xx = x + xy[i][0];
9940     int yy = y + xy[i][1];
9941
9942     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9943     {
9944       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9945
9946       break;
9947     }
9948   }
9949 }
9950
9951 static void InitTrap(int x, int y)
9952 {
9953   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9954 }
9955
9956 static void ActivateTrap(int x, int y)
9957 {
9958   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9959 }
9960
9961 static void ChangeActiveTrap(int x, int y)
9962 {
9963   int graphic = IMG_TRAP_ACTIVE;
9964
9965   // if new animation frame was drawn, correct crumbled sand border
9966   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9967     TEST_DrawLevelFieldCrumbled(x, y);
9968 }
9969
9970 static int getSpecialActionElement(int element, int number, int base_element)
9971 {
9972   return (element != EL_EMPTY ? element :
9973           number != -1 ? base_element + number - 1 :
9974           EL_EMPTY);
9975 }
9976
9977 static int getModifiedActionNumber(int value_old, int operator, int operand,
9978                                    int value_min, int value_max)
9979 {
9980   int value_new = (operator == CA_MODE_SET      ? operand :
9981                    operator == CA_MODE_ADD      ? value_old + operand :
9982                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9983                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9984                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9985                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9986                    value_old);
9987
9988   return (value_new < value_min ? value_min :
9989           value_new > value_max ? value_max :
9990           value_new);
9991 }
9992
9993 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9994 {
9995   struct ElementInfo *ei = &element_info[element];
9996   struct ElementChangeInfo *change = &ei->change_page[page];
9997   int target_element = change->target_element;
9998   int action_type = change->action_type;
9999   int action_mode = change->action_mode;
10000   int action_arg = change->action_arg;
10001   int action_element = change->action_element;
10002   int i;
10003
10004   if (!change->has_action)
10005     return;
10006
10007   // ---------- determine action paramater values -----------------------------
10008
10009   int level_time_value =
10010     (level.time > 0 ? TimeLeft :
10011      TimePlayed);
10012
10013   int action_arg_element_raw =
10014     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10015      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10016      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10017      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10018      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10019      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10020      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10021      EL_EMPTY);
10022   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10023
10024   int action_arg_direction =
10025     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10026      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10027      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10028      change->actual_trigger_side :
10029      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10030      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10031      MV_NONE);
10032
10033   int action_arg_number_min =
10034     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10035      CA_ARG_MIN);
10036
10037   int action_arg_number_max =
10038     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10039      action_type == CA_SET_LEVEL_GEMS ? 999 :
10040      action_type == CA_SET_LEVEL_TIME ? 9999 :
10041      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10042      action_type == CA_SET_CE_VALUE ? 9999 :
10043      action_type == CA_SET_CE_SCORE ? 9999 :
10044      CA_ARG_MAX);
10045
10046   int action_arg_number_reset =
10047     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10048      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10049      action_type == CA_SET_LEVEL_TIME ? level.time :
10050      action_type == CA_SET_LEVEL_SCORE ? 0 :
10051      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10052      action_type == CA_SET_CE_SCORE ? 0 :
10053      0);
10054
10055   int action_arg_number =
10056     (action_arg <= CA_ARG_MAX ? action_arg :
10057      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10058      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10059      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10060      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10061      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10062      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10063      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10064      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10065      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10066      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10067      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10068      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10069      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10070      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10071      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10072      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10073      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10074      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10075      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10076      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10077      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10078      -1);
10079
10080   int action_arg_number_old =
10081     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10082      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10083      action_type == CA_SET_LEVEL_SCORE ? game.score :
10084      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10085      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10086      0);
10087
10088   int action_arg_number_new =
10089     getModifiedActionNumber(action_arg_number_old,
10090                             action_mode, action_arg_number,
10091                             action_arg_number_min, action_arg_number_max);
10092
10093   int trigger_player_bits =
10094     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10095      change->actual_trigger_player_bits : change->trigger_player);
10096
10097   int action_arg_player_bits =
10098     (action_arg >= CA_ARG_PLAYER_1 &&
10099      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10100      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10101      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10102      PLAYER_BITS_ANY);
10103
10104   // ---------- execute action  -----------------------------------------------
10105
10106   switch (action_type)
10107   {
10108     case CA_NO_ACTION:
10109     {
10110       return;
10111     }
10112
10113     // ---------- level actions  ----------------------------------------------
10114
10115     case CA_RESTART_LEVEL:
10116     {
10117       game.restart_level = TRUE;
10118
10119       break;
10120     }
10121
10122     case CA_SHOW_ENVELOPE:
10123     {
10124       int element = getSpecialActionElement(action_arg_element,
10125                                             action_arg_number, EL_ENVELOPE_1);
10126
10127       if (IS_ENVELOPE(element))
10128         local_player->show_envelope = element;
10129
10130       break;
10131     }
10132
10133     case CA_SET_LEVEL_TIME:
10134     {
10135       if (level.time > 0)       // only modify limited time value
10136       {
10137         TimeLeft = action_arg_number_new;
10138
10139         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10140
10141         DisplayGameControlValues();
10142
10143         if (!TimeLeft && setup.time_limit)
10144           for (i = 0; i < MAX_PLAYERS; i++)
10145             KillPlayer(&stored_player[i]);
10146       }
10147
10148       break;
10149     }
10150
10151     case CA_SET_LEVEL_SCORE:
10152     {
10153       game.score = action_arg_number_new;
10154
10155       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10156
10157       DisplayGameControlValues();
10158
10159       break;
10160     }
10161
10162     case CA_SET_LEVEL_GEMS:
10163     {
10164       game.gems_still_needed = action_arg_number_new;
10165
10166       game.snapshot.collected_item = TRUE;
10167
10168       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10169
10170       DisplayGameControlValues();
10171
10172       break;
10173     }
10174
10175     case CA_SET_LEVEL_WIND:
10176     {
10177       game.wind_direction = action_arg_direction;
10178
10179       break;
10180     }
10181
10182     case CA_SET_LEVEL_RANDOM_SEED:
10183     {
10184       // ensure that setting a new random seed while playing is predictable
10185       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10186
10187       break;
10188     }
10189
10190     // ---------- player actions  ---------------------------------------------
10191
10192     case CA_MOVE_PLAYER:
10193     case CA_MOVE_PLAYER_NEW:
10194     {
10195       // automatically move to the next field in specified direction
10196       for (i = 0; i < MAX_PLAYERS; i++)
10197         if (trigger_player_bits & (1 << i))
10198           if (action_type == CA_MOVE_PLAYER ||
10199               stored_player[i].MovPos == 0)
10200             stored_player[i].programmed_action = action_arg_direction;
10201
10202       break;
10203     }
10204
10205     case CA_EXIT_PLAYER:
10206     {
10207       for (i = 0; i < MAX_PLAYERS; i++)
10208         if (action_arg_player_bits & (1 << i))
10209           ExitPlayer(&stored_player[i]);
10210
10211       if (game.players_still_needed == 0)
10212         LevelSolved();
10213
10214       break;
10215     }
10216
10217     case CA_KILL_PLAYER:
10218     {
10219       for (i = 0; i < MAX_PLAYERS; i++)
10220         if (action_arg_player_bits & (1 << i))
10221           KillPlayer(&stored_player[i]);
10222
10223       break;
10224     }
10225
10226     case CA_SET_PLAYER_KEYS:
10227     {
10228       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10229       int element = getSpecialActionElement(action_arg_element,
10230                                             action_arg_number, EL_KEY_1);
10231
10232       if (IS_KEY(element))
10233       {
10234         for (i = 0; i < MAX_PLAYERS; i++)
10235         {
10236           if (trigger_player_bits & (1 << i))
10237           {
10238             stored_player[i].key[KEY_NR(element)] = key_state;
10239
10240             DrawGameDoorValues();
10241           }
10242         }
10243       }
10244
10245       break;
10246     }
10247
10248     case CA_SET_PLAYER_SPEED:
10249     {
10250       for (i = 0; i < MAX_PLAYERS; i++)
10251       {
10252         if (trigger_player_bits & (1 << i))
10253         {
10254           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10255
10256           if (action_arg == CA_ARG_SPEED_FASTER &&
10257               stored_player[i].cannot_move)
10258           {
10259             action_arg_number = STEPSIZE_VERY_SLOW;
10260           }
10261           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10262                    action_arg == CA_ARG_SPEED_FASTER)
10263           {
10264             action_arg_number = 2;
10265             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10266                            CA_MODE_MULTIPLY);
10267           }
10268           else if (action_arg == CA_ARG_NUMBER_RESET)
10269           {
10270             action_arg_number = level.initial_player_stepsize[i];
10271           }
10272
10273           move_stepsize =
10274             getModifiedActionNumber(move_stepsize,
10275                                     action_mode,
10276                                     action_arg_number,
10277                                     action_arg_number_min,
10278                                     action_arg_number_max);
10279
10280           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10281         }
10282       }
10283
10284       break;
10285     }
10286
10287     case CA_SET_PLAYER_SHIELD:
10288     {
10289       for (i = 0; i < MAX_PLAYERS; i++)
10290       {
10291         if (trigger_player_bits & (1 << i))
10292         {
10293           if (action_arg == CA_ARG_SHIELD_OFF)
10294           {
10295             stored_player[i].shield_normal_time_left = 0;
10296             stored_player[i].shield_deadly_time_left = 0;
10297           }
10298           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10299           {
10300             stored_player[i].shield_normal_time_left = 999999;
10301           }
10302           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10303           {
10304             stored_player[i].shield_normal_time_left = 999999;
10305             stored_player[i].shield_deadly_time_left = 999999;
10306           }
10307         }
10308       }
10309
10310       break;
10311     }
10312
10313     case CA_SET_PLAYER_GRAVITY:
10314     {
10315       for (i = 0; i < MAX_PLAYERS; i++)
10316       {
10317         if (trigger_player_bits & (1 << i))
10318         {
10319           stored_player[i].gravity =
10320             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10321              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10322              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10323              stored_player[i].gravity);
10324         }
10325       }
10326
10327       break;
10328     }
10329
10330     case CA_SET_PLAYER_ARTWORK:
10331     {
10332       for (i = 0; i < MAX_PLAYERS; i++)
10333       {
10334         if (trigger_player_bits & (1 << i))
10335         {
10336           int artwork_element = action_arg_element;
10337
10338           if (action_arg == CA_ARG_ELEMENT_RESET)
10339             artwork_element =
10340               (level.use_artwork_element[i] ? level.artwork_element[i] :
10341                stored_player[i].element_nr);
10342
10343           if (stored_player[i].artwork_element != artwork_element)
10344             stored_player[i].Frame = 0;
10345
10346           stored_player[i].artwork_element = artwork_element;
10347
10348           SetPlayerWaiting(&stored_player[i], FALSE);
10349
10350           // set number of special actions for bored and sleeping animation
10351           stored_player[i].num_special_action_bored =
10352             get_num_special_action(artwork_element,
10353                                    ACTION_BORING_1, ACTION_BORING_LAST);
10354           stored_player[i].num_special_action_sleeping =
10355             get_num_special_action(artwork_element,
10356                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10357         }
10358       }
10359
10360       break;
10361     }
10362
10363     case CA_SET_PLAYER_INVENTORY:
10364     {
10365       for (i = 0; i < MAX_PLAYERS; i++)
10366       {
10367         struct PlayerInfo *player = &stored_player[i];
10368         int j, k;
10369
10370         if (trigger_player_bits & (1 << i))
10371         {
10372           int inventory_element = action_arg_element;
10373
10374           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10375               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10376               action_arg == CA_ARG_ELEMENT_ACTION)
10377           {
10378             int element = inventory_element;
10379             int collect_count = element_info[element].collect_count_initial;
10380
10381             if (!IS_CUSTOM_ELEMENT(element))
10382               collect_count = 1;
10383
10384             if (collect_count == 0)
10385               player->inventory_infinite_element = element;
10386             else
10387               for (k = 0; k < collect_count; k++)
10388                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10389                   player->inventory_element[player->inventory_size++] =
10390                     element;
10391           }
10392           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10393                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10394                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10395           {
10396             if (player->inventory_infinite_element != EL_UNDEFINED &&
10397                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10398                                      action_arg_element_raw))
10399               player->inventory_infinite_element = EL_UNDEFINED;
10400
10401             for (k = 0, j = 0; j < player->inventory_size; j++)
10402             {
10403               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10404                                         action_arg_element_raw))
10405                 player->inventory_element[k++] = player->inventory_element[j];
10406             }
10407
10408             player->inventory_size = k;
10409           }
10410           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10411           {
10412             if (player->inventory_size > 0)
10413             {
10414               for (j = 0; j < player->inventory_size - 1; j++)
10415                 player->inventory_element[j] = player->inventory_element[j + 1];
10416
10417               player->inventory_size--;
10418             }
10419           }
10420           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10421           {
10422             if (player->inventory_size > 0)
10423               player->inventory_size--;
10424           }
10425           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10426           {
10427             player->inventory_infinite_element = EL_UNDEFINED;
10428             player->inventory_size = 0;
10429           }
10430           else if (action_arg == CA_ARG_INVENTORY_RESET)
10431           {
10432             player->inventory_infinite_element = EL_UNDEFINED;
10433             player->inventory_size = 0;
10434
10435             if (level.use_initial_inventory[i])
10436             {
10437               for (j = 0; j < level.initial_inventory_size[i]; j++)
10438               {
10439                 int element = level.initial_inventory_content[i][j];
10440                 int collect_count = element_info[element].collect_count_initial;
10441
10442                 if (!IS_CUSTOM_ELEMENT(element))
10443                   collect_count = 1;
10444
10445                 if (collect_count == 0)
10446                   player->inventory_infinite_element = element;
10447                 else
10448                   for (k = 0; k < collect_count; k++)
10449                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10450                       player->inventory_element[player->inventory_size++] =
10451                         element;
10452               }
10453             }
10454           }
10455         }
10456       }
10457
10458       break;
10459     }
10460
10461     // ---------- CE actions  -------------------------------------------------
10462
10463     case CA_SET_CE_VALUE:
10464     {
10465       int last_ce_value = CustomValue[x][y];
10466
10467       CustomValue[x][y] = action_arg_number_new;
10468
10469       if (CustomValue[x][y] != last_ce_value)
10470       {
10471         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10472         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10473
10474         if (CustomValue[x][y] == 0)
10475         {
10476           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10477           ChangeCount[x][y] = 0;        // allow at least one more change
10478
10479           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10480           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10481         }
10482       }
10483
10484       break;
10485     }
10486
10487     case CA_SET_CE_SCORE:
10488     {
10489       int last_ce_score = ei->collect_score;
10490
10491       ei->collect_score = action_arg_number_new;
10492
10493       if (ei->collect_score != last_ce_score)
10494       {
10495         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10496         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10497
10498         if (ei->collect_score == 0)
10499         {
10500           int xx, yy;
10501
10502           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10503           ChangeCount[x][y] = 0;        // allow at least one more change
10504
10505           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10506           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10507
10508           /*
10509             This is a very special case that seems to be a mixture between
10510             CheckElementChange() and CheckTriggeredElementChange(): while
10511             the first one only affects single elements that are triggered
10512             directly, the second one affects multiple elements in the playfield
10513             that are triggered indirectly by another element. This is a third
10514             case: Changing the CE score always affects multiple identical CEs,
10515             so every affected CE must be checked, not only the single CE for
10516             which the CE score was changed in the first place (as every instance
10517             of that CE shares the same CE score, and therefore also can change)!
10518           */
10519           SCAN_PLAYFIELD(xx, yy)
10520           {
10521             if (Tile[xx][yy] == element)
10522               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10523                                  CE_SCORE_GETS_ZERO);
10524           }
10525         }
10526       }
10527
10528       break;
10529     }
10530
10531     case CA_SET_CE_ARTWORK:
10532     {
10533       int artwork_element = action_arg_element;
10534       boolean reset_frame = FALSE;
10535       int xx, yy;
10536
10537       if (action_arg == CA_ARG_ELEMENT_RESET)
10538         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10539                            element);
10540
10541       if (ei->gfx_element != artwork_element)
10542         reset_frame = TRUE;
10543
10544       ei->gfx_element = artwork_element;
10545
10546       SCAN_PLAYFIELD(xx, yy)
10547       {
10548         if (Tile[xx][yy] == element)
10549         {
10550           if (reset_frame)
10551           {
10552             ResetGfxAnimation(xx, yy);
10553             ResetRandomAnimationValue(xx, yy);
10554           }
10555
10556           TEST_DrawLevelField(xx, yy);
10557         }
10558       }
10559
10560       break;
10561     }
10562
10563     // ---------- engine actions  ---------------------------------------------
10564
10565     case CA_SET_ENGINE_SCAN_MODE:
10566     {
10567       InitPlayfieldScanMode(action_arg);
10568
10569       break;
10570     }
10571
10572     default:
10573       break;
10574   }
10575 }
10576
10577 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10578 {
10579   int old_element = Tile[x][y];
10580   int new_element = GetElementFromGroupElement(element);
10581   int previous_move_direction = MovDir[x][y];
10582   int last_ce_value = CustomValue[x][y];
10583   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10584   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10585   boolean add_player_onto_element = (new_element_is_player &&
10586                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10587                                      IS_WALKABLE(old_element));
10588
10589   if (!add_player_onto_element)
10590   {
10591     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10592       RemoveMovingField(x, y);
10593     else
10594       RemoveField(x, y);
10595
10596     Tile[x][y] = new_element;
10597
10598     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10599       MovDir[x][y] = previous_move_direction;
10600
10601     if (element_info[new_element].use_last_ce_value)
10602       CustomValue[x][y] = last_ce_value;
10603
10604     InitField_WithBug1(x, y, FALSE);
10605
10606     new_element = Tile[x][y];   // element may have changed
10607
10608     ResetGfxAnimation(x, y);
10609     ResetRandomAnimationValue(x, y);
10610
10611     TEST_DrawLevelField(x, y);
10612
10613     if (GFX_CRUMBLED(new_element))
10614       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10615   }
10616
10617   // check if element under the player changes from accessible to unaccessible
10618   // (needed for special case of dropping element which then changes)
10619   // (must be checked after creating new element for walkable group elements)
10620   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10621       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10622   {
10623     Bang(x, y);
10624
10625     return;
10626   }
10627
10628   // "ChangeCount" not set yet to allow "entered by player" change one time
10629   if (new_element_is_player)
10630     RelocatePlayer(x, y, new_element);
10631
10632   if (is_change)
10633     ChangeCount[x][y]++;        // count number of changes in the same frame
10634
10635   TestIfBadThingTouchesPlayer(x, y);
10636   TestIfPlayerTouchesCustomElement(x, y);
10637   TestIfElementTouchesCustomElement(x, y);
10638 }
10639
10640 static void CreateField(int x, int y, int element)
10641 {
10642   CreateFieldExt(x, y, element, FALSE);
10643 }
10644
10645 static void CreateElementFromChange(int x, int y, int element)
10646 {
10647   element = GET_VALID_RUNTIME_ELEMENT(element);
10648
10649   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10650   {
10651     int old_element = Tile[x][y];
10652
10653     // prevent changed element from moving in same engine frame
10654     // unless both old and new element can either fall or move
10655     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10656         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10657       Stop[x][y] = TRUE;
10658   }
10659
10660   CreateFieldExt(x, y, element, TRUE);
10661 }
10662
10663 static boolean ChangeElement(int x, int y, int element, int page)
10664 {
10665   struct ElementInfo *ei = &element_info[element];
10666   struct ElementChangeInfo *change = &ei->change_page[page];
10667   int ce_value = CustomValue[x][y];
10668   int ce_score = ei->collect_score;
10669   int target_element;
10670   int old_element = Tile[x][y];
10671
10672   // always use default change event to prevent running into a loop
10673   if (ChangeEvent[x][y] == -1)
10674     ChangeEvent[x][y] = CE_DELAY;
10675
10676   if (ChangeEvent[x][y] == CE_DELAY)
10677   {
10678     // reset actual trigger element, trigger player and action element
10679     change->actual_trigger_element = EL_EMPTY;
10680     change->actual_trigger_player = EL_EMPTY;
10681     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10682     change->actual_trigger_side = CH_SIDE_NONE;
10683     change->actual_trigger_ce_value = 0;
10684     change->actual_trigger_ce_score = 0;
10685   }
10686
10687   // do not change elements more than a specified maximum number of changes
10688   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10689     return FALSE;
10690
10691   ChangeCount[x][y]++;          // count number of changes in the same frame
10692
10693   if (change->explode)
10694   {
10695     Bang(x, y);
10696
10697     return TRUE;
10698   }
10699
10700   if (change->use_target_content)
10701   {
10702     boolean complete_replace = TRUE;
10703     boolean can_replace[3][3];
10704     int xx, yy;
10705
10706     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10707     {
10708       boolean is_empty;
10709       boolean is_walkable;
10710       boolean is_diggable;
10711       boolean is_collectible;
10712       boolean is_removable;
10713       boolean is_destructible;
10714       int ex = x + xx - 1;
10715       int ey = y + yy - 1;
10716       int content_element = change->target_content.e[xx][yy];
10717       int e;
10718
10719       can_replace[xx][yy] = TRUE;
10720
10721       if (ex == x && ey == y)   // do not check changing element itself
10722         continue;
10723
10724       if (content_element == EL_EMPTY_SPACE)
10725       {
10726         can_replace[xx][yy] = FALSE;    // do not replace border with space
10727
10728         continue;
10729       }
10730
10731       if (!IN_LEV_FIELD(ex, ey))
10732       {
10733         can_replace[xx][yy] = FALSE;
10734         complete_replace = FALSE;
10735
10736         continue;
10737       }
10738
10739       e = Tile[ex][ey];
10740
10741       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10742         e = MovingOrBlocked2Element(ex, ey);
10743
10744       is_empty = (IS_FREE(ex, ey) ||
10745                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10746
10747       is_walkable     = (is_empty || IS_WALKABLE(e));
10748       is_diggable     = (is_empty || IS_DIGGABLE(e));
10749       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10750       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10751       is_removable    = (is_diggable || is_collectible);
10752
10753       can_replace[xx][yy] =
10754         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10755           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10756           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10757           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10758           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10759           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10760          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10761
10762       if (!can_replace[xx][yy])
10763         complete_replace = FALSE;
10764     }
10765
10766     if (!change->only_if_complete || complete_replace)
10767     {
10768       boolean something_has_changed = FALSE;
10769
10770       if (change->only_if_complete && change->use_random_replace &&
10771           RND(100) < change->random_percentage)
10772         return FALSE;
10773
10774       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10775       {
10776         int ex = x + xx - 1;
10777         int ey = y + yy - 1;
10778         int content_element;
10779
10780         if (can_replace[xx][yy] && (!change->use_random_replace ||
10781                                     RND(100) < change->random_percentage))
10782         {
10783           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10784             RemoveMovingField(ex, ey);
10785
10786           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10787
10788           content_element = change->target_content.e[xx][yy];
10789           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10790                                               ce_value, ce_score);
10791
10792           CreateElementFromChange(ex, ey, target_element);
10793
10794           something_has_changed = TRUE;
10795
10796           // for symmetry reasons, freeze newly created border elements
10797           if (ex != x || ey != y)
10798             Stop[ex][ey] = TRUE;        // no more moving in this frame
10799         }
10800       }
10801
10802       if (something_has_changed)
10803       {
10804         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10805         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10806       }
10807     }
10808   }
10809   else
10810   {
10811     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10812                                         ce_value, ce_score);
10813
10814     if (element == EL_DIAGONAL_GROWING ||
10815         element == EL_DIAGONAL_SHRINKING)
10816     {
10817       target_element = Store[x][y];
10818
10819       Store[x][y] = EL_EMPTY;
10820     }
10821
10822     CreateElementFromChange(x, y, target_element);
10823
10824     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10825     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10826   }
10827
10828   // this uses direct change before indirect change
10829   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10830
10831   return TRUE;
10832 }
10833
10834 static void HandleElementChange(int x, int y, int page)
10835 {
10836   int element = MovingOrBlocked2Element(x, y);
10837   struct ElementInfo *ei = &element_info[element];
10838   struct ElementChangeInfo *change = &ei->change_page[page];
10839   boolean handle_action_before_change = FALSE;
10840
10841 #ifdef DEBUG
10842   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10843       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10844   {
10845     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10846           x, y, element, element_info[element].token_name);
10847     Debug("game:playing:HandleElementChange", "This should never happen!");
10848   }
10849 #endif
10850
10851   // this can happen with classic bombs on walkable, changing elements
10852   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10853   {
10854     return;
10855   }
10856
10857   if (ChangeDelay[x][y] == 0)           // initialize element change
10858   {
10859     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10860
10861     if (change->can_change)
10862     {
10863       // !!! not clear why graphic animation should be reset at all here !!!
10864       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10865       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10866
10867       /*
10868         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10869
10870         When using an animation frame delay of 1 (this only happens with
10871         "sp_zonk.moving.left/right" in the classic graphics), the default
10872         (non-moving) animation shows wrong animation frames (while the
10873         moving animation, like "sp_zonk.moving.left/right", is correct,
10874         so this graphical bug never shows up with the classic graphics).
10875         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10876         be drawn instead of the correct frames 0,1,2,3. This is caused by
10877         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10878         an element change: First when the change delay ("ChangeDelay[][]")
10879         counter has reached zero after decrementing, then a second time in
10880         the next frame (after "GfxFrame[][]" was already incremented) when
10881         "ChangeDelay[][]" is reset to the initial delay value again.
10882
10883         This causes frame 0 to be drawn twice, while the last frame won't
10884         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10885
10886         As some animations may already be cleverly designed around this bug
10887         (at least the "Snake Bite" snake tail animation does this), it cannot
10888         simply be fixed here without breaking such existing animations.
10889         Unfortunately, it cannot easily be detected if a graphics set was
10890         designed "before" or "after" the bug was fixed. As a workaround,
10891         a new graphics set option "game.graphics_engine_version" was added
10892         to be able to specify the game's major release version for which the
10893         graphics set was designed, which can then be used to decide if the
10894         bugfix should be used (version 4 and above) or not (version 3 or
10895         below, or if no version was specified at all, as with old sets).
10896
10897         (The wrong/fixed animation frames can be tested with the test level set
10898         "test_gfxframe" and level "000", which contains a specially prepared
10899         custom element at level position (x/y) == (11/9) which uses the zonk
10900         animation mentioned above. Using "game.graphics_engine_version: 4"
10901         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10902         This can also be seen from the debug output for this test element.)
10903       */
10904
10905       // when a custom element is about to change (for example by change delay),
10906       // do not reset graphic animation when the custom element is moving
10907       if (game.graphics_engine_version < 4 &&
10908           !IS_MOVING(x, y))
10909       {
10910         ResetGfxAnimation(x, y);
10911         ResetRandomAnimationValue(x, y);
10912       }
10913
10914       if (change->pre_change_function)
10915         change->pre_change_function(x, y);
10916     }
10917   }
10918
10919   ChangeDelay[x][y]--;
10920
10921   if (ChangeDelay[x][y] != 0)           // continue element change
10922   {
10923     if (change->can_change)
10924     {
10925       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10926
10927       if (IS_ANIMATED(graphic))
10928         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10929
10930       if (change->change_function)
10931         change->change_function(x, y);
10932     }
10933   }
10934   else                                  // finish element change
10935   {
10936     if (ChangePage[x][y] != -1)         // remember page from delayed change
10937     {
10938       page = ChangePage[x][y];
10939       ChangePage[x][y] = -1;
10940
10941       change = &ei->change_page[page];
10942     }
10943
10944     if (IS_MOVING(x, y))                // never change a running system ;-)
10945     {
10946       ChangeDelay[x][y] = 1;            // try change after next move step
10947       ChangePage[x][y] = page;          // remember page to use for change
10948
10949       return;
10950     }
10951
10952     // special case: set new level random seed before changing element
10953     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10954       handle_action_before_change = TRUE;
10955
10956     if (change->has_action && handle_action_before_change)
10957       ExecuteCustomElementAction(x, y, element, page);
10958
10959     if (change->can_change)
10960     {
10961       if (ChangeElement(x, y, element, page))
10962       {
10963         if (change->post_change_function)
10964           change->post_change_function(x, y);
10965       }
10966     }
10967
10968     if (change->has_action && !handle_action_before_change)
10969       ExecuteCustomElementAction(x, y, element, page);
10970   }
10971 }
10972
10973 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10974                                               int trigger_element,
10975                                               int trigger_event,
10976                                               int trigger_player,
10977                                               int trigger_side,
10978                                               int trigger_page)
10979 {
10980   boolean change_done_any = FALSE;
10981   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10982   int i;
10983
10984   if (!(trigger_events[trigger_element][trigger_event]))
10985     return FALSE;
10986
10987   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10988
10989   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10990   {
10991     int element = EL_CUSTOM_START + i;
10992     boolean change_done = FALSE;
10993     int p;
10994
10995     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10996         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10997       continue;
10998
10999     for (p = 0; p < element_info[element].num_change_pages; p++)
11000     {
11001       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11002
11003       if (change->can_change_or_has_action &&
11004           change->has_event[trigger_event] &&
11005           change->trigger_side & trigger_side &&
11006           change->trigger_player & trigger_player &&
11007           change->trigger_page & trigger_page_bits &&
11008           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11009       {
11010         change->actual_trigger_element = trigger_element;
11011         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11012         change->actual_trigger_player_bits = trigger_player;
11013         change->actual_trigger_side = trigger_side;
11014         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11015         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11016
11017         if ((change->can_change && !change_done) || change->has_action)
11018         {
11019           int x, y;
11020
11021           SCAN_PLAYFIELD(x, y)
11022           {
11023             if (Tile[x][y] == element)
11024             {
11025               if (change->can_change && !change_done)
11026               {
11027                 // if element already changed in this frame, not only prevent
11028                 // another element change (checked in ChangeElement()), but
11029                 // also prevent additional element actions for this element
11030
11031                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11032                     !level.use_action_after_change_bug)
11033                   continue;
11034
11035                 ChangeDelay[x][y] = 1;
11036                 ChangeEvent[x][y] = trigger_event;
11037
11038                 HandleElementChange(x, y, p);
11039               }
11040               else if (change->has_action)
11041               {
11042                 // if element already changed in this frame, not only prevent
11043                 // another element change (checked in ChangeElement()), but
11044                 // also prevent additional element actions for this element
11045
11046                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11047                     !level.use_action_after_change_bug)
11048                   continue;
11049
11050                 ExecuteCustomElementAction(x, y, element, p);
11051                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11052               }
11053             }
11054           }
11055
11056           if (change->can_change)
11057           {
11058             change_done = TRUE;
11059             change_done_any = TRUE;
11060           }
11061         }
11062       }
11063     }
11064   }
11065
11066   RECURSION_LOOP_DETECTION_END();
11067
11068   return change_done_any;
11069 }
11070
11071 static boolean CheckElementChangeExt(int x, int y,
11072                                      int element,
11073                                      int trigger_element,
11074                                      int trigger_event,
11075                                      int trigger_player,
11076                                      int trigger_side)
11077 {
11078   boolean change_done = FALSE;
11079   int p;
11080
11081   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11082       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11083     return FALSE;
11084
11085   if (Tile[x][y] == EL_BLOCKED)
11086   {
11087     Blocked2Moving(x, y, &x, &y);
11088     element = Tile[x][y];
11089   }
11090
11091   // check if element has already changed or is about to change after moving
11092   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11093        Tile[x][y] != element) ||
11094
11095       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11096        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11097         ChangePage[x][y] != -1)))
11098     return FALSE;
11099
11100   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11101
11102   for (p = 0; p < element_info[element].num_change_pages; p++)
11103   {
11104     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11105
11106     /* check trigger element for all events where the element that is checked
11107        for changing interacts with a directly adjacent element -- this is
11108        different to element changes that affect other elements to change on the
11109        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11110     boolean check_trigger_element =
11111       (trigger_event == CE_TOUCHING_X ||
11112        trigger_event == CE_HITTING_X ||
11113        trigger_event == CE_HIT_BY_X ||
11114        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11115
11116     if (change->can_change_or_has_action &&
11117         change->has_event[trigger_event] &&
11118         change->trigger_side & trigger_side &&
11119         change->trigger_player & trigger_player &&
11120         (!check_trigger_element ||
11121          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11122     {
11123       change->actual_trigger_element = trigger_element;
11124       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11125       change->actual_trigger_player_bits = trigger_player;
11126       change->actual_trigger_side = trigger_side;
11127       change->actual_trigger_ce_value = CustomValue[x][y];
11128       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11129
11130       // special case: trigger element not at (x,y) position for some events
11131       if (check_trigger_element)
11132       {
11133         static struct
11134         {
11135           int dx, dy;
11136         } move_xy[] =
11137           {
11138             {  0,  0 },
11139             { -1,  0 },
11140             { +1,  0 },
11141             {  0,  0 },
11142             {  0, -1 },
11143             {  0,  0 }, { 0, 0 }, { 0, 0 },
11144             {  0, +1 }
11145           };
11146
11147         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11148         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11149
11150         change->actual_trigger_ce_value = CustomValue[xx][yy];
11151         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11152       }
11153
11154       if (change->can_change && !change_done)
11155       {
11156         ChangeDelay[x][y] = 1;
11157         ChangeEvent[x][y] = trigger_event;
11158
11159         HandleElementChange(x, y, p);
11160
11161         change_done = TRUE;
11162       }
11163       else if (change->has_action)
11164       {
11165         ExecuteCustomElementAction(x, y, element, p);
11166         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11167       }
11168     }
11169   }
11170
11171   RECURSION_LOOP_DETECTION_END();
11172
11173   return change_done;
11174 }
11175
11176 static void PlayPlayerSound(struct PlayerInfo *player)
11177 {
11178   int jx = player->jx, jy = player->jy;
11179   int sound_element = player->artwork_element;
11180   int last_action = player->last_action_waiting;
11181   int action = player->action_waiting;
11182
11183   if (player->is_waiting)
11184   {
11185     if (action != last_action)
11186       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11187     else
11188       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11189   }
11190   else
11191   {
11192     if (action != last_action)
11193       StopSound(element_info[sound_element].sound[last_action]);
11194
11195     if (last_action == ACTION_SLEEPING)
11196       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11197   }
11198 }
11199
11200 static void PlayAllPlayersSound(void)
11201 {
11202   int i;
11203
11204   for (i = 0; i < MAX_PLAYERS; i++)
11205     if (stored_player[i].active)
11206       PlayPlayerSound(&stored_player[i]);
11207 }
11208
11209 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11210 {
11211   boolean last_waiting = player->is_waiting;
11212   int move_dir = player->MovDir;
11213
11214   player->dir_waiting = move_dir;
11215   player->last_action_waiting = player->action_waiting;
11216
11217   if (is_waiting)
11218   {
11219     if (!last_waiting)          // not waiting -> waiting
11220     {
11221       player->is_waiting = TRUE;
11222
11223       player->frame_counter_bored =
11224         FrameCounter +
11225         game.player_boring_delay_fixed +
11226         GetSimpleRandom(game.player_boring_delay_random);
11227       player->frame_counter_sleeping =
11228         FrameCounter +
11229         game.player_sleeping_delay_fixed +
11230         GetSimpleRandom(game.player_sleeping_delay_random);
11231
11232       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11233     }
11234
11235     if (game.player_sleeping_delay_fixed +
11236         game.player_sleeping_delay_random > 0 &&
11237         player->anim_delay_counter == 0 &&
11238         player->post_delay_counter == 0 &&
11239         FrameCounter >= player->frame_counter_sleeping)
11240       player->is_sleeping = TRUE;
11241     else if (game.player_boring_delay_fixed +
11242              game.player_boring_delay_random > 0 &&
11243              FrameCounter >= player->frame_counter_bored)
11244       player->is_bored = TRUE;
11245
11246     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11247                               player->is_bored ? ACTION_BORING :
11248                               ACTION_WAITING);
11249
11250     if (player->is_sleeping && player->use_murphy)
11251     {
11252       // special case for sleeping Murphy when leaning against non-free tile
11253
11254       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11255           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11256            !IS_MOVING(player->jx - 1, player->jy)))
11257         move_dir = MV_LEFT;
11258       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11259                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11260                 !IS_MOVING(player->jx + 1, player->jy)))
11261         move_dir = MV_RIGHT;
11262       else
11263         player->is_sleeping = FALSE;
11264
11265       player->dir_waiting = move_dir;
11266     }
11267
11268     if (player->is_sleeping)
11269     {
11270       if (player->num_special_action_sleeping > 0)
11271       {
11272         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11273         {
11274           int last_special_action = player->special_action_sleeping;
11275           int num_special_action = player->num_special_action_sleeping;
11276           int special_action =
11277             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11278              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11279              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11280              last_special_action + 1 : ACTION_SLEEPING);
11281           int special_graphic =
11282             el_act_dir2img(player->artwork_element, special_action, move_dir);
11283
11284           player->anim_delay_counter =
11285             graphic_info[special_graphic].anim_delay_fixed +
11286             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11287           player->post_delay_counter =
11288             graphic_info[special_graphic].post_delay_fixed +
11289             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11290
11291           player->special_action_sleeping = special_action;
11292         }
11293
11294         if (player->anim_delay_counter > 0)
11295         {
11296           player->action_waiting = player->special_action_sleeping;
11297           player->anim_delay_counter--;
11298         }
11299         else if (player->post_delay_counter > 0)
11300         {
11301           player->post_delay_counter--;
11302         }
11303       }
11304     }
11305     else if (player->is_bored)
11306     {
11307       if (player->num_special_action_bored > 0)
11308       {
11309         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11310         {
11311           int special_action =
11312             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11313           int special_graphic =
11314             el_act_dir2img(player->artwork_element, special_action, move_dir);
11315
11316           player->anim_delay_counter =
11317             graphic_info[special_graphic].anim_delay_fixed +
11318             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11319           player->post_delay_counter =
11320             graphic_info[special_graphic].post_delay_fixed +
11321             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11322
11323           player->special_action_bored = special_action;
11324         }
11325
11326         if (player->anim_delay_counter > 0)
11327         {
11328           player->action_waiting = player->special_action_bored;
11329           player->anim_delay_counter--;
11330         }
11331         else if (player->post_delay_counter > 0)
11332         {
11333           player->post_delay_counter--;
11334         }
11335       }
11336     }
11337   }
11338   else if (last_waiting)        // waiting -> not waiting
11339   {
11340     player->is_waiting = FALSE;
11341     player->is_bored = FALSE;
11342     player->is_sleeping = FALSE;
11343
11344     player->frame_counter_bored = -1;
11345     player->frame_counter_sleeping = -1;
11346
11347     player->anim_delay_counter = 0;
11348     player->post_delay_counter = 0;
11349
11350     player->dir_waiting = player->MovDir;
11351     player->action_waiting = ACTION_DEFAULT;
11352
11353     player->special_action_bored = ACTION_DEFAULT;
11354     player->special_action_sleeping = ACTION_DEFAULT;
11355   }
11356 }
11357
11358 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11359 {
11360   if ((!player->is_moving  && player->was_moving) ||
11361       (player->MovPos == 0 && player->was_moving) ||
11362       (player->is_snapping && !player->was_snapping) ||
11363       (player->is_dropping && !player->was_dropping))
11364   {
11365     if (!CheckSaveEngineSnapshotToList())
11366       return;
11367
11368     player->was_moving = FALSE;
11369     player->was_snapping = TRUE;
11370     player->was_dropping = TRUE;
11371   }
11372   else
11373   {
11374     if (player->is_moving)
11375       player->was_moving = TRUE;
11376
11377     if (!player->is_snapping)
11378       player->was_snapping = FALSE;
11379
11380     if (!player->is_dropping)
11381       player->was_dropping = FALSE;
11382   }
11383
11384   static struct MouseActionInfo mouse_action_last = { 0 };
11385   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11386   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11387
11388   if (new_released)
11389     CheckSaveEngineSnapshotToList();
11390
11391   mouse_action_last = mouse_action;
11392 }
11393
11394 static void CheckSingleStepMode(struct PlayerInfo *player)
11395 {
11396   if (tape.single_step && tape.recording && !tape.pausing)
11397   {
11398     // as it is called "single step mode", just return to pause mode when the
11399     // player stopped moving after one tile (or never starts moving at all)
11400     // (reverse logic needed here in case single step mode used in team mode)
11401     if (player->is_moving ||
11402         player->is_pushing ||
11403         player->is_dropping_pressed ||
11404         player->effective_mouse_action.button)
11405       game.enter_single_step_mode = FALSE;
11406   }
11407
11408   CheckSaveEngineSnapshot(player);
11409 }
11410
11411 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11412 {
11413   int left      = player_action & JOY_LEFT;
11414   int right     = player_action & JOY_RIGHT;
11415   int up        = player_action & JOY_UP;
11416   int down      = player_action & JOY_DOWN;
11417   int button1   = player_action & JOY_BUTTON_1;
11418   int button2   = player_action & JOY_BUTTON_2;
11419   int dx        = (left ? -1 : right ? 1 : 0);
11420   int dy        = (up   ? -1 : down  ? 1 : 0);
11421
11422   if (!player->active || tape.pausing)
11423     return 0;
11424
11425   if (player_action)
11426   {
11427     if (button1)
11428       SnapField(player, dx, dy);
11429     else
11430     {
11431       if (button2)
11432         DropElement(player);
11433
11434       MovePlayer(player, dx, dy);
11435     }
11436
11437     CheckSingleStepMode(player);
11438
11439     SetPlayerWaiting(player, FALSE);
11440
11441     return player_action;
11442   }
11443   else
11444   {
11445     // no actions for this player (no input at player's configured device)
11446
11447     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11448     SnapField(player, 0, 0);
11449     CheckGravityMovementWhenNotMoving(player);
11450
11451     if (player->MovPos == 0)
11452       SetPlayerWaiting(player, TRUE);
11453
11454     if (player->MovPos == 0)    // needed for tape.playing
11455       player->is_moving = FALSE;
11456
11457     player->is_dropping = FALSE;
11458     player->is_dropping_pressed = FALSE;
11459     player->drop_pressed_delay = 0;
11460
11461     CheckSingleStepMode(player);
11462
11463     return 0;
11464   }
11465 }
11466
11467 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11468                                          byte *tape_action)
11469 {
11470   if (!tape.use_mouse_actions)
11471     return;
11472
11473   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11474   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11475   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11476 }
11477
11478 static void SetTapeActionFromMouseAction(byte *tape_action,
11479                                          struct MouseActionInfo *mouse_action)
11480 {
11481   if (!tape.use_mouse_actions)
11482     return;
11483
11484   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11485   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11486   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11487 }
11488
11489 static void CheckLevelSolved(void)
11490 {
11491   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11492   {
11493     if (game_em.level_solved &&
11494         !game_em.game_over)                             // game won
11495     {
11496       LevelSolved();
11497
11498       game_em.game_over = TRUE;
11499
11500       game.all_players_gone = TRUE;
11501     }
11502
11503     if (game_em.game_over)                              // game lost
11504       game.all_players_gone = TRUE;
11505   }
11506   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11507   {
11508     if (game_sp.level_solved &&
11509         !game_sp.game_over)                             // game won
11510     {
11511       LevelSolved();
11512
11513       game_sp.game_over = TRUE;
11514
11515       game.all_players_gone = TRUE;
11516     }
11517
11518     if (game_sp.game_over)                              // game lost
11519       game.all_players_gone = TRUE;
11520   }
11521   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11522   {
11523     if (game_mm.level_solved &&
11524         !game_mm.game_over)                             // game won
11525     {
11526       LevelSolved();
11527
11528       game_mm.game_over = TRUE;
11529
11530       game.all_players_gone = TRUE;
11531     }
11532
11533     if (game_mm.game_over)                              // game lost
11534       game.all_players_gone = TRUE;
11535   }
11536 }
11537
11538 static void CheckLevelTime(void)
11539 {
11540   int i;
11541
11542   if (TimeFrames >= FRAMES_PER_SECOND)
11543   {
11544     TimeFrames = 0;
11545     TapeTime++;
11546
11547     for (i = 0; i < MAX_PLAYERS; i++)
11548     {
11549       struct PlayerInfo *player = &stored_player[i];
11550
11551       if (SHIELD_ON(player))
11552       {
11553         player->shield_normal_time_left--;
11554
11555         if (player->shield_deadly_time_left > 0)
11556           player->shield_deadly_time_left--;
11557       }
11558     }
11559
11560     if (!game.LevelSolved && !level.use_step_counter)
11561     {
11562       TimePlayed++;
11563
11564       if (TimeLeft > 0)
11565       {
11566         TimeLeft--;
11567
11568         if (TimeLeft <= 10 && setup.time_limit)
11569           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11570
11571         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11572            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11573
11574         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11575
11576         if (!TimeLeft && setup.time_limit)
11577         {
11578           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11579             game_em.lev->killed_out_of_time = TRUE;
11580           else
11581             for (i = 0; i < MAX_PLAYERS; i++)
11582               KillPlayer(&stored_player[i]);
11583         }
11584       }
11585       else if (game.no_time_limit && !game.all_players_gone)
11586       {
11587         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11588       }
11589
11590       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11591     }
11592
11593     if (tape.recording || tape.playing)
11594       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11595   }
11596
11597   if (tape.recording || tape.playing)
11598     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11599
11600   UpdateAndDisplayGameControlValues();
11601 }
11602
11603 void AdvanceFrameAndPlayerCounters(int player_nr)
11604 {
11605   int i;
11606
11607   // advance frame counters (global frame counter and time frame counter)
11608   FrameCounter++;
11609   TimeFrames++;
11610
11611   // advance player counters (counters for move delay, move animation etc.)
11612   for (i = 0; i < MAX_PLAYERS; i++)
11613   {
11614     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11615     int move_delay_value = stored_player[i].move_delay_value;
11616     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11617
11618     if (!advance_player_counters)       // not all players may be affected
11619       continue;
11620
11621     if (move_frames == 0)       // less than one move per game frame
11622     {
11623       int stepsize = TILEX / move_delay_value;
11624       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11625       int count = (stored_player[i].is_moving ?
11626                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11627
11628       if (count % delay == 0)
11629         move_frames = 1;
11630     }
11631
11632     stored_player[i].Frame += move_frames;
11633
11634     if (stored_player[i].MovPos != 0)
11635       stored_player[i].StepFrame += move_frames;
11636
11637     if (stored_player[i].move_delay > 0)
11638       stored_player[i].move_delay--;
11639
11640     // due to bugs in previous versions, counter must count up, not down
11641     if (stored_player[i].push_delay != -1)
11642       stored_player[i].push_delay++;
11643
11644     if (stored_player[i].drop_delay > 0)
11645       stored_player[i].drop_delay--;
11646
11647     if (stored_player[i].is_dropping_pressed)
11648       stored_player[i].drop_pressed_delay++;
11649   }
11650 }
11651
11652 void StartGameActions(boolean init_network_game, boolean record_tape,
11653                       int random_seed)
11654 {
11655   unsigned int new_random_seed = InitRND(random_seed);
11656
11657   if (record_tape)
11658     TapeStartRecording(new_random_seed);
11659
11660   if (init_network_game)
11661   {
11662     SendToServer_LevelFile();
11663     SendToServer_StartPlaying();
11664
11665     return;
11666   }
11667
11668   InitGame();
11669 }
11670
11671 static void GameActionsExt(void)
11672 {
11673 #if 0
11674   static unsigned int game_frame_delay = 0;
11675 #endif
11676   unsigned int game_frame_delay_value;
11677   byte *recorded_player_action;
11678   byte summarized_player_action = 0;
11679   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11680   int i;
11681
11682   // detect endless loops, caused by custom element programming
11683   if (recursion_loop_detected && recursion_loop_depth == 0)
11684   {
11685     char *message = getStringCat3("Internal Error! Element ",
11686                                   EL_NAME(recursion_loop_element),
11687                                   " caused endless loop! Quit the game?");
11688
11689     Warn("element '%s' caused endless loop in game engine",
11690          EL_NAME(recursion_loop_element));
11691
11692     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11693
11694     recursion_loop_detected = FALSE;    // if game should be continued
11695
11696     free(message);
11697
11698     return;
11699   }
11700
11701   if (game.restart_level)
11702     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11703
11704   CheckLevelSolved();
11705
11706   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11707     GameWon();
11708
11709   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11710     TapeStop();
11711
11712   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11713     return;
11714
11715   game_frame_delay_value =
11716     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11717
11718   if (tape.playing && tape.warp_forward && !tape.pausing)
11719     game_frame_delay_value = 0;
11720
11721   SetVideoFrameDelay(game_frame_delay_value);
11722
11723   // (de)activate virtual buttons depending on current game status
11724   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11725   {
11726     if (game.all_players_gone)  // if no players there to be controlled anymore
11727       SetOverlayActive(FALSE);
11728     else if (!tape.playing)     // if game continues after tape stopped playing
11729       SetOverlayActive(TRUE);
11730   }
11731
11732 #if 0
11733 #if 0
11734   // ---------- main game synchronization point ----------
11735
11736   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11737
11738   Debug("game:playing:skip", "skip == %d", skip);
11739
11740 #else
11741   // ---------- main game synchronization point ----------
11742
11743   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11744 #endif
11745 #endif
11746
11747   if (network_playing && !network_player_action_received)
11748   {
11749     // try to get network player actions in time
11750
11751     // last chance to get network player actions without main loop delay
11752     HandleNetworking();
11753
11754     // game was quit by network peer
11755     if (game_status != GAME_MODE_PLAYING)
11756       return;
11757
11758     // check if network player actions still missing and game still running
11759     if (!network_player_action_received && !checkGameEnded())
11760       return;           // failed to get network player actions in time
11761
11762     // do not yet reset "network_player_action_received" (for tape.pausing)
11763   }
11764
11765   if (tape.pausing)
11766     return;
11767
11768   // at this point we know that we really continue executing the game
11769
11770   network_player_action_received = FALSE;
11771
11772   // when playing tape, read previously recorded player input from tape data
11773   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11774
11775   local_player->effective_mouse_action = local_player->mouse_action;
11776
11777   if (recorded_player_action != NULL)
11778     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11779                                  recorded_player_action);
11780
11781   // TapePlayAction() may return NULL when toggling to "pause before death"
11782   if (tape.pausing)
11783     return;
11784
11785   if (tape.set_centered_player)
11786   {
11787     game.centered_player_nr_next = tape.centered_player_nr_next;
11788     game.set_centered_player = TRUE;
11789   }
11790
11791   for (i = 0; i < MAX_PLAYERS; i++)
11792   {
11793     summarized_player_action |= stored_player[i].action;
11794
11795     if (!network_playing && (game.team_mode || tape.playing))
11796       stored_player[i].effective_action = stored_player[i].action;
11797   }
11798
11799   if (network_playing && !checkGameEnded())
11800     SendToServer_MovePlayer(summarized_player_action);
11801
11802   // summarize all actions at local players mapped input device position
11803   // (this allows using different input devices in single player mode)
11804   if (!network.enabled && !game.team_mode)
11805     stored_player[map_player_action[local_player->index_nr]].effective_action =
11806       summarized_player_action;
11807
11808   // summarize all actions at centered player in local team mode
11809   if (tape.recording &&
11810       setup.team_mode && !network.enabled &&
11811       setup.input_on_focus &&
11812       game.centered_player_nr != -1)
11813   {
11814     for (i = 0; i < MAX_PLAYERS; i++)
11815       stored_player[map_player_action[i]].effective_action =
11816         (i == game.centered_player_nr ? summarized_player_action : 0);
11817   }
11818
11819   if (recorded_player_action != NULL)
11820     for (i = 0; i < MAX_PLAYERS; i++)
11821       stored_player[i].effective_action = recorded_player_action[i];
11822
11823   for (i = 0; i < MAX_PLAYERS; i++)
11824   {
11825     tape_action[i] = stored_player[i].effective_action;
11826
11827     /* (this may happen in the RND game engine if a player was not present on
11828        the playfield on level start, but appeared later from a custom element */
11829     if (setup.team_mode &&
11830         tape.recording &&
11831         tape_action[i] &&
11832         !tape.player_participates[i])
11833       tape.player_participates[i] = TRUE;
11834   }
11835
11836   SetTapeActionFromMouseAction(tape_action,
11837                                &local_player->effective_mouse_action);
11838
11839   // only record actions from input devices, but not programmed actions
11840   if (tape.recording)
11841     TapeRecordAction(tape_action);
11842
11843   // remember if game was played (especially after tape stopped playing)
11844   if (!tape.playing && summarized_player_action)
11845     game.GamePlayed = TRUE;
11846
11847 #if USE_NEW_PLAYER_ASSIGNMENTS
11848   // !!! also map player actions in single player mode !!!
11849   // if (game.team_mode)
11850   if (1)
11851   {
11852     byte mapped_action[MAX_PLAYERS];
11853
11854 #if DEBUG_PLAYER_ACTIONS
11855     for (i = 0; i < MAX_PLAYERS; i++)
11856       DebugContinued("", "%d, ", stored_player[i].effective_action);
11857 #endif
11858
11859     for (i = 0; i < MAX_PLAYERS; i++)
11860       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11861
11862     for (i = 0; i < MAX_PLAYERS; i++)
11863       stored_player[i].effective_action = mapped_action[i];
11864
11865 #if DEBUG_PLAYER_ACTIONS
11866     DebugContinued("", "=> ");
11867     for (i = 0; i < MAX_PLAYERS; i++)
11868       DebugContinued("", "%d, ", stored_player[i].effective_action);
11869     DebugContinued("game:playing:player", "\n");
11870 #endif
11871   }
11872 #if DEBUG_PLAYER_ACTIONS
11873   else
11874   {
11875     for (i = 0; i < MAX_PLAYERS; i++)
11876       DebugContinued("", "%d, ", stored_player[i].effective_action);
11877     DebugContinued("game:playing:player", "\n");
11878   }
11879 #endif
11880 #endif
11881
11882   for (i = 0; i < MAX_PLAYERS; i++)
11883   {
11884     // allow engine snapshot in case of changed movement attempt
11885     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11886         (stored_player[i].effective_action & KEY_MOTION))
11887       game.snapshot.changed_action = TRUE;
11888
11889     // allow engine snapshot in case of snapping/dropping attempt
11890     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11891         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11892       game.snapshot.changed_action = TRUE;
11893
11894     game.snapshot.last_action[i] = stored_player[i].effective_action;
11895   }
11896
11897   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11898   {
11899     GameActions_EM_Main();
11900   }
11901   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11902   {
11903     GameActions_SP_Main();
11904   }
11905   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11906   {
11907     GameActions_MM_Main();
11908   }
11909   else
11910   {
11911     GameActions_RND_Main();
11912   }
11913
11914   BlitScreenToBitmap(backbuffer);
11915
11916   CheckLevelSolved();
11917   CheckLevelTime();
11918
11919   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11920
11921   if (global.show_frames_per_second)
11922   {
11923     static unsigned int fps_counter = 0;
11924     static int fps_frames = 0;
11925     unsigned int fps_delay_ms = Counter() - fps_counter;
11926
11927     fps_frames++;
11928
11929     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11930     {
11931       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11932
11933       fps_frames = 0;
11934       fps_counter = Counter();
11935
11936       // always draw FPS to screen after FPS value was updated
11937       redraw_mask |= REDRAW_FPS;
11938     }
11939
11940     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11941     if (GetDrawDeactivationMask() == REDRAW_NONE)
11942       redraw_mask |= REDRAW_FPS;
11943   }
11944 }
11945
11946 static void GameActions_CheckSaveEngineSnapshot(void)
11947 {
11948   if (!game.snapshot.save_snapshot)
11949     return;
11950
11951   // clear flag for saving snapshot _before_ saving snapshot
11952   game.snapshot.save_snapshot = FALSE;
11953
11954   SaveEngineSnapshotToList();
11955 }
11956
11957 void GameActions(void)
11958 {
11959   GameActionsExt();
11960
11961   GameActions_CheckSaveEngineSnapshot();
11962 }
11963
11964 void GameActions_EM_Main(void)
11965 {
11966   byte effective_action[MAX_PLAYERS];
11967   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11968   int i;
11969
11970   for (i = 0; i < MAX_PLAYERS; i++)
11971     effective_action[i] = stored_player[i].effective_action;
11972
11973   GameActions_EM(effective_action, warp_mode);
11974 }
11975
11976 void GameActions_SP_Main(void)
11977 {
11978   byte effective_action[MAX_PLAYERS];
11979   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11980   int i;
11981
11982   for (i = 0; i < MAX_PLAYERS; i++)
11983     effective_action[i] = stored_player[i].effective_action;
11984
11985   GameActions_SP(effective_action, warp_mode);
11986
11987   for (i = 0; i < MAX_PLAYERS; i++)
11988   {
11989     if (stored_player[i].force_dropping)
11990       stored_player[i].action |= KEY_BUTTON_DROP;
11991
11992     stored_player[i].force_dropping = FALSE;
11993   }
11994 }
11995
11996 void GameActions_MM_Main(void)
11997 {
11998   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11999
12000   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12001 }
12002
12003 void GameActions_RND_Main(void)
12004 {
12005   GameActions_RND();
12006 }
12007
12008 void GameActions_RND(void)
12009 {
12010   static struct MouseActionInfo mouse_action_last = { 0 };
12011   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12012   int magic_wall_x = 0, magic_wall_y = 0;
12013   int i, x, y, element, graphic, last_gfx_frame;
12014
12015   InitPlayfieldScanModeVars();
12016
12017   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12018   {
12019     SCAN_PLAYFIELD(x, y)
12020     {
12021       ChangeCount[x][y] = 0;
12022       ChangeEvent[x][y] = -1;
12023     }
12024   }
12025
12026   if (game.set_centered_player)
12027   {
12028     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12029
12030     // switching to "all players" only possible if all players fit to screen
12031     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12032     {
12033       game.centered_player_nr_next = game.centered_player_nr;
12034       game.set_centered_player = FALSE;
12035     }
12036
12037     // do not switch focus to non-existing (or non-active) player
12038     if (game.centered_player_nr_next >= 0 &&
12039         !stored_player[game.centered_player_nr_next].active)
12040     {
12041       game.centered_player_nr_next = game.centered_player_nr;
12042       game.set_centered_player = FALSE;
12043     }
12044   }
12045
12046   if (game.set_centered_player &&
12047       ScreenMovPos == 0)        // screen currently aligned at tile position
12048   {
12049     int sx, sy;
12050
12051     if (game.centered_player_nr_next == -1)
12052     {
12053       setScreenCenteredToAllPlayers(&sx, &sy);
12054     }
12055     else
12056     {
12057       sx = stored_player[game.centered_player_nr_next].jx;
12058       sy = stored_player[game.centered_player_nr_next].jy;
12059     }
12060
12061     game.centered_player_nr = game.centered_player_nr_next;
12062     game.set_centered_player = FALSE;
12063
12064     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12065     DrawGameDoorValues();
12066   }
12067
12068   // check single step mode (set flag and clear again if any player is active)
12069   game.enter_single_step_mode =
12070     (tape.single_step && tape.recording && !tape.pausing);
12071
12072   for (i = 0; i < MAX_PLAYERS; i++)
12073   {
12074     int actual_player_action = stored_player[i].effective_action;
12075
12076 #if 1
12077     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12078        - rnd_equinox_tetrachloride 048
12079        - rnd_equinox_tetrachloride_ii 096
12080        - rnd_emanuel_schmieg 002
12081        - doctor_sloan_ww 001, 020
12082     */
12083     if (stored_player[i].MovPos == 0)
12084       CheckGravityMovement(&stored_player[i]);
12085 #endif
12086
12087     // overwrite programmed action with tape action
12088     if (stored_player[i].programmed_action)
12089       actual_player_action = stored_player[i].programmed_action;
12090
12091     PlayerActions(&stored_player[i], actual_player_action);
12092
12093     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12094   }
12095
12096   // single step pause mode may already have been toggled by "ScrollPlayer()"
12097   if (game.enter_single_step_mode && !tape.pausing)
12098     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12099
12100   ScrollScreen(NULL, SCROLL_GO_ON);
12101
12102   /* for backwards compatibility, the following code emulates a fixed bug that
12103      occured when pushing elements (causing elements that just made their last
12104      pushing step to already (if possible) make their first falling step in the
12105      same game frame, which is bad); this code is also needed to use the famous
12106      "spring push bug" which is used in older levels and might be wanted to be
12107      used also in newer levels, but in this case the buggy pushing code is only
12108      affecting the "spring" element and no other elements */
12109
12110   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12111   {
12112     for (i = 0; i < MAX_PLAYERS; i++)
12113     {
12114       struct PlayerInfo *player = &stored_player[i];
12115       int x = player->jx;
12116       int y = player->jy;
12117
12118       if (player->active && player->is_pushing && player->is_moving &&
12119           IS_MOVING(x, y) &&
12120           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12121            Tile[x][y] == EL_SPRING))
12122       {
12123         ContinueMoving(x, y);
12124
12125         // continue moving after pushing (this is actually a bug)
12126         if (!IS_MOVING(x, y))
12127           Stop[x][y] = FALSE;
12128       }
12129     }
12130   }
12131
12132   SCAN_PLAYFIELD(x, y)
12133   {
12134     Last[x][y] = Tile[x][y];
12135
12136     ChangeCount[x][y] = 0;
12137     ChangeEvent[x][y] = -1;
12138
12139     // this must be handled before main playfield loop
12140     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12141     {
12142       MovDelay[x][y]--;
12143       if (MovDelay[x][y] <= 0)
12144         RemoveField(x, y);
12145     }
12146
12147     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12148     {
12149       MovDelay[x][y]--;
12150       if (MovDelay[x][y] <= 0)
12151       {
12152         int element = Store[x][y];
12153         int move_direction = MovDir[x][y];
12154         int player_index_bit = Store2[x][y];
12155
12156         Store[x][y] = 0;
12157         Store2[x][y] = 0;
12158
12159         RemoveField(x, y);
12160         TEST_DrawLevelField(x, y);
12161
12162         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12163       }
12164     }
12165
12166 #if DEBUG
12167     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12168     {
12169       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12170             x, y);
12171       Debug("game:playing:GameActions_RND", "This should never happen!");
12172
12173       ChangePage[x][y] = -1;
12174     }
12175 #endif
12176
12177     Stop[x][y] = FALSE;
12178     if (WasJustMoving[x][y] > 0)
12179       WasJustMoving[x][y]--;
12180     if (WasJustFalling[x][y] > 0)
12181       WasJustFalling[x][y]--;
12182     if (CheckCollision[x][y] > 0)
12183       CheckCollision[x][y]--;
12184     if (CheckImpact[x][y] > 0)
12185       CheckImpact[x][y]--;
12186
12187     GfxFrame[x][y]++;
12188
12189     /* reset finished pushing action (not done in ContinueMoving() to allow
12190        continuous pushing animation for elements with zero push delay) */
12191     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12192     {
12193       ResetGfxAnimation(x, y);
12194       TEST_DrawLevelField(x, y);
12195     }
12196
12197 #if DEBUG
12198     if (IS_BLOCKED(x, y))
12199     {
12200       int oldx, oldy;
12201
12202       Blocked2Moving(x, y, &oldx, &oldy);
12203       if (!IS_MOVING(oldx, oldy))
12204       {
12205         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12206         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12207         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12208         Debug("game:playing:GameActions_RND", "This should never happen!");
12209       }
12210     }
12211 #endif
12212   }
12213
12214   if (mouse_action.button)
12215   {
12216     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12217     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12218
12219     x = mouse_action.lx;
12220     y = mouse_action.ly;
12221     element = Tile[x][y];
12222
12223     if (new_button)
12224     {
12225       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12226       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12227                                          ch_button);
12228     }
12229
12230     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12231     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12232                                        ch_button);
12233   }
12234
12235   SCAN_PLAYFIELD(x, y)
12236   {
12237     element = Tile[x][y];
12238     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12239     last_gfx_frame = GfxFrame[x][y];
12240
12241     ResetGfxFrame(x, y);
12242
12243     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12244       DrawLevelGraphicAnimation(x, y, graphic);
12245
12246     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12247         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12248       ResetRandomAnimationValue(x, y);
12249
12250     SetRandomAnimationValue(x, y);
12251
12252     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12253
12254     if (IS_INACTIVE(element))
12255     {
12256       if (IS_ANIMATED(graphic))
12257         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12258
12259       continue;
12260     }
12261
12262     // this may take place after moving, so 'element' may have changed
12263     if (IS_CHANGING(x, y) &&
12264         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12265     {
12266       int page = element_info[element].event_page_nr[CE_DELAY];
12267
12268       HandleElementChange(x, y, page);
12269
12270       element = Tile[x][y];
12271       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12272     }
12273
12274     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12275     {
12276       StartMoving(x, y);
12277
12278       element = Tile[x][y];
12279       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12280
12281       if (IS_ANIMATED(graphic) &&
12282           !IS_MOVING(x, y) &&
12283           !Stop[x][y])
12284         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12285
12286       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12287         TEST_DrawTwinkleOnField(x, y);
12288     }
12289     else if (element == EL_ACID)
12290     {
12291       if (!Stop[x][y])
12292         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12293     }
12294     else if ((element == EL_EXIT_OPEN ||
12295               element == EL_EM_EXIT_OPEN ||
12296               element == EL_SP_EXIT_OPEN ||
12297               element == EL_STEEL_EXIT_OPEN ||
12298               element == EL_EM_STEEL_EXIT_OPEN ||
12299               element == EL_SP_TERMINAL ||
12300               element == EL_SP_TERMINAL_ACTIVE ||
12301               element == EL_EXTRA_TIME ||
12302               element == EL_SHIELD_NORMAL ||
12303               element == EL_SHIELD_DEADLY) &&
12304              IS_ANIMATED(graphic))
12305       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12306     else if (IS_MOVING(x, y))
12307       ContinueMoving(x, y);
12308     else if (IS_ACTIVE_BOMB(element))
12309       CheckDynamite(x, y);
12310     else if (element == EL_AMOEBA_GROWING)
12311       AmoebaGrowing(x, y);
12312     else if (element == EL_AMOEBA_SHRINKING)
12313       AmoebaShrinking(x, y);
12314
12315 #if !USE_NEW_AMOEBA_CODE
12316     else if (IS_AMOEBALIVE(element))
12317       AmoebaReproduce(x, y);
12318 #endif
12319
12320     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12321       Life(x, y);
12322     else if (element == EL_EXIT_CLOSED)
12323       CheckExit(x, y);
12324     else if (element == EL_EM_EXIT_CLOSED)
12325       CheckExitEM(x, y);
12326     else if (element == EL_STEEL_EXIT_CLOSED)
12327       CheckExitSteel(x, y);
12328     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12329       CheckExitSteelEM(x, y);
12330     else if (element == EL_SP_EXIT_CLOSED)
12331       CheckExitSP(x, y);
12332     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12333              element == EL_EXPANDABLE_STEELWALL_GROWING)
12334       MauerWaechst(x, y);
12335     else if (element == EL_EXPANDABLE_WALL ||
12336              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12337              element == EL_EXPANDABLE_WALL_VERTICAL ||
12338              element == EL_EXPANDABLE_WALL_ANY ||
12339              element == EL_BD_EXPANDABLE_WALL)
12340       MauerAbleger(x, y);
12341     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12342              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12343              element == EL_EXPANDABLE_STEELWALL_ANY)
12344       MauerAblegerStahl(x, y);
12345     else if (element == EL_FLAMES)
12346       CheckForDragon(x, y);
12347     else if (element == EL_EXPLOSION)
12348       ; // drawing of correct explosion animation is handled separately
12349     else if (element == EL_ELEMENT_SNAPPING ||
12350              element == EL_DIAGONAL_SHRINKING ||
12351              element == EL_DIAGONAL_GROWING)
12352     {
12353       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12354
12355       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12356     }
12357     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12358       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12359
12360     if (IS_BELT_ACTIVE(element))
12361       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12362
12363     if (game.magic_wall_active)
12364     {
12365       int jx = local_player->jx, jy = local_player->jy;
12366
12367       // play the element sound at the position nearest to the player
12368       if ((element == EL_MAGIC_WALL_FULL ||
12369            element == EL_MAGIC_WALL_ACTIVE ||
12370            element == EL_MAGIC_WALL_EMPTYING ||
12371            element == EL_BD_MAGIC_WALL_FULL ||
12372            element == EL_BD_MAGIC_WALL_ACTIVE ||
12373            element == EL_BD_MAGIC_WALL_EMPTYING ||
12374            element == EL_DC_MAGIC_WALL_FULL ||
12375            element == EL_DC_MAGIC_WALL_ACTIVE ||
12376            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12377           ABS(x - jx) + ABS(y - jy) <
12378           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12379       {
12380         magic_wall_x = x;
12381         magic_wall_y = y;
12382       }
12383     }
12384   }
12385
12386 #if USE_NEW_AMOEBA_CODE
12387   // new experimental amoeba growth stuff
12388   if (!(FrameCounter % 8))
12389   {
12390     static unsigned int random = 1684108901;
12391
12392     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12393     {
12394       x = RND(lev_fieldx);
12395       y = RND(lev_fieldy);
12396       element = Tile[x][y];
12397
12398       if (!IS_PLAYER(x,y) &&
12399           (element == EL_EMPTY ||
12400            CAN_GROW_INTO(element) ||
12401            element == EL_QUICKSAND_EMPTY ||
12402            element == EL_QUICKSAND_FAST_EMPTY ||
12403            element == EL_ACID_SPLASH_LEFT ||
12404            element == EL_ACID_SPLASH_RIGHT))
12405       {
12406         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12407             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12408             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12409             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12410           Tile[x][y] = EL_AMOEBA_DROP;
12411       }
12412
12413       random = random * 129 + 1;
12414     }
12415   }
12416 #endif
12417
12418   game.explosions_delayed = FALSE;
12419
12420   SCAN_PLAYFIELD(x, y)
12421   {
12422     element = Tile[x][y];
12423
12424     if (ExplodeField[x][y])
12425       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12426     else if (element == EL_EXPLOSION)
12427       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12428
12429     ExplodeField[x][y] = EX_TYPE_NONE;
12430   }
12431
12432   game.explosions_delayed = TRUE;
12433
12434   if (game.magic_wall_active)
12435   {
12436     if (!(game.magic_wall_time_left % 4))
12437     {
12438       int element = Tile[magic_wall_x][magic_wall_y];
12439
12440       if (element == EL_BD_MAGIC_WALL_FULL ||
12441           element == EL_BD_MAGIC_WALL_ACTIVE ||
12442           element == EL_BD_MAGIC_WALL_EMPTYING)
12443         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12444       else if (element == EL_DC_MAGIC_WALL_FULL ||
12445                element == EL_DC_MAGIC_WALL_ACTIVE ||
12446                element == EL_DC_MAGIC_WALL_EMPTYING)
12447         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12448       else
12449         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12450     }
12451
12452     if (game.magic_wall_time_left > 0)
12453     {
12454       game.magic_wall_time_left--;
12455
12456       if (!game.magic_wall_time_left)
12457       {
12458         SCAN_PLAYFIELD(x, y)
12459         {
12460           element = Tile[x][y];
12461
12462           if (element == EL_MAGIC_WALL_ACTIVE ||
12463               element == EL_MAGIC_WALL_FULL)
12464           {
12465             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12466             TEST_DrawLevelField(x, y);
12467           }
12468           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12469                    element == EL_BD_MAGIC_WALL_FULL)
12470           {
12471             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12472             TEST_DrawLevelField(x, y);
12473           }
12474           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12475                    element == EL_DC_MAGIC_WALL_FULL)
12476           {
12477             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12478             TEST_DrawLevelField(x, y);
12479           }
12480         }
12481
12482         game.magic_wall_active = FALSE;
12483       }
12484     }
12485   }
12486
12487   if (game.light_time_left > 0)
12488   {
12489     game.light_time_left--;
12490
12491     if (game.light_time_left == 0)
12492       RedrawAllLightSwitchesAndInvisibleElements();
12493   }
12494
12495   if (game.timegate_time_left > 0)
12496   {
12497     game.timegate_time_left--;
12498
12499     if (game.timegate_time_left == 0)
12500       CloseAllOpenTimegates();
12501   }
12502
12503   if (game.lenses_time_left > 0)
12504   {
12505     game.lenses_time_left--;
12506
12507     if (game.lenses_time_left == 0)
12508       RedrawAllInvisibleElementsForLenses();
12509   }
12510
12511   if (game.magnify_time_left > 0)
12512   {
12513     game.magnify_time_left--;
12514
12515     if (game.magnify_time_left == 0)
12516       RedrawAllInvisibleElementsForMagnifier();
12517   }
12518
12519   for (i = 0; i < MAX_PLAYERS; i++)
12520   {
12521     struct PlayerInfo *player = &stored_player[i];
12522
12523     if (SHIELD_ON(player))
12524     {
12525       if (player->shield_deadly_time_left)
12526         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12527       else if (player->shield_normal_time_left)
12528         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12529     }
12530   }
12531
12532 #if USE_DELAYED_GFX_REDRAW
12533   SCAN_PLAYFIELD(x, y)
12534   {
12535     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12536     {
12537       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12538          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12539
12540       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12541         DrawLevelField(x, y);
12542
12543       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12544         DrawLevelFieldCrumbled(x, y);
12545
12546       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12547         DrawLevelFieldCrumbledNeighbours(x, y);
12548
12549       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12550         DrawTwinkleOnField(x, y);
12551     }
12552
12553     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12554   }
12555 #endif
12556
12557   DrawAllPlayers();
12558   PlayAllPlayersSound();
12559
12560   for (i = 0; i < MAX_PLAYERS; i++)
12561   {
12562     struct PlayerInfo *player = &stored_player[i];
12563
12564     if (player->show_envelope != 0 && (!player->active ||
12565                                        player->MovPos == 0))
12566     {
12567       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12568
12569       player->show_envelope = 0;
12570     }
12571   }
12572
12573   // use random number generator in every frame to make it less predictable
12574   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12575     RND(1);
12576
12577   mouse_action_last = mouse_action;
12578 }
12579
12580 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12581 {
12582   int min_x = x, min_y = y, max_x = x, max_y = y;
12583   int scr_fieldx = getScreenFieldSizeX();
12584   int scr_fieldy = getScreenFieldSizeY();
12585   int i;
12586
12587   for (i = 0; i < MAX_PLAYERS; i++)
12588   {
12589     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12590
12591     if (!stored_player[i].active || &stored_player[i] == player)
12592       continue;
12593
12594     min_x = MIN(min_x, jx);
12595     min_y = MIN(min_y, jy);
12596     max_x = MAX(max_x, jx);
12597     max_y = MAX(max_y, jy);
12598   }
12599
12600   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12601 }
12602
12603 static boolean AllPlayersInVisibleScreen(void)
12604 {
12605   int i;
12606
12607   for (i = 0; i < MAX_PLAYERS; i++)
12608   {
12609     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12610
12611     if (!stored_player[i].active)
12612       continue;
12613
12614     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12615       return FALSE;
12616   }
12617
12618   return TRUE;
12619 }
12620
12621 void ScrollLevel(int dx, int dy)
12622 {
12623   int scroll_offset = 2 * TILEX_VAR;
12624   int x, y;
12625
12626   BlitBitmap(drawto_field, drawto_field,
12627              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12628              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12629              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12630              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12631              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12632              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12633
12634   if (dx != 0)
12635   {
12636     x = (dx == 1 ? BX1 : BX2);
12637     for (y = BY1; y <= BY2; y++)
12638       DrawScreenField(x, y);
12639   }
12640
12641   if (dy != 0)
12642   {
12643     y = (dy == 1 ? BY1 : BY2);
12644     for (x = BX1; x <= BX2; x++)
12645       DrawScreenField(x, y);
12646   }
12647
12648   redraw_mask |= REDRAW_FIELD;
12649 }
12650
12651 static boolean canFallDown(struct PlayerInfo *player)
12652 {
12653   int jx = player->jx, jy = player->jy;
12654
12655   return (IN_LEV_FIELD(jx, jy + 1) &&
12656           (IS_FREE(jx, jy + 1) ||
12657            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12658           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12659           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12660 }
12661
12662 static boolean canPassField(int x, int y, int move_dir)
12663 {
12664   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12665   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12666   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12667   int nextx = x + dx;
12668   int nexty = y + dy;
12669   int element = Tile[x][y];
12670
12671   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12672           !CAN_MOVE(element) &&
12673           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12674           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12675           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12676 }
12677
12678 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12679 {
12680   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12681   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12682   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12683   int newx = x + dx;
12684   int newy = y + dy;
12685
12686   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12687           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12688           (IS_DIGGABLE(Tile[newx][newy]) ||
12689            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12690            canPassField(newx, newy, move_dir)));
12691 }
12692
12693 static void CheckGravityMovement(struct PlayerInfo *player)
12694 {
12695   if (player->gravity && !player->programmed_action)
12696   {
12697     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12698     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12699     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12700     int jx = player->jx, jy = player->jy;
12701     boolean player_is_moving_to_valid_field =
12702       (!player_is_snapping &&
12703        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12704         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12705     boolean player_can_fall_down = canFallDown(player);
12706
12707     if (player_can_fall_down &&
12708         !player_is_moving_to_valid_field)
12709       player->programmed_action = MV_DOWN;
12710   }
12711 }
12712
12713 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12714 {
12715   return CheckGravityMovement(player);
12716
12717   if (player->gravity && !player->programmed_action)
12718   {
12719     int jx = player->jx, jy = player->jy;
12720     boolean field_under_player_is_free =
12721       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12722     boolean player_is_standing_on_valid_field =
12723       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12724        (IS_WALKABLE(Tile[jx][jy]) &&
12725         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12726
12727     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12728       player->programmed_action = MV_DOWN;
12729   }
12730 }
12731
12732 /*
12733   MovePlayerOneStep()
12734   -----------------------------------------------------------------------------
12735   dx, dy:               direction (non-diagonal) to try to move the player to
12736   real_dx, real_dy:     direction as read from input device (can be diagonal)
12737 */
12738
12739 boolean MovePlayerOneStep(struct PlayerInfo *player,
12740                           int dx, int dy, int real_dx, int real_dy)
12741 {
12742   int jx = player->jx, jy = player->jy;
12743   int new_jx = jx + dx, new_jy = jy + dy;
12744   int can_move;
12745   boolean player_can_move = !player->cannot_move;
12746
12747   if (!player->active || (!dx && !dy))
12748     return MP_NO_ACTION;
12749
12750   player->MovDir = (dx < 0 ? MV_LEFT :
12751                     dx > 0 ? MV_RIGHT :
12752                     dy < 0 ? MV_UP :
12753                     dy > 0 ? MV_DOWN :  MV_NONE);
12754
12755   if (!IN_LEV_FIELD(new_jx, new_jy))
12756     return MP_NO_ACTION;
12757
12758   if (!player_can_move)
12759   {
12760     if (player->MovPos == 0)
12761     {
12762       player->is_moving = FALSE;
12763       player->is_digging = FALSE;
12764       player->is_collecting = FALSE;
12765       player->is_snapping = FALSE;
12766       player->is_pushing = FALSE;
12767     }
12768   }
12769
12770   if (!network.enabled && game.centered_player_nr == -1 &&
12771       !AllPlayersInSight(player, new_jx, new_jy))
12772     return MP_NO_ACTION;
12773
12774   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12775   if (can_move != MP_MOVING)
12776     return can_move;
12777
12778   // check if DigField() has caused relocation of the player
12779   if (player->jx != jx || player->jy != jy)
12780     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12781
12782   StorePlayer[jx][jy] = 0;
12783   player->last_jx = jx;
12784   player->last_jy = jy;
12785   player->jx = new_jx;
12786   player->jy = new_jy;
12787   StorePlayer[new_jx][new_jy] = player->element_nr;
12788
12789   if (player->move_delay_value_next != -1)
12790   {
12791     player->move_delay_value = player->move_delay_value_next;
12792     player->move_delay_value_next = -1;
12793   }
12794
12795   player->MovPos =
12796     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12797
12798   player->step_counter++;
12799
12800   PlayerVisit[jx][jy] = FrameCounter;
12801
12802   player->is_moving = TRUE;
12803
12804 #if 1
12805   // should better be called in MovePlayer(), but this breaks some tapes
12806   ScrollPlayer(player, SCROLL_INIT);
12807 #endif
12808
12809   return MP_MOVING;
12810 }
12811
12812 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12813 {
12814   int jx = player->jx, jy = player->jy;
12815   int old_jx = jx, old_jy = jy;
12816   int moved = MP_NO_ACTION;
12817
12818   if (!player->active)
12819     return FALSE;
12820
12821   if (!dx && !dy)
12822   {
12823     if (player->MovPos == 0)
12824     {
12825       player->is_moving = FALSE;
12826       player->is_digging = FALSE;
12827       player->is_collecting = FALSE;
12828       player->is_snapping = FALSE;
12829       player->is_pushing = FALSE;
12830     }
12831
12832     return FALSE;
12833   }
12834
12835   if (player->move_delay > 0)
12836     return FALSE;
12837
12838   player->move_delay = -1;              // set to "uninitialized" value
12839
12840   // store if player is automatically moved to next field
12841   player->is_auto_moving = (player->programmed_action != MV_NONE);
12842
12843   // remove the last programmed player action
12844   player->programmed_action = 0;
12845
12846   if (player->MovPos)
12847   {
12848     // should only happen if pre-1.2 tape recordings are played
12849     // this is only for backward compatibility
12850
12851     int original_move_delay_value = player->move_delay_value;
12852
12853 #if DEBUG
12854     Debug("game:playing:MovePlayer",
12855           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12856           tape.counter);
12857 #endif
12858
12859     // scroll remaining steps with finest movement resolution
12860     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12861
12862     while (player->MovPos)
12863     {
12864       ScrollPlayer(player, SCROLL_GO_ON);
12865       ScrollScreen(NULL, SCROLL_GO_ON);
12866
12867       AdvanceFrameAndPlayerCounters(player->index_nr);
12868
12869       DrawAllPlayers();
12870       BackToFront_WithFrameDelay(0);
12871     }
12872
12873     player->move_delay_value = original_move_delay_value;
12874   }
12875
12876   player->is_active = FALSE;
12877
12878   if (player->last_move_dir & MV_HORIZONTAL)
12879   {
12880     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12881       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12882   }
12883   else
12884   {
12885     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12886       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12887   }
12888
12889   if (!moved && !player->is_active)
12890   {
12891     player->is_moving = FALSE;
12892     player->is_digging = FALSE;
12893     player->is_collecting = FALSE;
12894     player->is_snapping = FALSE;
12895     player->is_pushing = FALSE;
12896   }
12897
12898   jx = player->jx;
12899   jy = player->jy;
12900
12901   if (moved & MP_MOVING && !ScreenMovPos &&
12902       (player->index_nr == game.centered_player_nr ||
12903        game.centered_player_nr == -1))
12904   {
12905     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12906
12907     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12908     {
12909       // actual player has left the screen -- scroll in that direction
12910       if (jx != old_jx)         // player has moved horizontally
12911         scroll_x += (jx - old_jx);
12912       else                      // player has moved vertically
12913         scroll_y += (jy - old_jy);
12914     }
12915     else
12916     {
12917       int offset_raw = game.scroll_delay_value;
12918
12919       if (jx != old_jx)         // player has moved horizontally
12920       {
12921         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12922         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12923         int new_scroll_x = jx - MIDPOSX + offset_x;
12924
12925         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12926             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12927           scroll_x = new_scroll_x;
12928
12929         // don't scroll over playfield boundaries
12930         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12931
12932         // don't scroll more than one field at a time
12933         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12934
12935         // don't scroll against the player's moving direction
12936         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12937             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12938           scroll_x = old_scroll_x;
12939       }
12940       else                      // player has moved vertically
12941       {
12942         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12943         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12944         int new_scroll_y = jy - MIDPOSY + offset_y;
12945
12946         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12947             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12948           scroll_y = new_scroll_y;
12949
12950         // don't scroll over playfield boundaries
12951         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12952
12953         // don't scroll more than one field at a time
12954         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12955
12956         // don't scroll against the player's moving direction
12957         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12958             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12959           scroll_y = old_scroll_y;
12960       }
12961     }
12962
12963     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12964     {
12965       if (!network.enabled && game.centered_player_nr == -1 &&
12966           !AllPlayersInVisibleScreen())
12967       {
12968         scroll_x = old_scroll_x;
12969         scroll_y = old_scroll_y;
12970       }
12971       else
12972       {
12973         ScrollScreen(player, SCROLL_INIT);
12974         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12975       }
12976     }
12977   }
12978
12979   player->StepFrame = 0;
12980
12981   if (moved & MP_MOVING)
12982   {
12983     if (old_jx != jx && old_jy == jy)
12984       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12985     else if (old_jx == jx && old_jy != jy)
12986       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12987
12988     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12989
12990     player->last_move_dir = player->MovDir;
12991     player->is_moving = TRUE;
12992     player->is_snapping = FALSE;
12993     player->is_switching = FALSE;
12994     player->is_dropping = FALSE;
12995     player->is_dropping_pressed = FALSE;
12996     player->drop_pressed_delay = 0;
12997
12998 #if 0
12999     // should better be called here than above, but this breaks some tapes
13000     ScrollPlayer(player, SCROLL_INIT);
13001 #endif
13002   }
13003   else
13004   {
13005     CheckGravityMovementWhenNotMoving(player);
13006
13007     player->is_moving = FALSE;
13008
13009     /* at this point, the player is allowed to move, but cannot move right now
13010        (e.g. because of something blocking the way) -- ensure that the player
13011        is also allowed to move in the next frame (in old versions before 3.1.1,
13012        the player was forced to wait again for eight frames before next try) */
13013
13014     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13015       player->move_delay = 0;   // allow direct movement in the next frame
13016   }
13017
13018   if (player->move_delay == -1)         // not yet initialized by DigField()
13019     player->move_delay = player->move_delay_value;
13020
13021   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13022   {
13023     TestIfPlayerTouchesBadThing(jx, jy);
13024     TestIfPlayerTouchesCustomElement(jx, jy);
13025   }
13026
13027   if (!player->active)
13028     RemovePlayer(player);
13029
13030   return moved;
13031 }
13032
13033 void ScrollPlayer(struct PlayerInfo *player, int mode)
13034 {
13035   int jx = player->jx, jy = player->jy;
13036   int last_jx = player->last_jx, last_jy = player->last_jy;
13037   int move_stepsize = TILEX / player->move_delay_value;
13038
13039   if (!player->active)
13040     return;
13041
13042   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13043     return;
13044
13045   if (mode == SCROLL_INIT)
13046   {
13047     player->actual_frame_counter = FrameCounter;
13048     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13049
13050     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13051         Tile[last_jx][last_jy] == EL_EMPTY)
13052     {
13053       int last_field_block_delay = 0;   // start with no blocking at all
13054       int block_delay_adjustment = player->block_delay_adjustment;
13055
13056       // if player blocks last field, add delay for exactly one move
13057       if (player->block_last_field)
13058       {
13059         last_field_block_delay += player->move_delay_value;
13060
13061         // when blocking enabled, prevent moving up despite gravity
13062         if (player->gravity && player->MovDir == MV_UP)
13063           block_delay_adjustment = -1;
13064       }
13065
13066       // add block delay adjustment (also possible when not blocking)
13067       last_field_block_delay += block_delay_adjustment;
13068
13069       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13070       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13071     }
13072
13073     if (player->MovPos != 0)    // player has not yet reached destination
13074       return;
13075   }
13076   else if (!FrameReached(&player->actual_frame_counter, 1))
13077     return;
13078
13079   if (player->MovPos != 0)
13080   {
13081     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13082     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13083
13084     // before DrawPlayer() to draw correct player graphic for this case
13085     if (player->MovPos == 0)
13086       CheckGravityMovement(player);
13087   }
13088
13089   if (player->MovPos == 0)      // player reached destination field
13090   {
13091     if (player->move_delay_reset_counter > 0)
13092     {
13093       player->move_delay_reset_counter--;
13094
13095       if (player->move_delay_reset_counter == 0)
13096       {
13097         // continue with normal speed after quickly moving through gate
13098         HALVE_PLAYER_SPEED(player);
13099
13100         // be able to make the next move without delay
13101         player->move_delay = 0;
13102       }
13103     }
13104
13105     player->last_jx = jx;
13106     player->last_jy = jy;
13107
13108     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13109         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13110         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13111         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13112         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13113         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13114         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13115         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13116     {
13117       ExitPlayer(player);
13118
13119       if (game.players_still_needed == 0 &&
13120           (game.friends_still_needed == 0 ||
13121            IS_SP_ELEMENT(Tile[jx][jy])))
13122         LevelSolved();
13123     }
13124
13125     // this breaks one level: "machine", level 000
13126     {
13127       int move_direction = player->MovDir;
13128       int enter_side = MV_DIR_OPPOSITE(move_direction);
13129       int leave_side = move_direction;
13130       int old_jx = last_jx;
13131       int old_jy = last_jy;
13132       int old_element = Tile[old_jx][old_jy];
13133       int new_element = Tile[jx][jy];
13134
13135       if (IS_CUSTOM_ELEMENT(old_element))
13136         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13137                                    CE_LEFT_BY_PLAYER,
13138                                    player->index_bit, leave_side);
13139
13140       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13141                                           CE_PLAYER_LEAVES_X,
13142                                           player->index_bit, leave_side);
13143
13144       if (IS_CUSTOM_ELEMENT(new_element))
13145         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13146                                    player->index_bit, enter_side);
13147
13148       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13149                                           CE_PLAYER_ENTERS_X,
13150                                           player->index_bit, enter_side);
13151
13152       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13153                                         CE_MOVE_OF_X, move_direction);
13154     }
13155
13156     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13157     {
13158       TestIfPlayerTouchesBadThing(jx, jy);
13159       TestIfPlayerTouchesCustomElement(jx, jy);
13160
13161       /* needed because pushed element has not yet reached its destination,
13162          so it would trigger a change event at its previous field location */
13163       if (!player->is_pushing)
13164         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13165
13166       if (level.finish_dig_collect &&
13167           (player->is_digging || player->is_collecting))
13168       {
13169         int last_element = player->last_removed_element;
13170         int move_direction = player->MovDir;
13171         int enter_side = MV_DIR_OPPOSITE(move_direction);
13172         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13173                             CE_PLAYER_COLLECTS_X);
13174
13175         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13176                                             player->index_bit, enter_side);
13177
13178         player->last_removed_element = EL_UNDEFINED;
13179       }
13180
13181       if (!player->active)
13182         RemovePlayer(player);
13183     }
13184
13185     if (level.use_step_counter)
13186     {
13187       int i;
13188
13189       TimePlayed++;
13190
13191       if (TimeLeft > 0)
13192       {
13193         TimeLeft--;
13194
13195         if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
13196           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13197
13198         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13199
13200         DisplayGameControlValues();
13201
13202         if (!TimeLeft && setup.time_limit && !game.LevelSolved)
13203           for (i = 0; i < MAX_PLAYERS; i++)
13204             KillPlayer(&stored_player[i]);
13205       }
13206       else if (game.no_time_limit && !game.all_players_gone)
13207       {
13208         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13209
13210         DisplayGameControlValues();
13211       }
13212     }
13213
13214     if (tape.single_step && tape.recording && !tape.pausing &&
13215         !player->programmed_action)
13216       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13217
13218     if (!player->programmed_action)
13219       CheckSaveEngineSnapshot(player);
13220   }
13221 }
13222
13223 void ScrollScreen(struct PlayerInfo *player, int mode)
13224 {
13225   static unsigned int screen_frame_counter = 0;
13226
13227   if (mode == SCROLL_INIT)
13228   {
13229     // set scrolling step size according to actual player's moving speed
13230     ScrollStepSize = TILEX / player->move_delay_value;
13231
13232     screen_frame_counter = FrameCounter;
13233     ScreenMovDir = player->MovDir;
13234     ScreenMovPos = player->MovPos;
13235     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13236     return;
13237   }
13238   else if (!FrameReached(&screen_frame_counter, 1))
13239     return;
13240
13241   if (ScreenMovPos)
13242   {
13243     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13244     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13245     redraw_mask |= REDRAW_FIELD;
13246   }
13247   else
13248     ScreenMovDir = MV_NONE;
13249 }
13250
13251 void TestIfPlayerTouchesCustomElement(int x, int y)
13252 {
13253   static int xy[4][2] =
13254   {
13255     { 0, -1 },
13256     { -1, 0 },
13257     { +1, 0 },
13258     { 0, +1 }
13259   };
13260   static int trigger_sides[4][2] =
13261   {
13262     // center side       border side
13263     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13264     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13265     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13266     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13267   };
13268   static int touch_dir[4] =
13269   {
13270     MV_LEFT | MV_RIGHT,
13271     MV_UP   | MV_DOWN,
13272     MV_UP   | MV_DOWN,
13273     MV_LEFT | MV_RIGHT
13274   };
13275   int center_element = Tile[x][y];      // should always be non-moving!
13276   int i;
13277
13278   for (i = 0; i < NUM_DIRECTIONS; i++)
13279   {
13280     int xx = x + xy[i][0];
13281     int yy = y + xy[i][1];
13282     int center_side = trigger_sides[i][0];
13283     int border_side = trigger_sides[i][1];
13284     int border_element;
13285
13286     if (!IN_LEV_FIELD(xx, yy))
13287       continue;
13288
13289     if (IS_PLAYER(x, y))                // player found at center element
13290     {
13291       struct PlayerInfo *player = PLAYERINFO(x, y);
13292
13293       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13294         border_element = Tile[xx][yy];          // may be moving!
13295       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13296         border_element = Tile[xx][yy];
13297       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13298         border_element = MovingOrBlocked2Element(xx, yy);
13299       else
13300         continue;               // center and border element do not touch
13301
13302       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13303                                  player->index_bit, border_side);
13304       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13305                                           CE_PLAYER_TOUCHES_X,
13306                                           player->index_bit, border_side);
13307
13308       {
13309         /* use player element that is initially defined in the level playfield,
13310            not the player element that corresponds to the runtime player number
13311            (example: a level that contains EL_PLAYER_3 as the only player would
13312            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13313         int player_element = PLAYERINFO(x, y)->initial_element;
13314
13315         CheckElementChangeBySide(xx, yy, border_element, player_element,
13316                                  CE_TOUCHING_X, border_side);
13317       }
13318     }
13319     else if (IS_PLAYER(xx, yy))         // player found at border element
13320     {
13321       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13322
13323       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13324       {
13325         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13326           continue;             // center and border element do not touch
13327       }
13328
13329       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13330                                  player->index_bit, center_side);
13331       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13332                                           CE_PLAYER_TOUCHES_X,
13333                                           player->index_bit, center_side);
13334
13335       {
13336         /* use player element that is initially defined in the level playfield,
13337            not the player element that corresponds to the runtime player number
13338            (example: a level that contains EL_PLAYER_3 as the only player would
13339            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13340         int player_element = PLAYERINFO(xx, yy)->initial_element;
13341
13342         CheckElementChangeBySide(x, y, center_element, player_element,
13343                                  CE_TOUCHING_X, center_side);
13344       }
13345
13346       break;
13347     }
13348   }
13349 }
13350
13351 void TestIfElementTouchesCustomElement(int x, int y)
13352 {
13353   static int xy[4][2] =
13354   {
13355     { 0, -1 },
13356     { -1, 0 },
13357     { +1, 0 },
13358     { 0, +1 }
13359   };
13360   static int trigger_sides[4][2] =
13361   {
13362     // center side      border side
13363     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13364     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13365     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13366     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13367   };
13368   static int touch_dir[4] =
13369   {
13370     MV_LEFT | MV_RIGHT,
13371     MV_UP   | MV_DOWN,
13372     MV_UP   | MV_DOWN,
13373     MV_LEFT | MV_RIGHT
13374   };
13375   boolean change_center_element = FALSE;
13376   int center_element = Tile[x][y];      // should always be non-moving!
13377   int border_element_old[NUM_DIRECTIONS];
13378   int i;
13379
13380   for (i = 0; i < NUM_DIRECTIONS; i++)
13381   {
13382     int xx = x + xy[i][0];
13383     int yy = y + xy[i][1];
13384     int border_element;
13385
13386     border_element_old[i] = -1;
13387
13388     if (!IN_LEV_FIELD(xx, yy))
13389       continue;
13390
13391     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13392       border_element = Tile[xx][yy];    // may be moving!
13393     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13394       border_element = Tile[xx][yy];
13395     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13396       border_element = MovingOrBlocked2Element(xx, yy);
13397     else
13398       continue;                 // center and border element do not touch
13399
13400     border_element_old[i] = border_element;
13401   }
13402
13403   for (i = 0; i < NUM_DIRECTIONS; i++)
13404   {
13405     int xx = x + xy[i][0];
13406     int yy = y + xy[i][1];
13407     int center_side = trigger_sides[i][0];
13408     int border_element = border_element_old[i];
13409
13410     if (border_element == -1)
13411       continue;
13412
13413     // check for change of border element
13414     CheckElementChangeBySide(xx, yy, border_element, center_element,
13415                              CE_TOUCHING_X, center_side);
13416
13417     // (center element cannot be player, so we dont have to check this here)
13418   }
13419
13420   for (i = 0; i < NUM_DIRECTIONS; i++)
13421   {
13422     int xx = x + xy[i][0];
13423     int yy = y + xy[i][1];
13424     int border_side = trigger_sides[i][1];
13425     int border_element = border_element_old[i];
13426
13427     if (border_element == -1)
13428       continue;
13429
13430     // check for change of center element (but change it only once)
13431     if (!change_center_element)
13432       change_center_element =
13433         CheckElementChangeBySide(x, y, center_element, border_element,
13434                                  CE_TOUCHING_X, border_side);
13435
13436     if (IS_PLAYER(xx, yy))
13437     {
13438       /* use player element that is initially defined in the level playfield,
13439          not the player element that corresponds to the runtime player number
13440          (example: a level that contains EL_PLAYER_3 as the only player would
13441          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13442       int player_element = PLAYERINFO(xx, yy)->initial_element;
13443
13444       CheckElementChangeBySide(x, y, center_element, player_element,
13445                                CE_TOUCHING_X, border_side);
13446     }
13447   }
13448 }
13449
13450 void TestIfElementHitsCustomElement(int x, int y, int direction)
13451 {
13452   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13453   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13454   int hitx = x + dx, hity = y + dy;
13455   int hitting_element = Tile[x][y];
13456   int touched_element;
13457
13458   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13459     return;
13460
13461   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13462                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13463
13464   if (IN_LEV_FIELD(hitx, hity))
13465   {
13466     int opposite_direction = MV_DIR_OPPOSITE(direction);
13467     int hitting_side = direction;
13468     int touched_side = opposite_direction;
13469     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13470                           MovDir[hitx][hity] != direction ||
13471                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13472
13473     object_hit = TRUE;
13474
13475     if (object_hit)
13476     {
13477       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13478                                CE_HITTING_X, touched_side);
13479
13480       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13481                                CE_HIT_BY_X, hitting_side);
13482
13483       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13484                                CE_HIT_BY_SOMETHING, opposite_direction);
13485
13486       if (IS_PLAYER(hitx, hity))
13487       {
13488         /* use player element that is initially defined in the level playfield,
13489            not the player element that corresponds to the runtime player number
13490            (example: a level that contains EL_PLAYER_3 as the only player would
13491            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13492         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13493
13494         CheckElementChangeBySide(x, y, hitting_element, player_element,
13495                                  CE_HITTING_X, touched_side);
13496       }
13497     }
13498   }
13499
13500   // "hitting something" is also true when hitting the playfield border
13501   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13502                            CE_HITTING_SOMETHING, direction);
13503 }
13504
13505 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13506 {
13507   int i, kill_x = -1, kill_y = -1;
13508
13509   int bad_element = -1;
13510   static int test_xy[4][2] =
13511   {
13512     { 0, -1 },
13513     { -1, 0 },
13514     { +1, 0 },
13515     { 0, +1 }
13516   };
13517   static int test_dir[4] =
13518   {
13519     MV_UP,
13520     MV_LEFT,
13521     MV_RIGHT,
13522     MV_DOWN
13523   };
13524
13525   for (i = 0; i < NUM_DIRECTIONS; i++)
13526   {
13527     int test_x, test_y, test_move_dir, test_element;
13528
13529     test_x = good_x + test_xy[i][0];
13530     test_y = good_y + test_xy[i][1];
13531
13532     if (!IN_LEV_FIELD(test_x, test_y))
13533       continue;
13534
13535     test_move_dir =
13536       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13537
13538     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13539
13540     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13541        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13542     */
13543     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13544         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13545     {
13546       kill_x = test_x;
13547       kill_y = test_y;
13548       bad_element = test_element;
13549
13550       break;
13551     }
13552   }
13553
13554   if (kill_x != -1 || kill_y != -1)
13555   {
13556     if (IS_PLAYER(good_x, good_y))
13557     {
13558       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13559
13560       if (player->shield_deadly_time_left > 0 &&
13561           !IS_INDESTRUCTIBLE(bad_element))
13562         Bang(kill_x, kill_y);
13563       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13564         KillPlayer(player);
13565     }
13566     else
13567       Bang(good_x, good_y);
13568   }
13569 }
13570
13571 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13572 {
13573   int i, kill_x = -1, kill_y = -1;
13574   int bad_element = Tile[bad_x][bad_y];
13575   static int test_xy[4][2] =
13576   {
13577     { 0, -1 },
13578     { -1, 0 },
13579     { +1, 0 },
13580     { 0, +1 }
13581   };
13582   static int touch_dir[4] =
13583   {
13584     MV_LEFT | MV_RIGHT,
13585     MV_UP   | MV_DOWN,
13586     MV_UP   | MV_DOWN,
13587     MV_LEFT | MV_RIGHT
13588   };
13589   static int test_dir[4] =
13590   {
13591     MV_UP,
13592     MV_LEFT,
13593     MV_RIGHT,
13594     MV_DOWN
13595   };
13596
13597   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13598     return;
13599
13600   for (i = 0; i < NUM_DIRECTIONS; i++)
13601   {
13602     int test_x, test_y, test_move_dir, test_element;
13603
13604     test_x = bad_x + test_xy[i][0];
13605     test_y = bad_y + test_xy[i][1];
13606
13607     if (!IN_LEV_FIELD(test_x, test_y))
13608       continue;
13609
13610     test_move_dir =
13611       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13612
13613     test_element = Tile[test_x][test_y];
13614
13615     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13616        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13617     */
13618     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13619         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13620     {
13621       // good thing is player or penguin that does not move away
13622       if (IS_PLAYER(test_x, test_y))
13623       {
13624         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13625
13626         if (bad_element == EL_ROBOT && player->is_moving)
13627           continue;     // robot does not kill player if he is moving
13628
13629         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13630         {
13631           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13632             continue;           // center and border element do not touch
13633         }
13634
13635         kill_x = test_x;
13636         kill_y = test_y;
13637
13638         break;
13639       }
13640       else if (test_element == EL_PENGUIN)
13641       {
13642         kill_x = test_x;
13643         kill_y = test_y;
13644
13645         break;
13646       }
13647     }
13648   }
13649
13650   if (kill_x != -1 || kill_y != -1)
13651   {
13652     if (IS_PLAYER(kill_x, kill_y))
13653     {
13654       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13655
13656       if (player->shield_deadly_time_left > 0 &&
13657           !IS_INDESTRUCTIBLE(bad_element))
13658         Bang(bad_x, bad_y);
13659       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13660         KillPlayer(player);
13661     }
13662     else
13663       Bang(kill_x, kill_y);
13664   }
13665 }
13666
13667 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13668 {
13669   int bad_element = Tile[bad_x][bad_y];
13670   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13671   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13672   int test_x = bad_x + dx, test_y = bad_y + dy;
13673   int test_move_dir, test_element;
13674   int kill_x = -1, kill_y = -1;
13675
13676   if (!IN_LEV_FIELD(test_x, test_y))
13677     return;
13678
13679   test_move_dir =
13680     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13681
13682   test_element = Tile[test_x][test_y];
13683
13684   if (test_move_dir != bad_move_dir)
13685   {
13686     // good thing can be player or penguin that does not move away
13687     if (IS_PLAYER(test_x, test_y))
13688     {
13689       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13690
13691       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13692          player as being hit when he is moving towards the bad thing, because
13693          the "get hit by" condition would be lost after the player stops) */
13694       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13695         return;         // player moves away from bad thing
13696
13697       kill_x = test_x;
13698       kill_y = test_y;
13699     }
13700     else if (test_element == EL_PENGUIN)
13701     {
13702       kill_x = test_x;
13703       kill_y = test_y;
13704     }
13705   }
13706
13707   if (kill_x != -1 || kill_y != -1)
13708   {
13709     if (IS_PLAYER(kill_x, kill_y))
13710     {
13711       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13712
13713       if (player->shield_deadly_time_left > 0 &&
13714           !IS_INDESTRUCTIBLE(bad_element))
13715         Bang(bad_x, bad_y);
13716       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13717         KillPlayer(player);
13718     }
13719     else
13720       Bang(kill_x, kill_y);
13721   }
13722 }
13723
13724 void TestIfPlayerTouchesBadThing(int x, int y)
13725 {
13726   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13727 }
13728
13729 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13730 {
13731   TestIfGoodThingHitsBadThing(x, y, move_dir);
13732 }
13733
13734 void TestIfBadThingTouchesPlayer(int x, int y)
13735 {
13736   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13737 }
13738
13739 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13740 {
13741   TestIfBadThingHitsGoodThing(x, y, move_dir);
13742 }
13743
13744 void TestIfFriendTouchesBadThing(int x, int y)
13745 {
13746   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13747 }
13748
13749 void TestIfBadThingTouchesFriend(int x, int y)
13750 {
13751   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13752 }
13753
13754 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13755 {
13756   int i, kill_x = bad_x, kill_y = bad_y;
13757   static int xy[4][2] =
13758   {
13759     { 0, -1 },
13760     { -1, 0 },
13761     { +1, 0 },
13762     { 0, +1 }
13763   };
13764
13765   for (i = 0; i < NUM_DIRECTIONS; i++)
13766   {
13767     int x, y, element;
13768
13769     x = bad_x + xy[i][0];
13770     y = bad_y + xy[i][1];
13771     if (!IN_LEV_FIELD(x, y))
13772       continue;
13773
13774     element = Tile[x][y];
13775     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13776         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13777     {
13778       kill_x = x;
13779       kill_y = y;
13780       break;
13781     }
13782   }
13783
13784   if (kill_x != bad_x || kill_y != bad_y)
13785     Bang(bad_x, bad_y);
13786 }
13787
13788 void KillPlayer(struct PlayerInfo *player)
13789 {
13790   int jx = player->jx, jy = player->jy;
13791
13792   if (!player->active)
13793     return;
13794
13795 #if 0
13796   Debug("game:playing:KillPlayer",
13797         "0: killed == %d, active == %d, reanimated == %d",
13798         player->killed, player->active, player->reanimated);
13799 #endif
13800
13801   /* the following code was introduced to prevent an infinite loop when calling
13802      -> Bang()
13803      -> CheckTriggeredElementChangeExt()
13804      -> ExecuteCustomElementAction()
13805      -> KillPlayer()
13806      -> (infinitely repeating the above sequence of function calls)
13807      which occurs when killing the player while having a CE with the setting
13808      "kill player X when explosion of <player X>"; the solution using a new
13809      field "player->killed" was chosen for backwards compatibility, although
13810      clever use of the fields "player->active" etc. would probably also work */
13811 #if 1
13812   if (player->killed)
13813     return;
13814 #endif
13815
13816   player->killed = TRUE;
13817
13818   // remove accessible field at the player's position
13819   Tile[jx][jy] = EL_EMPTY;
13820
13821   // deactivate shield (else Bang()/Explode() would not work right)
13822   player->shield_normal_time_left = 0;
13823   player->shield_deadly_time_left = 0;
13824
13825 #if 0
13826   Debug("game:playing:KillPlayer",
13827         "1: killed == %d, active == %d, reanimated == %d",
13828         player->killed, player->active, player->reanimated);
13829 #endif
13830
13831   Bang(jx, jy);
13832
13833 #if 0
13834   Debug("game:playing:KillPlayer",
13835         "2: killed == %d, active == %d, reanimated == %d",
13836         player->killed, player->active, player->reanimated);
13837 #endif
13838
13839   if (player->reanimated)       // killed player may have been reanimated
13840     player->killed = player->reanimated = FALSE;
13841   else
13842     BuryPlayer(player);
13843 }
13844
13845 static void KillPlayerUnlessEnemyProtected(int x, int y)
13846 {
13847   if (!PLAYER_ENEMY_PROTECTED(x, y))
13848     KillPlayer(PLAYERINFO(x, y));
13849 }
13850
13851 static void KillPlayerUnlessExplosionProtected(int x, int y)
13852 {
13853   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13854     KillPlayer(PLAYERINFO(x, y));
13855 }
13856
13857 void BuryPlayer(struct PlayerInfo *player)
13858 {
13859   int jx = player->jx, jy = player->jy;
13860
13861   if (!player->active)
13862     return;
13863
13864   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13865   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13866
13867   RemovePlayer(player);
13868
13869   player->buried = TRUE;
13870
13871   if (game.all_players_gone)
13872     game.GameOver = TRUE;
13873 }
13874
13875 void RemovePlayer(struct PlayerInfo *player)
13876 {
13877   int jx = player->jx, jy = player->jy;
13878   int i, found = FALSE;
13879
13880   player->present = FALSE;
13881   player->active = FALSE;
13882
13883   // required for some CE actions (even if the player is not active anymore)
13884   player->MovPos = 0;
13885
13886   if (!ExplodeField[jx][jy])
13887     StorePlayer[jx][jy] = 0;
13888
13889   if (player->is_moving)
13890     TEST_DrawLevelField(player->last_jx, player->last_jy);
13891
13892   for (i = 0; i < MAX_PLAYERS; i++)
13893     if (stored_player[i].active)
13894       found = TRUE;
13895
13896   if (!found)
13897   {
13898     game.all_players_gone = TRUE;
13899     game.GameOver = TRUE;
13900   }
13901
13902   game.exit_x = game.robot_wheel_x = jx;
13903   game.exit_y = game.robot_wheel_y = jy;
13904 }
13905
13906 void ExitPlayer(struct PlayerInfo *player)
13907 {
13908   DrawPlayer(player);   // needed here only to cleanup last field
13909   RemovePlayer(player);
13910
13911   if (game.players_still_needed > 0)
13912     game.players_still_needed--;
13913 }
13914
13915 static void SetFieldForSnapping(int x, int y, int element, int direction,
13916                                 int player_index_bit)
13917 {
13918   struct ElementInfo *ei = &element_info[element];
13919   int direction_bit = MV_DIR_TO_BIT(direction);
13920   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13921   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13922                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13923
13924   Tile[x][y] = EL_ELEMENT_SNAPPING;
13925   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13926   MovDir[x][y] = direction;
13927   Store[x][y] = element;
13928   Store2[x][y] = player_index_bit;
13929
13930   ResetGfxAnimation(x, y);
13931
13932   GfxElement[x][y] = element;
13933   GfxAction[x][y] = action;
13934   GfxDir[x][y] = direction;
13935   GfxFrame[x][y] = -1;
13936 }
13937
13938 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13939                                    int player_index_bit)
13940 {
13941   TestIfElementTouchesCustomElement(x, y);      // for empty space
13942
13943   if (level.finish_dig_collect)
13944   {
13945     int dig_side = MV_DIR_OPPOSITE(direction);
13946
13947     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13948                                         player_index_bit, dig_side);
13949   }
13950 }
13951
13952 /*
13953   =============================================================================
13954   checkDiagonalPushing()
13955   -----------------------------------------------------------------------------
13956   check if diagonal input device direction results in pushing of object
13957   (by checking if the alternative direction is walkable, diggable, ...)
13958   =============================================================================
13959 */
13960
13961 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13962                                     int x, int y, int real_dx, int real_dy)
13963 {
13964   int jx, jy, dx, dy, xx, yy;
13965
13966   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13967     return TRUE;
13968
13969   // diagonal direction: check alternative direction
13970   jx = player->jx;
13971   jy = player->jy;
13972   dx = x - jx;
13973   dy = y - jy;
13974   xx = jx + (dx == 0 ? real_dx : 0);
13975   yy = jy + (dy == 0 ? real_dy : 0);
13976
13977   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13978 }
13979
13980 /*
13981   =============================================================================
13982   DigField()
13983   -----------------------------------------------------------------------------
13984   x, y:                 field next to player (non-diagonal) to try to dig to
13985   real_dx, real_dy:     direction as read from input device (can be diagonal)
13986   =============================================================================
13987 */
13988
13989 static int DigField(struct PlayerInfo *player,
13990                     int oldx, int oldy, int x, int y,
13991                     int real_dx, int real_dy, int mode)
13992 {
13993   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13994   boolean player_was_pushing = player->is_pushing;
13995   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13996   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13997   int jx = oldx, jy = oldy;
13998   int dx = x - jx, dy = y - jy;
13999   int nextx = x + dx, nexty = y + dy;
14000   int move_direction = (dx == -1 ? MV_LEFT  :
14001                         dx == +1 ? MV_RIGHT :
14002                         dy == -1 ? MV_UP    :
14003                         dy == +1 ? MV_DOWN  : MV_NONE);
14004   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14005   int dig_side = MV_DIR_OPPOSITE(move_direction);
14006   int old_element = Tile[jx][jy];
14007   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14008   int collect_count;
14009
14010   if (is_player)                // function can also be called by EL_PENGUIN
14011   {
14012     if (player->MovPos == 0)
14013     {
14014       player->is_digging = FALSE;
14015       player->is_collecting = FALSE;
14016     }
14017
14018     if (player->MovPos == 0)    // last pushing move finished
14019       player->is_pushing = FALSE;
14020
14021     if (mode == DF_NO_PUSH)     // player just stopped pushing
14022     {
14023       player->is_switching = FALSE;
14024       player->push_delay = -1;
14025
14026       return MP_NO_ACTION;
14027     }
14028   }
14029
14030   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14031     old_element = Back[jx][jy];
14032
14033   // in case of element dropped at player position, check background
14034   else if (Back[jx][jy] != EL_EMPTY &&
14035            game.engine_version >= VERSION_IDENT(2,2,0,0))
14036     old_element = Back[jx][jy];
14037
14038   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14039     return MP_NO_ACTION;        // field has no opening in this direction
14040
14041   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14042     return MP_NO_ACTION;        // field has no opening in this direction
14043
14044   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14045   {
14046     SplashAcid(x, y);
14047
14048     Tile[jx][jy] = player->artwork_element;
14049     InitMovingField(jx, jy, MV_DOWN);
14050     Store[jx][jy] = EL_ACID;
14051     ContinueMoving(jx, jy);
14052     BuryPlayer(player);
14053
14054     return MP_DONT_RUN_INTO;
14055   }
14056
14057   if (player_can_move && DONT_RUN_INTO(element))
14058   {
14059     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14060
14061     return MP_DONT_RUN_INTO;
14062   }
14063
14064   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14065     return MP_NO_ACTION;
14066
14067   collect_count = element_info[element].collect_count_initial;
14068
14069   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14070     return MP_NO_ACTION;
14071
14072   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14073     player_can_move = player_can_move_or_snap;
14074
14075   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14076       game.engine_version >= VERSION_IDENT(2,2,0,0))
14077   {
14078     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14079                                player->index_bit, dig_side);
14080     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14081                                         player->index_bit, dig_side);
14082
14083     if (element == EL_DC_LANDMINE)
14084       Bang(x, y);
14085
14086     if (Tile[x][y] != element)          // field changed by snapping
14087       return MP_ACTION;
14088
14089     return MP_NO_ACTION;
14090   }
14091
14092   if (player->gravity && is_player && !player->is_auto_moving &&
14093       canFallDown(player) && move_direction != MV_DOWN &&
14094       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14095     return MP_NO_ACTION;        // player cannot walk here due to gravity
14096
14097   if (player_can_move &&
14098       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14099   {
14100     int sound_element = SND_ELEMENT(element);
14101     int sound_action = ACTION_WALKING;
14102
14103     if (IS_RND_GATE(element))
14104     {
14105       if (!player->key[RND_GATE_NR(element)])
14106         return MP_NO_ACTION;
14107     }
14108     else if (IS_RND_GATE_GRAY(element))
14109     {
14110       if (!player->key[RND_GATE_GRAY_NR(element)])
14111         return MP_NO_ACTION;
14112     }
14113     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14114     {
14115       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14116         return MP_NO_ACTION;
14117     }
14118     else if (element == EL_EXIT_OPEN ||
14119              element == EL_EM_EXIT_OPEN ||
14120              element == EL_EM_EXIT_OPENING ||
14121              element == EL_STEEL_EXIT_OPEN ||
14122              element == EL_EM_STEEL_EXIT_OPEN ||
14123              element == EL_EM_STEEL_EXIT_OPENING ||
14124              element == EL_SP_EXIT_OPEN ||
14125              element == EL_SP_EXIT_OPENING)
14126     {
14127       sound_action = ACTION_PASSING;    // player is passing exit
14128     }
14129     else if (element == EL_EMPTY)
14130     {
14131       sound_action = ACTION_MOVING;             // nothing to walk on
14132     }
14133
14134     // play sound from background or player, whatever is available
14135     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14136       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14137     else
14138       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14139   }
14140   else if (player_can_move &&
14141            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14142   {
14143     if (!ACCESS_FROM(element, opposite_direction))
14144       return MP_NO_ACTION;      // field not accessible from this direction
14145
14146     if (CAN_MOVE(element))      // only fixed elements can be passed!
14147       return MP_NO_ACTION;
14148
14149     if (IS_EM_GATE(element))
14150     {
14151       if (!player->key[EM_GATE_NR(element)])
14152         return MP_NO_ACTION;
14153     }
14154     else if (IS_EM_GATE_GRAY(element))
14155     {
14156       if (!player->key[EM_GATE_GRAY_NR(element)])
14157         return MP_NO_ACTION;
14158     }
14159     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14160     {
14161       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14162         return MP_NO_ACTION;
14163     }
14164     else if (IS_EMC_GATE(element))
14165     {
14166       if (!player->key[EMC_GATE_NR(element)])
14167         return MP_NO_ACTION;
14168     }
14169     else if (IS_EMC_GATE_GRAY(element))
14170     {
14171       if (!player->key[EMC_GATE_GRAY_NR(element)])
14172         return MP_NO_ACTION;
14173     }
14174     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14175     {
14176       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14177         return MP_NO_ACTION;
14178     }
14179     else if (element == EL_DC_GATE_WHITE ||
14180              element == EL_DC_GATE_WHITE_GRAY ||
14181              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14182     {
14183       if (player->num_white_keys == 0)
14184         return MP_NO_ACTION;
14185
14186       player->num_white_keys--;
14187     }
14188     else if (IS_SP_PORT(element))
14189     {
14190       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14191           element == EL_SP_GRAVITY_PORT_RIGHT ||
14192           element == EL_SP_GRAVITY_PORT_UP ||
14193           element == EL_SP_GRAVITY_PORT_DOWN)
14194         player->gravity = !player->gravity;
14195       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14196                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14197                element == EL_SP_GRAVITY_ON_PORT_UP ||
14198                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14199         player->gravity = TRUE;
14200       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14201                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14202                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14203                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14204         player->gravity = FALSE;
14205     }
14206
14207     // automatically move to the next field with double speed
14208     player->programmed_action = move_direction;
14209
14210     if (player->move_delay_reset_counter == 0)
14211     {
14212       player->move_delay_reset_counter = 2;     // two double speed steps
14213
14214       DOUBLE_PLAYER_SPEED(player);
14215     }
14216
14217     PlayLevelSoundAction(x, y, ACTION_PASSING);
14218   }
14219   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14220   {
14221     RemoveField(x, y);
14222
14223     if (mode != DF_SNAP)
14224     {
14225       GfxElement[x][y] = GFX_ELEMENT(element);
14226       player->is_digging = TRUE;
14227     }
14228
14229     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14230
14231     // use old behaviour for old levels (digging)
14232     if (!level.finish_dig_collect)
14233     {
14234       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14235                                           player->index_bit, dig_side);
14236
14237       // if digging triggered player relocation, finish digging tile
14238       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14239         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14240     }
14241
14242     if (mode == DF_SNAP)
14243     {
14244       if (level.block_snap_field)
14245         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14246       else
14247         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14248
14249       // use old behaviour for old levels (snapping)
14250       if (!level.finish_dig_collect)
14251         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14252                                             player->index_bit, dig_side);
14253     }
14254   }
14255   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14256   {
14257     RemoveField(x, y);
14258
14259     if (is_player && mode != DF_SNAP)
14260     {
14261       GfxElement[x][y] = element;
14262       player->is_collecting = TRUE;
14263     }
14264
14265     if (element == EL_SPEED_PILL)
14266     {
14267       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14268     }
14269     else if (element == EL_EXTRA_TIME && level.time > 0)
14270     {
14271       TimeLeft += level.extra_time;
14272
14273       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14274
14275       DisplayGameControlValues();
14276     }
14277     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14278     {
14279       player->shield_normal_time_left += level.shield_normal_time;
14280       if (element == EL_SHIELD_DEADLY)
14281         player->shield_deadly_time_left += level.shield_deadly_time;
14282     }
14283     else if (element == EL_DYNAMITE ||
14284              element == EL_EM_DYNAMITE ||
14285              element == EL_SP_DISK_RED)
14286     {
14287       if (player->inventory_size < MAX_INVENTORY_SIZE)
14288         player->inventory_element[player->inventory_size++] = element;
14289
14290       DrawGameDoorValues();
14291     }
14292     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14293     {
14294       player->dynabomb_count++;
14295       player->dynabombs_left++;
14296     }
14297     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14298     {
14299       player->dynabomb_size++;
14300     }
14301     else if (element == EL_DYNABOMB_INCREASE_POWER)
14302     {
14303       player->dynabomb_xl = TRUE;
14304     }
14305     else if (IS_KEY(element))
14306     {
14307       player->key[KEY_NR(element)] = TRUE;
14308
14309       DrawGameDoorValues();
14310     }
14311     else if (element == EL_DC_KEY_WHITE)
14312     {
14313       player->num_white_keys++;
14314
14315       // display white keys?
14316       // DrawGameDoorValues();
14317     }
14318     else if (IS_ENVELOPE(element))
14319     {
14320       player->show_envelope = element;
14321     }
14322     else if (element == EL_EMC_LENSES)
14323     {
14324       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14325
14326       RedrawAllInvisibleElementsForLenses();
14327     }
14328     else if (element == EL_EMC_MAGNIFIER)
14329     {
14330       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14331
14332       RedrawAllInvisibleElementsForMagnifier();
14333     }
14334     else if (IS_DROPPABLE(element) ||
14335              IS_THROWABLE(element))     // can be collected and dropped
14336     {
14337       int i;
14338
14339       if (collect_count == 0)
14340         player->inventory_infinite_element = element;
14341       else
14342         for (i = 0; i < collect_count; i++)
14343           if (player->inventory_size < MAX_INVENTORY_SIZE)
14344             player->inventory_element[player->inventory_size++] = element;
14345
14346       DrawGameDoorValues();
14347     }
14348     else if (collect_count > 0)
14349     {
14350       game.gems_still_needed -= collect_count;
14351       if (game.gems_still_needed < 0)
14352         game.gems_still_needed = 0;
14353
14354       game.snapshot.collected_item = TRUE;
14355
14356       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14357
14358       DisplayGameControlValues();
14359     }
14360
14361     RaiseScoreElement(element);
14362     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14363
14364     // use old behaviour for old levels (collecting)
14365     if (!level.finish_dig_collect && is_player)
14366     {
14367       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14368                                           player->index_bit, dig_side);
14369
14370       // if collecting triggered player relocation, finish collecting tile
14371       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14372         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14373     }
14374
14375     if (mode == DF_SNAP)
14376     {
14377       if (level.block_snap_field)
14378         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14379       else
14380         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14381
14382       // use old behaviour for old levels (snapping)
14383       if (!level.finish_dig_collect)
14384         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14385                                             player->index_bit, dig_side);
14386     }
14387   }
14388   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14389   {
14390     if (mode == DF_SNAP && element != EL_BD_ROCK)
14391       return MP_NO_ACTION;
14392
14393     if (CAN_FALL(element) && dy)
14394       return MP_NO_ACTION;
14395
14396     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14397         !(element == EL_SPRING && level.use_spring_bug))
14398       return MP_NO_ACTION;
14399
14400     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14401         ((move_direction & MV_VERTICAL &&
14402           ((element_info[element].move_pattern & MV_LEFT &&
14403             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14404            (element_info[element].move_pattern & MV_RIGHT &&
14405             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14406          (move_direction & MV_HORIZONTAL &&
14407           ((element_info[element].move_pattern & MV_UP &&
14408             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14409            (element_info[element].move_pattern & MV_DOWN &&
14410             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14411       return MP_NO_ACTION;
14412
14413     // do not push elements already moving away faster than player
14414     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14415         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14416       return MP_NO_ACTION;
14417
14418     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14419     {
14420       if (player->push_delay_value == -1 || !player_was_pushing)
14421         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14422     }
14423     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14424     {
14425       if (player->push_delay_value == -1)
14426         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14427     }
14428     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14429     {
14430       if (!player->is_pushing)
14431         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14432     }
14433
14434     player->is_pushing = TRUE;
14435     player->is_active = TRUE;
14436
14437     if (!(IN_LEV_FIELD(nextx, nexty) &&
14438           (IS_FREE(nextx, nexty) ||
14439            (IS_SB_ELEMENT(element) &&
14440             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14441            (IS_CUSTOM_ELEMENT(element) &&
14442             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14443       return MP_NO_ACTION;
14444
14445     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14446       return MP_NO_ACTION;
14447
14448     if (player->push_delay == -1)       // new pushing; restart delay
14449       player->push_delay = 0;
14450
14451     if (player->push_delay < player->push_delay_value &&
14452         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14453         element != EL_SPRING && element != EL_BALLOON)
14454     {
14455       // make sure that there is no move delay before next try to push
14456       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14457         player->move_delay = 0;
14458
14459       return MP_NO_ACTION;
14460     }
14461
14462     if (IS_CUSTOM_ELEMENT(element) &&
14463         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14464     {
14465       if (!DigFieldByCE(nextx, nexty, element))
14466         return MP_NO_ACTION;
14467     }
14468
14469     if (IS_SB_ELEMENT(element))
14470     {
14471       boolean sokoban_task_solved = FALSE;
14472
14473       if (element == EL_SOKOBAN_FIELD_FULL)
14474       {
14475         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14476
14477         IncrementSokobanFieldsNeeded();
14478         IncrementSokobanObjectsNeeded();
14479       }
14480
14481       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14482       {
14483         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14484
14485         DecrementSokobanFieldsNeeded();
14486         DecrementSokobanObjectsNeeded();
14487
14488         // sokoban object was pushed from empty field to sokoban field
14489         if (Back[x][y] == EL_EMPTY)
14490           sokoban_task_solved = TRUE;
14491       }
14492
14493       Tile[x][y] = EL_SOKOBAN_OBJECT;
14494
14495       if (Back[x][y] == Back[nextx][nexty])
14496         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14497       else if (Back[x][y] != 0)
14498         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14499                                     ACTION_EMPTYING);
14500       else
14501         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14502                                     ACTION_FILLING);
14503
14504       if (sokoban_task_solved &&
14505           game.sokoban_fields_still_needed == 0 &&
14506           game.sokoban_objects_still_needed == 0 &&
14507           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14508       {
14509         game.players_still_needed = 0;
14510
14511         LevelSolved();
14512
14513         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14514       }
14515     }
14516     else
14517       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14518
14519     InitMovingField(x, y, move_direction);
14520     GfxAction[x][y] = ACTION_PUSHING;
14521
14522     if (mode == DF_SNAP)
14523       ContinueMoving(x, y);
14524     else
14525       MovPos[x][y] = (dx != 0 ? dx : dy);
14526
14527     Pushed[x][y] = TRUE;
14528     Pushed[nextx][nexty] = TRUE;
14529
14530     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14531       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14532     else
14533       player->push_delay_value = -1;    // get new value later
14534
14535     // check for element change _after_ element has been pushed
14536     if (game.use_change_when_pushing_bug)
14537     {
14538       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14539                                  player->index_bit, dig_side);
14540       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14541                                           player->index_bit, dig_side);
14542     }
14543   }
14544   else if (IS_SWITCHABLE(element))
14545   {
14546     if (PLAYER_SWITCHING(player, x, y))
14547     {
14548       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14549                                           player->index_bit, dig_side);
14550
14551       return MP_ACTION;
14552     }
14553
14554     player->is_switching = TRUE;
14555     player->switch_x = x;
14556     player->switch_y = y;
14557
14558     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14559
14560     if (element == EL_ROBOT_WHEEL)
14561     {
14562       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14563
14564       game.robot_wheel_x = x;
14565       game.robot_wheel_y = y;
14566       game.robot_wheel_active = TRUE;
14567
14568       TEST_DrawLevelField(x, y);
14569     }
14570     else if (element == EL_SP_TERMINAL)
14571     {
14572       int xx, yy;
14573
14574       SCAN_PLAYFIELD(xx, yy)
14575       {
14576         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14577         {
14578           Bang(xx, yy);
14579         }
14580         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14581         {
14582           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14583
14584           ResetGfxAnimation(xx, yy);
14585           TEST_DrawLevelField(xx, yy);
14586         }
14587       }
14588     }
14589     else if (IS_BELT_SWITCH(element))
14590     {
14591       ToggleBeltSwitch(x, y);
14592     }
14593     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14594              element == EL_SWITCHGATE_SWITCH_DOWN ||
14595              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14596              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14597     {
14598       ToggleSwitchgateSwitch(x, y);
14599     }
14600     else if (element == EL_LIGHT_SWITCH ||
14601              element == EL_LIGHT_SWITCH_ACTIVE)
14602     {
14603       ToggleLightSwitch(x, y);
14604     }
14605     else if (element == EL_TIMEGATE_SWITCH ||
14606              element == EL_DC_TIMEGATE_SWITCH)
14607     {
14608       ActivateTimegateSwitch(x, y);
14609     }
14610     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14611              element == EL_BALLOON_SWITCH_RIGHT ||
14612              element == EL_BALLOON_SWITCH_UP    ||
14613              element == EL_BALLOON_SWITCH_DOWN  ||
14614              element == EL_BALLOON_SWITCH_NONE  ||
14615              element == EL_BALLOON_SWITCH_ANY)
14616     {
14617       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14618                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14619                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14620                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14621                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14622                              move_direction);
14623     }
14624     else if (element == EL_LAMP)
14625     {
14626       Tile[x][y] = EL_LAMP_ACTIVE;
14627       game.lights_still_needed--;
14628
14629       ResetGfxAnimation(x, y);
14630       TEST_DrawLevelField(x, y);
14631     }
14632     else if (element == EL_TIME_ORB_FULL)
14633     {
14634       Tile[x][y] = EL_TIME_ORB_EMPTY;
14635
14636       if (level.time > 0 || level.use_time_orb_bug)
14637       {
14638         TimeLeft += level.time_orb_time;
14639         game.no_time_limit = FALSE;
14640
14641         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14642
14643         DisplayGameControlValues();
14644       }
14645
14646       ResetGfxAnimation(x, y);
14647       TEST_DrawLevelField(x, y);
14648     }
14649     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14650              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14651     {
14652       int xx, yy;
14653
14654       game.ball_active = !game.ball_active;
14655
14656       SCAN_PLAYFIELD(xx, yy)
14657       {
14658         int e = Tile[xx][yy];
14659
14660         if (game.ball_active)
14661         {
14662           if (e == EL_EMC_MAGIC_BALL)
14663             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14664           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14665             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14666         }
14667         else
14668         {
14669           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14670             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14671           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14672             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14673         }
14674       }
14675     }
14676
14677     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14678                                         player->index_bit, dig_side);
14679
14680     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14681                                         player->index_bit, dig_side);
14682
14683     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14684                                         player->index_bit, dig_side);
14685
14686     return MP_ACTION;
14687   }
14688   else
14689   {
14690     if (!PLAYER_SWITCHING(player, x, y))
14691     {
14692       player->is_switching = TRUE;
14693       player->switch_x = x;
14694       player->switch_y = y;
14695
14696       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14697                                  player->index_bit, dig_side);
14698       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14699                                           player->index_bit, dig_side);
14700
14701       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14702                                  player->index_bit, dig_side);
14703       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14704                                           player->index_bit, dig_side);
14705     }
14706
14707     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14708                                player->index_bit, dig_side);
14709     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14710                                         player->index_bit, dig_side);
14711
14712     return MP_NO_ACTION;
14713   }
14714
14715   player->push_delay = -1;
14716
14717   if (is_player)                // function can also be called by EL_PENGUIN
14718   {
14719     if (Tile[x][y] != element)          // really digged/collected something
14720     {
14721       player->is_collecting = !player->is_digging;
14722       player->is_active = TRUE;
14723
14724       player->last_removed_element = element;
14725     }
14726   }
14727
14728   return MP_MOVING;
14729 }
14730
14731 static boolean DigFieldByCE(int x, int y, int digging_element)
14732 {
14733   int element = Tile[x][y];
14734
14735   if (!IS_FREE(x, y))
14736   {
14737     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14738                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14739                   ACTION_BREAKING);
14740
14741     // no element can dig solid indestructible elements
14742     if (IS_INDESTRUCTIBLE(element) &&
14743         !IS_DIGGABLE(element) &&
14744         !IS_COLLECTIBLE(element))
14745       return FALSE;
14746
14747     if (AmoebaNr[x][y] &&
14748         (element == EL_AMOEBA_FULL ||
14749          element == EL_BD_AMOEBA ||
14750          element == EL_AMOEBA_GROWING))
14751     {
14752       AmoebaCnt[AmoebaNr[x][y]]--;
14753       AmoebaCnt2[AmoebaNr[x][y]]--;
14754     }
14755
14756     if (IS_MOVING(x, y))
14757       RemoveMovingField(x, y);
14758     else
14759     {
14760       RemoveField(x, y);
14761       TEST_DrawLevelField(x, y);
14762     }
14763
14764     // if digged element was about to explode, prevent the explosion
14765     ExplodeField[x][y] = EX_TYPE_NONE;
14766
14767     PlayLevelSoundAction(x, y, action);
14768   }
14769
14770   Store[x][y] = EL_EMPTY;
14771
14772   // this makes it possible to leave the removed element again
14773   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14774     Store[x][y] = element;
14775
14776   return TRUE;
14777 }
14778
14779 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14780 {
14781   int jx = player->jx, jy = player->jy;
14782   int x = jx + dx, y = jy + dy;
14783   int snap_direction = (dx == -1 ? MV_LEFT  :
14784                         dx == +1 ? MV_RIGHT :
14785                         dy == -1 ? MV_UP    :
14786                         dy == +1 ? MV_DOWN  : MV_NONE);
14787   boolean can_continue_snapping = (level.continuous_snapping &&
14788                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14789
14790   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14791     return FALSE;
14792
14793   if (!player->active || !IN_LEV_FIELD(x, y))
14794     return FALSE;
14795
14796   if (dx && dy)
14797     return FALSE;
14798
14799   if (!dx && !dy)
14800   {
14801     if (player->MovPos == 0)
14802       player->is_pushing = FALSE;
14803
14804     player->is_snapping = FALSE;
14805
14806     if (player->MovPos == 0)
14807     {
14808       player->is_moving = FALSE;
14809       player->is_digging = FALSE;
14810       player->is_collecting = FALSE;
14811     }
14812
14813     return FALSE;
14814   }
14815
14816   // prevent snapping with already pressed snap key when not allowed
14817   if (player->is_snapping && !can_continue_snapping)
14818     return FALSE;
14819
14820   player->MovDir = snap_direction;
14821
14822   if (player->MovPos == 0)
14823   {
14824     player->is_moving = FALSE;
14825     player->is_digging = FALSE;
14826     player->is_collecting = FALSE;
14827   }
14828
14829   player->is_dropping = FALSE;
14830   player->is_dropping_pressed = FALSE;
14831   player->drop_pressed_delay = 0;
14832
14833   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14834     return FALSE;
14835
14836   player->is_snapping = TRUE;
14837   player->is_active = TRUE;
14838
14839   if (player->MovPos == 0)
14840   {
14841     player->is_moving = FALSE;
14842     player->is_digging = FALSE;
14843     player->is_collecting = FALSE;
14844   }
14845
14846   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14847     TEST_DrawLevelField(player->last_jx, player->last_jy);
14848
14849   TEST_DrawLevelField(x, y);
14850
14851   return TRUE;
14852 }
14853
14854 static boolean DropElement(struct PlayerInfo *player)
14855 {
14856   int old_element, new_element;
14857   int dropx = player->jx, dropy = player->jy;
14858   int drop_direction = player->MovDir;
14859   int drop_side = drop_direction;
14860   int drop_element = get_next_dropped_element(player);
14861
14862   /* do not drop an element on top of another element; when holding drop key
14863      pressed without moving, dropped element must move away before the next
14864      element can be dropped (this is especially important if the next element
14865      is dynamite, which can be placed on background for historical reasons) */
14866   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14867     return MP_ACTION;
14868
14869   if (IS_THROWABLE(drop_element))
14870   {
14871     dropx += GET_DX_FROM_DIR(drop_direction);
14872     dropy += GET_DY_FROM_DIR(drop_direction);
14873
14874     if (!IN_LEV_FIELD(dropx, dropy))
14875       return FALSE;
14876   }
14877
14878   old_element = Tile[dropx][dropy];     // old element at dropping position
14879   new_element = drop_element;           // default: no change when dropping
14880
14881   // check if player is active, not moving and ready to drop
14882   if (!player->active || player->MovPos || player->drop_delay > 0)
14883     return FALSE;
14884
14885   // check if player has anything that can be dropped
14886   if (new_element == EL_UNDEFINED)
14887     return FALSE;
14888
14889   // only set if player has anything that can be dropped
14890   player->is_dropping_pressed = TRUE;
14891
14892   // check if drop key was pressed long enough for EM style dynamite
14893   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14894     return FALSE;
14895
14896   // check if anything can be dropped at the current position
14897   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14898     return FALSE;
14899
14900   // collected custom elements can only be dropped on empty fields
14901   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14902     return FALSE;
14903
14904   if (old_element != EL_EMPTY)
14905     Back[dropx][dropy] = old_element;   // store old element on this field
14906
14907   ResetGfxAnimation(dropx, dropy);
14908   ResetRandomAnimationValue(dropx, dropy);
14909
14910   if (player->inventory_size > 0 ||
14911       player->inventory_infinite_element != EL_UNDEFINED)
14912   {
14913     if (player->inventory_size > 0)
14914     {
14915       player->inventory_size--;
14916
14917       DrawGameDoorValues();
14918
14919       if (new_element == EL_DYNAMITE)
14920         new_element = EL_DYNAMITE_ACTIVE;
14921       else if (new_element == EL_EM_DYNAMITE)
14922         new_element = EL_EM_DYNAMITE_ACTIVE;
14923       else if (new_element == EL_SP_DISK_RED)
14924         new_element = EL_SP_DISK_RED_ACTIVE;
14925     }
14926
14927     Tile[dropx][dropy] = new_element;
14928
14929     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14930       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14931                           el2img(Tile[dropx][dropy]), 0);
14932
14933     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14934
14935     // needed if previous element just changed to "empty" in the last frame
14936     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14937
14938     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14939                                player->index_bit, drop_side);
14940     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14941                                         CE_PLAYER_DROPS_X,
14942                                         player->index_bit, drop_side);
14943
14944     TestIfElementTouchesCustomElement(dropx, dropy);
14945   }
14946   else          // player is dropping a dyna bomb
14947   {
14948     player->dynabombs_left--;
14949
14950     Tile[dropx][dropy] = new_element;
14951
14952     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14953       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14954                           el2img(Tile[dropx][dropy]), 0);
14955
14956     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14957   }
14958
14959   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14960     InitField_WithBug1(dropx, dropy, FALSE);
14961
14962   new_element = Tile[dropx][dropy];     // element might have changed
14963
14964   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14965       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14966   {
14967     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14968       MovDir[dropx][dropy] = drop_direction;
14969
14970     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14971
14972     // do not cause impact style collision by dropping elements that can fall
14973     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14974   }
14975
14976   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14977   player->is_dropping = TRUE;
14978
14979   player->drop_pressed_delay = 0;
14980   player->is_dropping_pressed = FALSE;
14981
14982   player->drop_x = dropx;
14983   player->drop_y = dropy;
14984
14985   return TRUE;
14986 }
14987
14988 // ----------------------------------------------------------------------------
14989 // game sound playing functions
14990 // ----------------------------------------------------------------------------
14991
14992 static int *loop_sound_frame = NULL;
14993 static int *loop_sound_volume = NULL;
14994
14995 void InitPlayLevelSound(void)
14996 {
14997   int num_sounds = getSoundListSize();
14998
14999   checked_free(loop_sound_frame);
15000   checked_free(loop_sound_volume);
15001
15002   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15003   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15004 }
15005
15006 static void PlayLevelSound(int x, int y, int nr)
15007 {
15008   int sx = SCREENX(x), sy = SCREENY(y);
15009   int volume, stereo_position;
15010   int max_distance = 8;
15011   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15012
15013   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15014       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15015     return;
15016
15017   if (!IN_LEV_FIELD(x, y) ||
15018       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15019       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15020     return;
15021
15022   volume = SOUND_MAX_VOLUME;
15023
15024   if (!IN_SCR_FIELD(sx, sy))
15025   {
15026     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15027     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15028
15029     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15030   }
15031
15032   stereo_position = (SOUND_MAX_LEFT +
15033                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15034                      (SCR_FIELDX + 2 * max_distance));
15035
15036   if (IS_LOOP_SOUND(nr))
15037   {
15038     /* This assures that quieter loop sounds do not overwrite louder ones,
15039        while restarting sound volume comparison with each new game frame. */
15040
15041     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15042       return;
15043
15044     loop_sound_volume[nr] = volume;
15045     loop_sound_frame[nr] = FrameCounter;
15046   }
15047
15048   PlaySoundExt(nr, volume, stereo_position, type);
15049 }
15050
15051 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15052 {
15053   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15054                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15055                  y < LEVELY(BY1) ? LEVELY(BY1) :
15056                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15057                  sound_action);
15058 }
15059
15060 static void PlayLevelSoundAction(int x, int y, int action)
15061 {
15062   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15063 }
15064
15065 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15066 {
15067   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15068
15069   if (sound_effect != SND_UNDEFINED)
15070     PlayLevelSound(x, y, sound_effect);
15071 }
15072
15073 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15074                                               int action)
15075 {
15076   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15077
15078   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15079     PlayLevelSound(x, y, sound_effect);
15080 }
15081
15082 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15083 {
15084   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15085
15086   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15087     PlayLevelSound(x, y, sound_effect);
15088 }
15089
15090 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15091 {
15092   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15093
15094   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15095     StopSound(sound_effect);
15096 }
15097
15098 static int getLevelMusicNr(void)
15099 {
15100   if (levelset.music[level_nr] != MUS_UNDEFINED)
15101     return levelset.music[level_nr];            // from config file
15102   else
15103     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15104 }
15105
15106 static void FadeLevelSounds(void)
15107 {
15108   FadeSounds();
15109 }
15110
15111 static void FadeLevelMusic(void)
15112 {
15113   int music_nr = getLevelMusicNr();
15114   char *curr_music = getCurrentlyPlayingMusicFilename();
15115   char *next_music = getMusicInfoEntryFilename(music_nr);
15116
15117   if (!strEqual(curr_music, next_music))
15118     FadeMusic();
15119 }
15120
15121 void FadeLevelSoundsAndMusic(void)
15122 {
15123   FadeLevelSounds();
15124   FadeLevelMusic();
15125 }
15126
15127 static void PlayLevelMusic(void)
15128 {
15129   int music_nr = getLevelMusicNr();
15130   char *curr_music = getCurrentlyPlayingMusicFilename();
15131   char *next_music = getMusicInfoEntryFilename(music_nr);
15132
15133   if (!strEqual(curr_music, next_music))
15134     PlayMusicLoop(music_nr);
15135 }
15136
15137 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15138 {
15139   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15140   int offset = 0;
15141   int x = xx - offset;
15142   int y = yy - offset;
15143
15144   switch (sample)
15145   {
15146     case SOUND_blank:
15147       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15148       break;
15149
15150     case SOUND_roll:
15151       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15152       break;
15153
15154     case SOUND_stone:
15155       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15156       break;
15157
15158     case SOUND_nut:
15159       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15160       break;
15161
15162     case SOUND_crack:
15163       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15164       break;
15165
15166     case SOUND_bug:
15167       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15168       break;
15169
15170     case SOUND_tank:
15171       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15172       break;
15173
15174     case SOUND_android_clone:
15175       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15176       break;
15177
15178     case SOUND_android_move:
15179       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15180       break;
15181
15182     case SOUND_spring:
15183       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15184       break;
15185
15186     case SOUND_slurp:
15187       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15188       break;
15189
15190     case SOUND_eater:
15191       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15192       break;
15193
15194     case SOUND_eater_eat:
15195       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15196       break;
15197
15198     case SOUND_alien:
15199       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15200       break;
15201
15202     case SOUND_collect:
15203       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15204       break;
15205
15206     case SOUND_diamond:
15207       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15208       break;
15209
15210     case SOUND_squash:
15211       // !!! CHECK THIS !!!
15212 #if 1
15213       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15214 #else
15215       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15216 #endif
15217       break;
15218
15219     case SOUND_wonderfall:
15220       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15221       break;
15222
15223     case SOUND_drip:
15224       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15225       break;
15226
15227     case SOUND_push:
15228       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15229       break;
15230
15231     case SOUND_dirt:
15232       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15233       break;
15234
15235     case SOUND_acid:
15236       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15237       break;
15238
15239     case SOUND_ball:
15240       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15241       break;
15242
15243     case SOUND_slide:
15244       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15245       break;
15246
15247     case SOUND_wonder:
15248       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15249       break;
15250
15251     case SOUND_door:
15252       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15253       break;
15254
15255     case SOUND_exit_open:
15256       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15257       break;
15258
15259     case SOUND_exit_leave:
15260       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15261       break;
15262
15263     case SOUND_dynamite:
15264       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15265       break;
15266
15267     case SOUND_tick:
15268       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15269       break;
15270
15271     case SOUND_press:
15272       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15273       break;
15274
15275     case SOUND_wheel:
15276       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15277       break;
15278
15279     case SOUND_boom:
15280       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15281       break;
15282
15283     case SOUND_die:
15284       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15285       break;
15286
15287     case SOUND_time:
15288       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15289       break;
15290
15291     default:
15292       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15293       break;
15294   }
15295 }
15296
15297 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15298 {
15299   int element = map_element_SP_to_RND(element_sp);
15300   int action = map_action_SP_to_RND(action_sp);
15301   int offset = (setup.sp_show_border_elements ? 0 : 1);
15302   int x = xx - offset;
15303   int y = yy - offset;
15304
15305   PlayLevelSoundElementAction(x, y, element, action);
15306 }
15307
15308 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15309 {
15310   int element = map_element_MM_to_RND(element_mm);
15311   int action = map_action_MM_to_RND(action_mm);
15312   int offset = 0;
15313   int x = xx - offset;
15314   int y = yy - offset;
15315
15316   if (!IS_MM_ELEMENT(element))
15317     element = EL_MM_DEFAULT;
15318
15319   PlayLevelSoundElementAction(x, y, element, action);
15320 }
15321
15322 void PlaySound_MM(int sound_mm)
15323 {
15324   int sound = map_sound_MM_to_RND(sound_mm);
15325
15326   if (sound == SND_UNDEFINED)
15327     return;
15328
15329   PlaySound(sound);
15330 }
15331
15332 void PlaySoundLoop_MM(int sound_mm)
15333 {
15334   int sound = map_sound_MM_to_RND(sound_mm);
15335
15336   if (sound == SND_UNDEFINED)
15337     return;
15338
15339   PlaySoundLoop(sound);
15340 }
15341
15342 void StopSound_MM(int sound_mm)
15343 {
15344   int sound = map_sound_MM_to_RND(sound_mm);
15345
15346   if (sound == SND_UNDEFINED)
15347     return;
15348
15349   StopSound(sound);
15350 }
15351
15352 void RaiseScore(int value)
15353 {
15354   game.score += value;
15355
15356   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15357
15358   DisplayGameControlValues();
15359 }
15360
15361 void RaiseScoreElement(int element)
15362 {
15363   switch (element)
15364   {
15365     case EL_EMERALD:
15366     case EL_BD_DIAMOND:
15367     case EL_EMERALD_YELLOW:
15368     case EL_EMERALD_RED:
15369     case EL_EMERALD_PURPLE:
15370     case EL_SP_INFOTRON:
15371       RaiseScore(level.score[SC_EMERALD]);
15372       break;
15373     case EL_DIAMOND:
15374       RaiseScore(level.score[SC_DIAMOND]);
15375       break;
15376     case EL_CRYSTAL:
15377       RaiseScore(level.score[SC_CRYSTAL]);
15378       break;
15379     case EL_PEARL:
15380       RaiseScore(level.score[SC_PEARL]);
15381       break;
15382     case EL_BUG:
15383     case EL_BD_BUTTERFLY:
15384     case EL_SP_ELECTRON:
15385       RaiseScore(level.score[SC_BUG]);
15386       break;
15387     case EL_SPACESHIP:
15388     case EL_BD_FIREFLY:
15389     case EL_SP_SNIKSNAK:
15390       RaiseScore(level.score[SC_SPACESHIP]);
15391       break;
15392     case EL_YAMYAM:
15393     case EL_DARK_YAMYAM:
15394       RaiseScore(level.score[SC_YAMYAM]);
15395       break;
15396     case EL_ROBOT:
15397       RaiseScore(level.score[SC_ROBOT]);
15398       break;
15399     case EL_PACMAN:
15400       RaiseScore(level.score[SC_PACMAN]);
15401       break;
15402     case EL_NUT:
15403       RaiseScore(level.score[SC_NUT]);
15404       break;
15405     case EL_DYNAMITE:
15406     case EL_EM_DYNAMITE:
15407     case EL_SP_DISK_RED:
15408     case EL_DYNABOMB_INCREASE_NUMBER:
15409     case EL_DYNABOMB_INCREASE_SIZE:
15410     case EL_DYNABOMB_INCREASE_POWER:
15411       RaiseScore(level.score[SC_DYNAMITE]);
15412       break;
15413     case EL_SHIELD_NORMAL:
15414     case EL_SHIELD_DEADLY:
15415       RaiseScore(level.score[SC_SHIELD]);
15416       break;
15417     case EL_EXTRA_TIME:
15418       RaiseScore(level.extra_time_score);
15419       break;
15420     case EL_KEY_1:
15421     case EL_KEY_2:
15422     case EL_KEY_3:
15423     case EL_KEY_4:
15424     case EL_EM_KEY_1:
15425     case EL_EM_KEY_2:
15426     case EL_EM_KEY_3:
15427     case EL_EM_KEY_4:
15428     case EL_EMC_KEY_5:
15429     case EL_EMC_KEY_6:
15430     case EL_EMC_KEY_7:
15431     case EL_EMC_KEY_8:
15432     case EL_DC_KEY_WHITE:
15433       RaiseScore(level.score[SC_KEY]);
15434       break;
15435     default:
15436       RaiseScore(element_info[element].collect_score);
15437       break;
15438   }
15439 }
15440
15441 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15442 {
15443   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15444   {
15445     if (!quick_quit)
15446     {
15447       // prevent short reactivation of overlay buttons while closing door
15448       SetOverlayActive(FALSE);
15449
15450       // door may still be open due to skipped or envelope style request
15451       CloseDoor(DOOR_CLOSE_1);
15452     }
15453
15454     if (network.enabled)
15455       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15456     else
15457     {
15458       if (quick_quit)
15459         FadeSkipNextFadeIn();
15460
15461       SetGameStatus(GAME_MODE_MAIN);
15462
15463       DrawMainMenu();
15464     }
15465   }
15466   else          // continue playing the game
15467   {
15468     if (tape.playing && tape.deactivate_display)
15469       TapeDeactivateDisplayOff(TRUE);
15470
15471     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15472
15473     if (tape.playing && tape.deactivate_display)
15474       TapeDeactivateDisplayOn();
15475   }
15476 }
15477
15478 void RequestQuitGame(boolean escape_key_pressed)
15479 {
15480   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15481   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15482                         level_editor_test_game);
15483   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15484                           quick_quit);
15485
15486   RequestQuitGameExt(skip_request, quick_quit,
15487                      "Do you really want to quit the game?");
15488 }
15489
15490 void RequestRestartGame(char *message)
15491 {
15492   game.restart_game_message = NULL;
15493
15494   boolean has_started_game = hasStartedNetworkGame();
15495   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15496
15497   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15498   {
15499     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15500   }
15501   else
15502   {
15503     // needed in case of envelope request to close game panel
15504     CloseDoor(DOOR_CLOSE_1);
15505
15506     SetGameStatus(GAME_MODE_MAIN);
15507
15508     DrawMainMenu();
15509   }
15510 }
15511
15512 void CheckGameOver(void)
15513 {
15514   static boolean last_game_over = FALSE;
15515   static int game_over_delay = 0;
15516   int game_over_delay_value = 50;
15517   boolean game_over = checkGameFailed();
15518
15519   // do not handle game over if request dialog is already active
15520   if (game.request_active)
15521     return;
15522
15523   // do not ask to play again if game was never actually played
15524   if (!game.GamePlayed)
15525     return;
15526
15527   if (!game_over)
15528   {
15529     last_game_over = FALSE;
15530     game_over_delay = game_over_delay_value;
15531
15532     return;
15533   }
15534
15535   if (game_over_delay > 0)
15536   {
15537     game_over_delay--;
15538
15539     return;
15540   }
15541
15542   if (last_game_over != game_over)
15543     game.restart_game_message = (hasStartedNetworkGame() ?
15544                                  "Game over! Play it again?" :
15545                                  "Game over!");
15546
15547   last_game_over = game_over;
15548 }
15549
15550 boolean checkGameSolved(void)
15551 {
15552   // set for all game engines if level was solved
15553   return game.LevelSolved_GameEnd;
15554 }
15555
15556 boolean checkGameFailed(void)
15557 {
15558   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15559     return (game_em.game_over && !game_em.level_solved);
15560   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15561     return (game_sp.game_over && !game_sp.level_solved);
15562   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15563     return (game_mm.game_over && !game_mm.level_solved);
15564   else                          // GAME_ENGINE_TYPE_RND
15565     return (game.GameOver && !game.LevelSolved);
15566 }
15567
15568 boolean checkGameEnded(void)
15569 {
15570   return (checkGameSolved() || checkGameFailed());
15571 }
15572
15573
15574 // ----------------------------------------------------------------------------
15575 // random generator functions
15576 // ----------------------------------------------------------------------------
15577
15578 unsigned int InitEngineRandom_RND(int seed)
15579 {
15580   game.num_random_calls = 0;
15581
15582   return InitEngineRandom(seed);
15583 }
15584
15585 unsigned int RND(int max)
15586 {
15587   if (max > 0)
15588   {
15589     game.num_random_calls++;
15590
15591     return GetEngineRandom(max);
15592   }
15593
15594   return 0;
15595 }
15596
15597
15598 // ----------------------------------------------------------------------------
15599 // game engine snapshot handling functions
15600 // ----------------------------------------------------------------------------
15601
15602 struct EngineSnapshotInfo
15603 {
15604   // runtime values for custom element collect score
15605   int collect_score[NUM_CUSTOM_ELEMENTS];
15606
15607   // runtime values for group element choice position
15608   int choice_pos[NUM_GROUP_ELEMENTS];
15609
15610   // runtime values for belt position animations
15611   int belt_graphic[4][NUM_BELT_PARTS];
15612   int belt_anim_mode[4][NUM_BELT_PARTS];
15613 };
15614
15615 static struct EngineSnapshotInfo engine_snapshot_rnd;
15616 static char *snapshot_level_identifier = NULL;
15617 static int snapshot_level_nr = -1;
15618
15619 static void SaveEngineSnapshotValues_RND(void)
15620 {
15621   static int belt_base_active_element[4] =
15622   {
15623     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15624     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15625     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15626     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15627   };
15628   int i, j;
15629
15630   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15631   {
15632     int element = EL_CUSTOM_START + i;
15633
15634     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15635   }
15636
15637   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15638   {
15639     int element = EL_GROUP_START + i;
15640
15641     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15642   }
15643
15644   for (i = 0; i < 4; i++)
15645   {
15646     for (j = 0; j < NUM_BELT_PARTS; j++)
15647     {
15648       int element = belt_base_active_element[i] + j;
15649       int graphic = el2img(element);
15650       int anim_mode = graphic_info[graphic].anim_mode;
15651
15652       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15653       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15654     }
15655   }
15656 }
15657
15658 static void LoadEngineSnapshotValues_RND(void)
15659 {
15660   unsigned int num_random_calls = game.num_random_calls;
15661   int i, j;
15662
15663   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15664   {
15665     int element = EL_CUSTOM_START + i;
15666
15667     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15668   }
15669
15670   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15671   {
15672     int element = EL_GROUP_START + i;
15673
15674     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15675   }
15676
15677   for (i = 0; i < 4; i++)
15678   {
15679     for (j = 0; j < NUM_BELT_PARTS; j++)
15680     {
15681       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15682       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15683
15684       graphic_info[graphic].anim_mode = anim_mode;
15685     }
15686   }
15687
15688   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15689   {
15690     InitRND(tape.random_seed);
15691     for (i = 0; i < num_random_calls; i++)
15692       RND(1);
15693   }
15694
15695   if (game.num_random_calls != num_random_calls)
15696   {
15697     Error("number of random calls out of sync");
15698     Error("number of random calls should be %d", num_random_calls);
15699     Error("number of random calls is %d", game.num_random_calls);
15700
15701     Fail("this should not happen -- please debug");
15702   }
15703 }
15704
15705 void FreeEngineSnapshotSingle(void)
15706 {
15707   FreeSnapshotSingle();
15708
15709   setString(&snapshot_level_identifier, NULL);
15710   snapshot_level_nr = -1;
15711 }
15712
15713 void FreeEngineSnapshotList(void)
15714 {
15715   FreeSnapshotList();
15716 }
15717
15718 static ListNode *SaveEngineSnapshotBuffers(void)
15719 {
15720   ListNode *buffers = NULL;
15721
15722   // copy some special values to a structure better suited for the snapshot
15723
15724   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15725     SaveEngineSnapshotValues_RND();
15726   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15727     SaveEngineSnapshotValues_EM();
15728   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15729     SaveEngineSnapshotValues_SP(&buffers);
15730   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15731     SaveEngineSnapshotValues_MM(&buffers);
15732
15733   // save values stored in special snapshot structure
15734
15735   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15736     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15737   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15738     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15739   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15740     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15741   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15742     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15743
15744   // save further RND engine values
15745
15746   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15747   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15748   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15749
15750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15755
15756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15757   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15759
15760   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15761
15762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15763   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15764
15765   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15766   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15767   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15768   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15769   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15770   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15771   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15772   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15773   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15774   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15775   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15776   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15777   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15778   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15779   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15780   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15781   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15782   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15783
15784   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15785   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15786
15787   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15788   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15789   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15790
15791   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15792   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15793
15794   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15795   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15796   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15797   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15798   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15799
15800   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15801   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15802
15803 #if 0
15804   ListNode *node = engine_snapshot_list_rnd;
15805   int num_bytes = 0;
15806
15807   while (node != NULL)
15808   {
15809     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15810
15811     node = node->next;
15812   }
15813
15814   Debug("game:playing:SaveEngineSnapshotBuffers",
15815         "size of engine snapshot: %d bytes", num_bytes);
15816 #endif
15817
15818   return buffers;
15819 }
15820
15821 void SaveEngineSnapshotSingle(void)
15822 {
15823   ListNode *buffers = SaveEngineSnapshotBuffers();
15824
15825   // finally save all snapshot buffers to single snapshot
15826   SaveSnapshotSingle(buffers);
15827
15828   // save level identification information
15829   setString(&snapshot_level_identifier, leveldir_current->identifier);
15830   snapshot_level_nr = level_nr;
15831 }
15832
15833 boolean CheckSaveEngineSnapshotToList(void)
15834 {
15835   boolean save_snapshot =
15836     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15837      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15838       game.snapshot.changed_action) ||
15839      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15840       game.snapshot.collected_item));
15841
15842   game.snapshot.changed_action = FALSE;
15843   game.snapshot.collected_item = FALSE;
15844   game.snapshot.save_snapshot = save_snapshot;
15845
15846   return save_snapshot;
15847 }
15848
15849 void SaveEngineSnapshotToList(void)
15850 {
15851   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15852       tape.quick_resume)
15853     return;
15854
15855   ListNode *buffers = SaveEngineSnapshotBuffers();
15856
15857   // finally save all snapshot buffers to snapshot list
15858   SaveSnapshotToList(buffers);
15859 }
15860
15861 void SaveEngineSnapshotToListInitial(void)
15862 {
15863   FreeEngineSnapshotList();
15864
15865   SaveEngineSnapshotToList();
15866 }
15867
15868 static void LoadEngineSnapshotValues(void)
15869 {
15870   // restore special values from snapshot structure
15871
15872   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15873     LoadEngineSnapshotValues_RND();
15874   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15875     LoadEngineSnapshotValues_EM();
15876   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15877     LoadEngineSnapshotValues_SP();
15878   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15879     LoadEngineSnapshotValues_MM();
15880 }
15881
15882 void LoadEngineSnapshotSingle(void)
15883 {
15884   LoadSnapshotSingle();
15885
15886   LoadEngineSnapshotValues();
15887 }
15888
15889 static void LoadEngineSnapshot_Undo(int steps)
15890 {
15891   LoadSnapshotFromList_Older(steps);
15892
15893   LoadEngineSnapshotValues();
15894 }
15895
15896 static void LoadEngineSnapshot_Redo(int steps)
15897 {
15898   LoadSnapshotFromList_Newer(steps);
15899
15900   LoadEngineSnapshotValues();
15901 }
15902
15903 boolean CheckEngineSnapshotSingle(void)
15904 {
15905   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15906           snapshot_level_nr == level_nr);
15907 }
15908
15909 boolean CheckEngineSnapshotList(void)
15910 {
15911   return CheckSnapshotList();
15912 }
15913
15914
15915 // ---------- new game button stuff -------------------------------------------
15916
15917 static struct
15918 {
15919   int graphic;
15920   struct XY *pos;
15921   int gadget_id;
15922   boolean *setup_value;
15923   boolean allowed_on_tape;
15924   boolean is_touch_button;
15925   char *infotext;
15926 } gamebutton_info[NUM_GAME_BUTTONS] =
15927 {
15928   {
15929     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15930     GAME_CTRL_ID_STOP,                          NULL,
15931     TRUE, FALSE,                                "stop game"
15932   },
15933   {
15934     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15935     GAME_CTRL_ID_PAUSE,                         NULL,
15936     TRUE, FALSE,                                "pause game"
15937   },
15938   {
15939     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15940     GAME_CTRL_ID_PLAY,                          NULL,
15941     TRUE, FALSE,                                "play game"
15942   },
15943   {
15944     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15945     GAME_CTRL_ID_UNDO,                          NULL,
15946     TRUE, FALSE,                                "undo step"
15947   },
15948   {
15949     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15950     GAME_CTRL_ID_REDO,                          NULL,
15951     TRUE, FALSE,                                "redo step"
15952   },
15953   {
15954     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15955     GAME_CTRL_ID_SAVE,                          NULL,
15956     TRUE, FALSE,                                "save game"
15957   },
15958   {
15959     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15960     GAME_CTRL_ID_PAUSE2,                        NULL,
15961     TRUE, FALSE,                                "pause game"
15962   },
15963   {
15964     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15965     GAME_CTRL_ID_LOAD,                          NULL,
15966     TRUE, FALSE,                                "load game"
15967   },
15968   {
15969     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15970     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15971     FALSE, FALSE,                               "stop game"
15972   },
15973   {
15974     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15975     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15976     FALSE, FALSE,                               "pause game"
15977   },
15978   {
15979     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15980     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15981     FALSE, FALSE,                               "play game"
15982   },
15983   {
15984     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15985     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15986     FALSE, TRUE,                                "stop game"
15987   },
15988   {
15989     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15990     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15991     FALSE, TRUE,                                "pause game"
15992   },
15993   {
15994     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15995     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15996     TRUE, FALSE,                                "background music on/off"
15997   },
15998   {
15999     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16000     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16001     TRUE, FALSE,                                "sound loops on/off"
16002   },
16003   {
16004     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16005     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16006     TRUE, FALSE,                                "normal sounds on/off"
16007   },
16008   {
16009     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16010     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16011     FALSE, FALSE,                               "background music on/off"
16012   },
16013   {
16014     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16015     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16016     FALSE, FALSE,                               "sound loops on/off"
16017   },
16018   {
16019     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16020     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16021     FALSE, FALSE,                               "normal sounds on/off"
16022   }
16023 };
16024
16025 void CreateGameButtons(void)
16026 {
16027   int i;
16028
16029   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16030   {
16031     int graphic = gamebutton_info[i].graphic;
16032     struct GraphicInfo *gfx = &graphic_info[graphic];
16033     struct XY *pos = gamebutton_info[i].pos;
16034     struct GadgetInfo *gi;
16035     int button_type;
16036     boolean checked;
16037     unsigned int event_mask;
16038     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16039     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16040     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16041     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16042     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16043     int gd_x   = gfx->src_x;
16044     int gd_y   = gfx->src_y;
16045     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16046     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16047     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16048     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16049     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16050     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16051     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16052     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16053     int id = i;
16054
16055     if (gfx->bitmap == NULL)
16056     {
16057       game_gadget[id] = NULL;
16058
16059       continue;
16060     }
16061
16062     if (id == GAME_CTRL_ID_STOP ||
16063         id == GAME_CTRL_ID_PANEL_STOP ||
16064         id == GAME_CTRL_ID_TOUCH_STOP ||
16065         id == GAME_CTRL_ID_PLAY ||
16066         id == GAME_CTRL_ID_PANEL_PLAY ||
16067         id == GAME_CTRL_ID_SAVE ||
16068         id == GAME_CTRL_ID_LOAD)
16069     {
16070       button_type = GD_TYPE_NORMAL_BUTTON;
16071       checked = FALSE;
16072       event_mask = GD_EVENT_RELEASED;
16073     }
16074     else if (id == GAME_CTRL_ID_UNDO ||
16075              id == GAME_CTRL_ID_REDO)
16076     {
16077       button_type = GD_TYPE_NORMAL_BUTTON;
16078       checked = FALSE;
16079       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16080     }
16081     else
16082     {
16083       button_type = GD_TYPE_CHECK_BUTTON;
16084       checked = (gamebutton_info[i].setup_value != NULL ?
16085                  *gamebutton_info[i].setup_value : FALSE);
16086       event_mask = GD_EVENT_PRESSED;
16087     }
16088
16089     gi = CreateGadget(GDI_CUSTOM_ID, id,
16090                       GDI_IMAGE_ID, graphic,
16091                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16092                       GDI_X, base_x + x,
16093                       GDI_Y, base_y + y,
16094                       GDI_WIDTH, gfx->width,
16095                       GDI_HEIGHT, gfx->height,
16096                       GDI_TYPE, button_type,
16097                       GDI_STATE, GD_BUTTON_UNPRESSED,
16098                       GDI_CHECKED, checked,
16099                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16100                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16101                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16102                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16103                       GDI_DIRECT_DRAW, FALSE,
16104                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16105                       GDI_EVENT_MASK, event_mask,
16106                       GDI_CALLBACK_ACTION, HandleGameButtons,
16107                       GDI_END);
16108
16109     if (gi == NULL)
16110       Fail("cannot create gadget");
16111
16112     game_gadget[id] = gi;
16113   }
16114 }
16115
16116 void FreeGameButtons(void)
16117 {
16118   int i;
16119
16120   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16121     FreeGadget(game_gadget[i]);
16122 }
16123
16124 static void UnmapGameButtonsAtSamePosition(int id)
16125 {
16126   int i;
16127
16128   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16129     if (i != id &&
16130         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16131         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16132       UnmapGadget(game_gadget[i]);
16133 }
16134
16135 static void UnmapGameButtonsAtSamePosition_All(void)
16136 {
16137   if (setup.show_snapshot_buttons)
16138   {
16139     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16140     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16141     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16142   }
16143   else
16144   {
16145     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16146     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16147     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16148
16149     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16150     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16151     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16152   }
16153 }
16154
16155 static void MapGameButtonsAtSamePosition(int id)
16156 {
16157   int i;
16158
16159   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16160     if (i != id &&
16161         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16162         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16163       MapGadget(game_gadget[i]);
16164
16165   UnmapGameButtonsAtSamePosition_All();
16166 }
16167
16168 void MapUndoRedoButtons(void)
16169 {
16170   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16171   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16172
16173   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16174   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16175 }
16176
16177 void UnmapUndoRedoButtons(void)
16178 {
16179   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16180   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16181
16182   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16183   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16184 }
16185
16186 void ModifyPauseButtons(void)
16187 {
16188   static int ids[] =
16189   {
16190     GAME_CTRL_ID_PAUSE,
16191     GAME_CTRL_ID_PAUSE2,
16192     GAME_CTRL_ID_PANEL_PAUSE,
16193     GAME_CTRL_ID_TOUCH_PAUSE,
16194     -1
16195   };
16196   int i;
16197
16198   for (i = 0; ids[i] > -1; i++)
16199     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16200 }
16201
16202 static void MapGameButtonsExt(boolean on_tape)
16203 {
16204   int i;
16205
16206   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16207     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16208         i != GAME_CTRL_ID_UNDO &&
16209         i != GAME_CTRL_ID_REDO)
16210       MapGadget(game_gadget[i]);
16211
16212   UnmapGameButtonsAtSamePosition_All();
16213
16214   RedrawGameButtons();
16215 }
16216
16217 static void UnmapGameButtonsExt(boolean on_tape)
16218 {
16219   int i;
16220
16221   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16222     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16223       UnmapGadget(game_gadget[i]);
16224 }
16225
16226 static void RedrawGameButtonsExt(boolean on_tape)
16227 {
16228   int i;
16229
16230   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16231     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16232       RedrawGadget(game_gadget[i]);
16233 }
16234
16235 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16236 {
16237   if (gi == NULL)
16238     return;
16239
16240   gi->checked = state;
16241 }
16242
16243 static void RedrawSoundButtonGadget(int id)
16244 {
16245   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16246              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16247              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16248              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16249              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16250              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16251              id);
16252
16253   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16254   RedrawGadget(game_gadget[id2]);
16255 }
16256
16257 void MapGameButtons(void)
16258 {
16259   MapGameButtonsExt(FALSE);
16260 }
16261
16262 void UnmapGameButtons(void)
16263 {
16264   UnmapGameButtonsExt(FALSE);
16265 }
16266
16267 void RedrawGameButtons(void)
16268 {
16269   RedrawGameButtonsExt(FALSE);
16270 }
16271
16272 void MapGameButtonsOnTape(void)
16273 {
16274   MapGameButtonsExt(TRUE);
16275 }
16276
16277 void UnmapGameButtonsOnTape(void)
16278 {
16279   UnmapGameButtonsExt(TRUE);
16280 }
16281
16282 void RedrawGameButtonsOnTape(void)
16283 {
16284   RedrawGameButtonsExt(TRUE);
16285 }
16286
16287 static void GameUndoRedoExt(void)
16288 {
16289   ClearPlayerAction();
16290
16291   tape.pausing = TRUE;
16292
16293   RedrawPlayfield();
16294   UpdateAndDisplayGameControlValues();
16295
16296   DrawCompleteVideoDisplay();
16297   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16298   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16299   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16300
16301   BackToFront();
16302 }
16303
16304 static void GameUndo(int steps)
16305 {
16306   if (!CheckEngineSnapshotList())
16307     return;
16308
16309   LoadEngineSnapshot_Undo(steps);
16310
16311   GameUndoRedoExt();
16312 }
16313
16314 static void GameRedo(int steps)
16315 {
16316   if (!CheckEngineSnapshotList())
16317     return;
16318
16319   LoadEngineSnapshot_Redo(steps);
16320
16321   GameUndoRedoExt();
16322 }
16323
16324 static void HandleGameButtonsExt(int id, int button)
16325 {
16326   static boolean game_undo_executed = FALSE;
16327   int steps = BUTTON_STEPSIZE(button);
16328   boolean handle_game_buttons =
16329     (game_status == GAME_MODE_PLAYING ||
16330      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16331
16332   if (!handle_game_buttons)
16333     return;
16334
16335   switch (id)
16336   {
16337     case GAME_CTRL_ID_STOP:
16338     case GAME_CTRL_ID_PANEL_STOP:
16339     case GAME_CTRL_ID_TOUCH_STOP:
16340       if (game_status == GAME_MODE_MAIN)
16341         break;
16342
16343       if (tape.playing)
16344         TapeStop();
16345       else
16346         RequestQuitGame(FALSE);
16347
16348       break;
16349
16350     case GAME_CTRL_ID_PAUSE:
16351     case GAME_CTRL_ID_PAUSE2:
16352     case GAME_CTRL_ID_PANEL_PAUSE:
16353     case GAME_CTRL_ID_TOUCH_PAUSE:
16354       if (network.enabled && game_status == GAME_MODE_PLAYING)
16355       {
16356         if (tape.pausing)
16357           SendToServer_ContinuePlaying();
16358         else
16359           SendToServer_PausePlaying();
16360       }
16361       else
16362         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16363
16364       game_undo_executed = FALSE;
16365
16366       break;
16367
16368     case GAME_CTRL_ID_PLAY:
16369     case GAME_CTRL_ID_PANEL_PLAY:
16370       if (game_status == GAME_MODE_MAIN)
16371       {
16372         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16373       }
16374       else if (tape.pausing)
16375       {
16376         if (network.enabled)
16377           SendToServer_ContinuePlaying();
16378         else
16379           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16380       }
16381       break;
16382
16383     case GAME_CTRL_ID_UNDO:
16384       // Important: When using "save snapshot when collecting an item" mode,
16385       // load last (current) snapshot for first "undo" after pressing "pause"
16386       // (else the last-but-one snapshot would be loaded, because the snapshot
16387       // pointer already points to the last snapshot when pressing "pause",
16388       // which is fine for "every step/move" mode, but not for "every collect")
16389       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16390           !game_undo_executed)
16391         steps--;
16392
16393       game_undo_executed = TRUE;
16394
16395       GameUndo(steps);
16396       break;
16397
16398     case GAME_CTRL_ID_REDO:
16399       GameRedo(steps);
16400       break;
16401
16402     case GAME_CTRL_ID_SAVE:
16403       TapeQuickSave();
16404       break;
16405
16406     case GAME_CTRL_ID_LOAD:
16407       TapeQuickLoad();
16408       break;
16409
16410     case SOUND_CTRL_ID_MUSIC:
16411     case SOUND_CTRL_ID_PANEL_MUSIC:
16412       if (setup.sound_music)
16413       { 
16414         setup.sound_music = FALSE;
16415
16416         FadeMusic();
16417       }
16418       else if (audio.music_available)
16419       { 
16420         setup.sound = setup.sound_music = TRUE;
16421
16422         SetAudioMode(setup.sound);
16423
16424         if (game_status == GAME_MODE_PLAYING)
16425           PlayLevelMusic();
16426       }
16427
16428       RedrawSoundButtonGadget(id);
16429
16430       break;
16431
16432     case SOUND_CTRL_ID_LOOPS:
16433     case SOUND_CTRL_ID_PANEL_LOOPS:
16434       if (setup.sound_loops)
16435         setup.sound_loops = FALSE;
16436       else if (audio.loops_available)
16437       {
16438         setup.sound = setup.sound_loops = TRUE;
16439
16440         SetAudioMode(setup.sound);
16441       }
16442
16443       RedrawSoundButtonGadget(id);
16444
16445       break;
16446
16447     case SOUND_CTRL_ID_SIMPLE:
16448     case SOUND_CTRL_ID_PANEL_SIMPLE:
16449       if (setup.sound_simple)
16450         setup.sound_simple = FALSE;
16451       else if (audio.sound_available)
16452       {
16453         setup.sound = setup.sound_simple = TRUE;
16454
16455         SetAudioMode(setup.sound);
16456       }
16457
16458       RedrawSoundButtonGadget(id);
16459
16460       break;
16461
16462     default:
16463       break;
16464   }
16465 }
16466
16467 static void HandleGameButtons(struct GadgetInfo *gi)
16468 {
16469   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16470 }
16471
16472 void HandleSoundButtonKeys(Key key)
16473 {
16474   if (key == setup.shortcut.sound_simple)
16475     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16476   else if (key == setup.shortcut.sound_loops)
16477     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16478   else if (key == setup.shortcut.sound_music)
16479     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16480 }