cleanup of code for key arrangement on game panel
[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_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Tile[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Tile[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Tile[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
971                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Tile[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebaNeighbourNr(int, int);
1096 void AmoebaToDiamond(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Tile[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Tile[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Tile[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Tile[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       Debug("game:init:player", "- player element %d activated",
1773             player->element_nr);
1774       Debug("game:init:player", "  (local player is %d and currently %s)",
1775             local_player->element_nr,
1776             local_player->active ? "active" : "not active");
1777     }
1778 #endif
1779
1780     Tile[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   // always check if player was just killed and should be reanimated
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Tile[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Tile[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementSokobanFieldsNeeded();
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementSokobanObjectsNeeded();
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1828         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1830         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1834         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878     case EL_SPRING_LEFT:
1879     case EL_SPRING_RIGHT:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Tile[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_active)
1967         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_active)
1972         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Tile[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Tile[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Tile[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Tile[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return game_em.ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return game_sp.red_disk_count;
2132   else
2133     return stored_player[player_nr].inventory_size;
2134 }
2135
2136 static void InitGameControlValues(void)
2137 {
2138   int i;
2139
2140   for (i = 0; game_panel_controls[i].nr != -1; i++)
2141   {
2142     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144     struct TextPosInfo *pos = gpc->pos;
2145     int nr = gpc->nr;
2146     int type = gpc->type;
2147
2148     if (nr != i)
2149     {
2150       Error("'game_panel_controls' structure corrupted at %d", i);
2151
2152       Fail("this should not happen -- please debug");
2153     }
2154
2155     // force update of game controls after initialization
2156     gpc->value = gpc->last_value = -1;
2157     gpc->frame = gpc->last_frame = -1;
2158     gpc->gfx_frame = -1;
2159
2160     // determine panel value width for later calculation of alignment
2161     if (type == TYPE_INTEGER || type == TYPE_STRING)
2162     {
2163       pos->width = pos->size * getFontWidth(pos->font);
2164       pos->height = getFontHeight(pos->font);
2165     }
2166     else if (type == TYPE_ELEMENT)
2167     {
2168       pos->width = pos->size;
2169       pos->height = pos->size;
2170     }
2171
2172     // fill structure for game panel draw order
2173     gpo->nr = gpc->nr;
2174     gpo->sort_priority = pos->sort_priority;
2175   }
2176
2177   // sort game panel controls according to sort_priority and control number
2178   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2179         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2180 }
2181
2182 static void UpdatePlayfieldElementCount(void)
2183 {
2184   boolean use_element_count = FALSE;
2185   int i, j, x, y;
2186
2187   // first check if it is needed at all to calculate playfield element count
2188   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2189     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2190       use_element_count = TRUE;
2191
2192   if (!use_element_count)
2193     return;
2194
2195   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2196     element_info[i].element_count = 0;
2197
2198   SCAN_PLAYFIELD(x, y)
2199   {
2200     element_info[Tile[x][y]].element_count++;
2201   }
2202
2203   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2204     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2205       if (IS_IN_GROUP(j, i))
2206         element_info[EL_GROUP_START + i].element_count +=
2207           element_info[j].element_count;
2208 }
2209
2210 static void UpdateGameControlValues(void)
2211 {
2212   int i, k;
2213   int time = (game.LevelSolved ?
2214               game.LevelSolved_CountingTime :
2215               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2216               game_em.lev->time :
2217               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2218               game_sp.time_played :
2219               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220               game_mm.energy_left :
2221               game.no_time_limit ? TimePlayed : TimeLeft);
2222   int score = (game.LevelSolved ?
2223                game.LevelSolved_CountingScore :
2224                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2225                game_em.lev->score :
2226                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2227                game_sp.score :
2228                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2229                game_mm.score :
2230                game.score);
2231   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2232               game_em.lev->gems_needed :
2233               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2234               game_sp.infotrons_still_needed :
2235               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2236               game_mm.kettles_still_needed :
2237               game.gems_still_needed);
2238   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2239                      game_em.lev->gems_needed > 0 :
2240                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2241                      game_sp.infotrons_still_needed > 0 :
2242                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2243                      game_mm.kettles_still_needed > 0 ||
2244                      game_mm.lights_still_needed > 0 :
2245                      game.gems_still_needed > 0 ||
2246                      game.sokoban_fields_still_needed > 0 ||
2247                      game.sokoban_objects_still_needed > 0 ||
2248                      game.lights_still_needed > 0);
2249   int health = (game.LevelSolved ?
2250                 game.LevelSolved_CountingHealth :
2251                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2252                 MM_HEALTH(game_mm.laser_overload_value) :
2253                 game.health);
2254   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2255
2256   UpdatePlayfieldElementCount();
2257
2258   // update game panel control values
2259
2260   // used instead of "level_nr" (for network games)
2261   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2262   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2263
2264   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2265   for (i = 0; i < MAX_NUM_KEYS; i++)
2266     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2267   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2268   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2269
2270   if (game.centered_player_nr == -1)
2271   {
2272     for (i = 0; i < MAX_PLAYERS; i++)
2273     {
2274       // only one player in Supaplex game engine
2275       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2276         break;
2277
2278       for (k = 0; k < MAX_NUM_KEYS; k++)
2279       {
2280         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2281         {
2282           if (game_em.ply[i]->keys & (1 << k))
2283             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284               get_key_element_from_nr(k);
2285         }
2286         else if (stored_player[i].key[k])
2287           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2288             get_key_element_from_nr(k);
2289       }
2290
2291       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292         getPlayerInventorySize(i);
2293
2294       if (stored_player[i].num_white_keys > 0)
2295         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2296           EL_DC_KEY_WHITE;
2297
2298       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2299         stored_player[i].num_white_keys;
2300     }
2301   }
2302   else
2303   {
2304     int player_nr = game.centered_player_nr;
2305
2306     for (k = 0; k < MAX_NUM_KEYS; k++)
2307     {
2308       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2309       {
2310         if (game_em.ply[player_nr]->keys & (1 << k))
2311           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2312             get_key_element_from_nr(k);
2313       }
2314       else if (stored_player[player_nr].key[k])
2315         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2316           get_key_element_from_nr(k);
2317     }
2318
2319     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2320       getPlayerInventorySize(player_nr);
2321
2322     if (stored_player[player_nr].num_white_keys > 0)
2323       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2324
2325     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2326       stored_player[player_nr].num_white_keys;
2327   }
2328
2329   // re-arrange keys on game panel, if needed or if defined by style settings
2330   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2331   {
2332     int nr = GAME_PANEL_KEY_1 + i;
2333     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2334     struct TextPosInfo *pos = gpc->pos;
2335
2336     // skip check if key is not in the player's inventory
2337     if (gpc->value == EL_EMPTY)
2338       continue;
2339
2340     // check if keys should be arranged on panel from left to right
2341     if (pos->style == STYLE_LEFTMOST_POSITION)
2342     {
2343       // check previous key positions (left from current key)
2344       for (k = 0; k < i; k++)
2345       {
2346         int nr_new = GAME_PANEL_KEY_1 + k;
2347
2348         if (game_panel_controls[nr_new].value == EL_EMPTY)
2349         {
2350           game_panel_controls[nr_new].value = gpc->value;
2351           gpc->value = EL_EMPTY;
2352
2353           break;
2354         }
2355       }
2356     }
2357
2358     // check if "undefined" keys can be placed at some other position
2359     if (pos->x == -1 && pos->y == -1)
2360     {
2361       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2362
2363       // 1st try: display key at the same position as normal or EM keys
2364       if (game_panel_controls[nr_new].value == EL_EMPTY)
2365       {
2366         game_panel_controls[nr_new].value = gpc->value;
2367       }
2368       else
2369       {
2370         // 2nd try: display key at the next free position in the key panel
2371         for (k = 0; k < STD_NUM_KEYS; k++)
2372         {
2373           nr_new = GAME_PANEL_KEY_1 + k;
2374
2375           if (game_panel_controls[nr_new].value == EL_EMPTY)
2376           {
2377             game_panel_controls[nr_new].value = gpc->value;
2378
2379             break;
2380           }
2381         }
2382       }
2383     }
2384   }
2385
2386   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2387   {
2388     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2389       get_inventory_element_from_pos(local_player, i);
2390     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2391       get_inventory_element_from_pos(local_player, -i - 1);
2392   }
2393
2394   game_panel_controls[GAME_PANEL_SCORE].value = score;
2395   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2396
2397   game_panel_controls[GAME_PANEL_TIME].value = time;
2398
2399   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2400   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2401   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2402
2403   if (level.time == 0)
2404     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2405   else
2406     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2407
2408   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2409   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2410
2411   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2412
2413   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2414     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2415      EL_EMPTY);
2416   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2417     local_player->shield_normal_time_left;
2418   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2419     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2420      EL_EMPTY);
2421   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2422     local_player->shield_deadly_time_left;
2423
2424   game_panel_controls[GAME_PANEL_EXIT].value =
2425     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2426
2427   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2428     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2429   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2430     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2431      EL_EMC_MAGIC_BALL_SWITCH);
2432
2433   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2434     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2435   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2436     game.light_time_left;
2437
2438   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2439     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2440   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2441     game.timegate_time_left;
2442
2443   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2444     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2445
2446   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2447     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2448   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2449     game.lenses_time_left;
2450
2451   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2452     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2453   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2454     game.magnify_time_left;
2455
2456   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2457     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2458      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2459      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2460      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2461      EL_BALLOON_SWITCH_NONE);
2462
2463   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2464     local_player->dynabomb_count;
2465   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2466     local_player->dynabomb_size;
2467   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2468     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2469
2470   game_panel_controls[GAME_PANEL_PENGUINS].value =
2471     game.friends_still_needed;
2472
2473   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2474     game.sokoban_objects_still_needed;
2475   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2476     game.sokoban_fields_still_needed;
2477
2478   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2479     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2480
2481   for (i = 0; i < NUM_BELTS; i++)
2482   {
2483     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2484       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2485        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2486     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2487       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2488   }
2489
2490   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2491     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2492   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2493     game.magic_wall_time_left;
2494
2495   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2496     local_player->gravity;
2497
2498   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2499     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2500
2501   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2502     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2503       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2504        game.panel.element[i].id : EL_UNDEFINED);
2505
2506   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2507     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2508       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2509        element_info[game.panel.element_count[i].id].element_count : 0);
2510
2511   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2512     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2513       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2514        element_info[game.panel.ce_score[i].id].collect_score : 0);
2515
2516   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2517     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2518       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2519        element_info[game.panel.ce_score_element[i].id].collect_score :
2520        EL_UNDEFINED);
2521
2522   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2523   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2524   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2525
2526   // update game panel control frames
2527
2528   for (i = 0; game_panel_controls[i].nr != -1; i++)
2529   {
2530     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2531
2532     if (gpc->type == TYPE_ELEMENT)
2533     {
2534       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2535       {
2536         int last_anim_random_frame = gfx.anim_random_frame;
2537         int element = gpc->value;
2538         int graphic = el2panelimg(element);
2539         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2540                                sync_random_frame : INIT_GFX_RANDOM());
2541
2542         if (gpc->value != gpc->last_value)
2543         {
2544           gpc->gfx_frame = 0;
2545           gpc->gfx_random = init_gfx_random;
2546         }
2547         else
2548         {
2549           gpc->gfx_frame++;
2550
2551           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2552               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2553             gpc->gfx_random = init_gfx_random;
2554         }
2555
2556         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2557           gfx.anim_random_frame = gpc->gfx_random;
2558
2559         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2560           gpc->gfx_frame = element_info[element].collect_score;
2561
2562         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2563
2564         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2565           gfx.anim_random_frame = last_anim_random_frame;
2566       }
2567     }
2568     else if (gpc->type == TYPE_GRAPHIC)
2569     {
2570       if (gpc->graphic != IMG_UNDEFINED)
2571       {
2572         int last_anim_random_frame = gfx.anim_random_frame;
2573         int graphic = gpc->graphic;
2574         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2575                                sync_random_frame : INIT_GFX_RANDOM());
2576
2577         if (gpc->value != gpc->last_value)
2578         {
2579           gpc->gfx_frame = 0;
2580           gpc->gfx_random = init_gfx_random;
2581         }
2582         else
2583         {
2584           gpc->gfx_frame++;
2585
2586           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2587               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2588             gpc->gfx_random = init_gfx_random;
2589         }
2590
2591         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2592           gfx.anim_random_frame = gpc->gfx_random;
2593
2594         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2595
2596         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2597           gfx.anim_random_frame = last_anim_random_frame;
2598       }
2599     }
2600   }
2601 }
2602
2603 static void DisplayGameControlValues(void)
2604 {
2605   boolean redraw_panel = FALSE;
2606   int i;
2607
2608   for (i = 0; game_panel_controls[i].nr != -1; i++)
2609   {
2610     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2611
2612     if (PANEL_DEACTIVATED(gpc->pos))
2613       continue;
2614
2615     if (gpc->value == gpc->last_value &&
2616         gpc->frame == gpc->last_frame)
2617       continue;
2618
2619     redraw_panel = TRUE;
2620   }
2621
2622   if (!redraw_panel)
2623     return;
2624
2625   // copy default game door content to main double buffer
2626
2627   // !!! CHECK AGAIN !!!
2628   SetPanelBackground();
2629   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2630   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2631
2632   // redraw game control buttons
2633   RedrawGameButtons();
2634
2635   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2636
2637   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2638   {
2639     int nr = game_panel_order[i].nr;
2640     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2641     struct TextPosInfo *pos = gpc->pos;
2642     int type = gpc->type;
2643     int value = gpc->value;
2644     int frame = gpc->frame;
2645     int size = pos->size;
2646     int font = pos->font;
2647     boolean draw_masked = pos->draw_masked;
2648     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2649
2650     if (PANEL_DEACTIVATED(pos))
2651       continue;
2652
2653     if (pos->class == get_hash_from_key("extra_panel_items") &&
2654         !setup.prefer_extra_panel_items)
2655       continue;
2656
2657     gpc->last_value = value;
2658     gpc->last_frame = frame;
2659
2660     if (type == TYPE_INTEGER)
2661     {
2662       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2663           nr == GAME_PANEL_TIME)
2664       {
2665         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2666
2667         if (use_dynamic_size)           // use dynamic number of digits
2668         {
2669           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2670           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2671           int size2 = size1 + 1;
2672           int font1 = pos->font;
2673           int font2 = pos->font_alt;
2674
2675           size = (value < value_change ? size1 : size2);
2676           font = (value < value_change ? font1 : font2);
2677         }
2678       }
2679
2680       // correct text size if "digits" is zero or less
2681       if (size <= 0)
2682         size = strlen(int2str(value, size));
2683
2684       // dynamically correct text alignment
2685       pos->width = size * getFontWidth(font);
2686
2687       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2688                   int2str(value, size), font, mask_mode);
2689     }
2690     else if (type == TYPE_ELEMENT)
2691     {
2692       int element, graphic;
2693       Bitmap *src_bitmap;
2694       int src_x, src_y;
2695       int width, height;
2696       int dst_x = PANEL_XPOS(pos);
2697       int dst_y = PANEL_YPOS(pos);
2698
2699       if (value != EL_UNDEFINED && value != EL_EMPTY)
2700       {
2701         element = value;
2702         graphic = el2panelimg(value);
2703
2704 #if 0
2705         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2706               element, EL_NAME(element), size);
2707 #endif
2708
2709         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2710           size = TILESIZE;
2711
2712         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2713                               &src_x, &src_y);
2714
2715         width  = graphic_info[graphic].width  * size / TILESIZE;
2716         height = graphic_info[graphic].height * size / TILESIZE;
2717
2718         if (draw_masked)
2719           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2720                            dst_x, dst_y);
2721         else
2722           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2723                      dst_x, dst_y);
2724       }
2725     }
2726     else if (type == TYPE_GRAPHIC)
2727     {
2728       int graphic        = gpc->graphic;
2729       int graphic_active = gpc->graphic_active;
2730       Bitmap *src_bitmap;
2731       int src_x, src_y;
2732       int width, height;
2733       int dst_x = PANEL_XPOS(pos);
2734       int dst_y = PANEL_YPOS(pos);
2735       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2736                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2737
2738       if (graphic != IMG_UNDEFINED && !skip)
2739       {
2740         if (pos->style == STYLE_REVERSE)
2741           value = 100 - value;
2742
2743         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2744
2745         if (pos->direction & MV_HORIZONTAL)
2746         {
2747           width  = graphic_info[graphic_active].width * value / 100;
2748           height = graphic_info[graphic_active].height;
2749
2750           if (pos->direction == MV_LEFT)
2751           {
2752             src_x += graphic_info[graphic_active].width - width;
2753             dst_x += graphic_info[graphic_active].width - width;
2754           }
2755         }
2756         else
2757         {
2758           width  = graphic_info[graphic_active].width;
2759           height = graphic_info[graphic_active].height * value / 100;
2760
2761           if (pos->direction == MV_UP)
2762           {
2763             src_y += graphic_info[graphic_active].height - height;
2764             dst_y += graphic_info[graphic_active].height - height;
2765           }
2766         }
2767
2768         if (draw_masked)
2769           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2770                            dst_x, dst_y);
2771         else
2772           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2773                      dst_x, dst_y);
2774
2775         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2776
2777         if (pos->direction & MV_HORIZONTAL)
2778         {
2779           if (pos->direction == MV_RIGHT)
2780           {
2781             src_x += width;
2782             dst_x += width;
2783           }
2784           else
2785           {
2786             dst_x = PANEL_XPOS(pos);
2787           }
2788
2789           width = graphic_info[graphic].width - width;
2790         }
2791         else
2792         {
2793           if (pos->direction == MV_DOWN)
2794           {
2795             src_y += height;
2796             dst_y += height;
2797           }
2798           else
2799           {
2800             dst_y = PANEL_YPOS(pos);
2801           }
2802
2803           height = graphic_info[graphic].height - height;
2804         }
2805
2806         if (draw_masked)
2807           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2808                            dst_x, dst_y);
2809         else
2810           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2811                      dst_x, dst_y);
2812       }
2813     }
2814     else if (type == TYPE_STRING)
2815     {
2816       boolean active = (value != 0);
2817       char *state_normal = "off";
2818       char *state_active = "on";
2819       char *state = (active ? state_active : state_normal);
2820       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2821                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2822                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2823                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2824
2825       if (nr == GAME_PANEL_GRAVITY_STATE)
2826       {
2827         int font1 = pos->font;          // (used for normal state)
2828         int font2 = pos->font_alt;      // (used for active state)
2829
2830         font = (active ? font2 : font1);
2831       }
2832
2833       if (s != NULL)
2834       {
2835         char *s_cut;
2836
2837         if (size <= 0)
2838         {
2839           // don't truncate output if "chars" is zero or less
2840           size = strlen(s);
2841
2842           // dynamically correct text alignment
2843           pos->width = size * getFontWidth(font);
2844         }
2845
2846         s_cut = getStringCopyN(s, size);
2847
2848         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2849                     s_cut, font, mask_mode);
2850
2851         free(s_cut);
2852       }
2853     }
2854
2855     redraw_mask |= REDRAW_DOOR_1;
2856   }
2857
2858   SetGameStatus(GAME_MODE_PLAYING);
2859 }
2860
2861 void UpdateAndDisplayGameControlValues(void)
2862 {
2863   if (tape.deactivate_display)
2864     return;
2865
2866   UpdateGameControlValues();
2867   DisplayGameControlValues();
2868 }
2869
2870 #if 0
2871 static void UpdateGameDoorValues(void)
2872 {
2873   UpdateGameControlValues();
2874 }
2875 #endif
2876
2877 void DrawGameDoorValues(void)
2878 {
2879   DisplayGameControlValues();
2880 }
2881
2882
2883 // ============================================================================
2884 // InitGameEngine()
2885 // ----------------------------------------------------------------------------
2886 // initialize game engine due to level / tape version number
2887 // ============================================================================
2888
2889 static void InitGameEngine(void)
2890 {
2891   int i, j, k, l, x, y;
2892
2893   // set game engine from tape file when re-playing, else from level file
2894   game.engine_version = (tape.playing ? tape.engine_version :
2895                          level.game_version);
2896
2897   // set single or multi-player game mode (needed for re-playing tapes)
2898   game.team_mode = setup.team_mode;
2899
2900   if (tape.playing)
2901   {
2902     int num_players = 0;
2903
2904     for (i = 0; i < MAX_PLAYERS; i++)
2905       if (tape.player_participates[i])
2906         num_players++;
2907
2908     // multi-player tapes contain input data for more than one player
2909     game.team_mode = (num_players > 1);
2910   }
2911
2912 #if 0
2913   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2914         level.game_version);
2915   Debug("game:init:level", "          tape.file_version   == %06d",
2916         tape.file_version);
2917   Debug("game:init:level", "          tape.game_version   == %06d",
2918         tape.game_version);
2919   Debug("game:init:level", "          tape.engine_version == %06d",
2920         tape.engine_version);
2921   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2922         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2923 #endif
2924
2925   // --------------------------------------------------------------------------
2926   // set flags for bugs and changes according to active game engine version
2927   // --------------------------------------------------------------------------
2928
2929   /*
2930     Summary of bugfix:
2931     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2932
2933     Bug was introduced in version:
2934     2.0.1
2935
2936     Bug was fixed in version:
2937     4.2.0.0
2938
2939     Description:
2940     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2941     but the property "can fall" was missing, which caused some levels to be
2942     unsolvable. This was fixed in version 4.2.0.0.
2943
2944     Affected levels/tapes:
2945     An example for a tape that was fixed by this bugfix is tape 029 from the
2946     level set "rnd_sam_bateman".
2947     The wrong behaviour will still be used for all levels or tapes that were
2948     created/recorded with it. An example for this is tape 023 from the level
2949     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2950   */
2951
2952   boolean use_amoeba_dropping_cannot_fall_bug =
2953     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2954       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2955      (tape.playing &&
2956       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2957       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2958
2959   /*
2960     Summary of bugfix/change:
2961     Fixed move speed of elements entering or leaving magic wall.
2962
2963     Fixed/changed in version:
2964     2.0.1
2965
2966     Description:
2967     Before 2.0.1, move speed of elements entering or leaving magic wall was
2968     twice as fast as it is now.
2969     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2970
2971     Affected levels/tapes:
2972     The first condition is generally needed for all levels/tapes before version
2973     2.0.1, which might use the old behaviour before it was changed; known tapes
2974     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2975     The second condition is an exception from the above case and is needed for
2976     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2977     above, but before it was known that this change would break tapes like the
2978     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2979     although the engine version while recording maybe was before 2.0.1. There
2980     are a lot of tapes that are affected by this exception, like tape 006 from
2981     the level set "rnd_conor_mancone".
2982   */
2983
2984   boolean use_old_move_stepsize_for_magic_wall =
2985     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2986      !(tape.playing &&
2987        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2988        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2989
2990   /*
2991     Summary of bugfix/change:
2992     Fixed handling for custom elements that change when pushed by the player.
2993
2994     Fixed/changed in version:
2995     3.1.0
2996
2997     Description:
2998     Before 3.1.0, custom elements that "change when pushing" changed directly
2999     after the player started pushing them (until then handled in "DigField()").
3000     Since 3.1.0, these custom elements are not changed until the "pushing"
3001     move of the element is finished (now handled in "ContinueMoving()").
3002
3003     Affected levels/tapes:
3004     The first condition is generally needed for all levels/tapes before version
3005     3.1.0, which might use the old behaviour before it was changed; known tapes
3006     that are affected are some tapes from the level set "Walpurgis Gardens" by
3007     Jamie Cullen.
3008     The second condition is an exception from the above case and is needed for
3009     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3010     above (including some development versions of 3.1.0), but before it was
3011     known that this change would break tapes like the above and was fixed in
3012     3.1.1, so that the changed behaviour was active although the engine version
3013     while recording maybe was before 3.1.0. There is at least one tape that is
3014     affected by this exception, which is the tape for the one-level set "Bug
3015     Machine" by Juergen Bonhagen.
3016   */
3017
3018   game.use_change_when_pushing_bug =
3019     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3020      !(tape.playing &&
3021        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3022        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3023
3024   /*
3025     Summary of bugfix/change:
3026     Fixed handling for blocking the field the player leaves when moving.
3027
3028     Fixed/changed in version:
3029     3.1.1
3030
3031     Description:
3032     Before 3.1.1, when "block last field when moving" was enabled, the field
3033     the player is leaving when moving was blocked for the time of the move,
3034     and was directly unblocked afterwards. This resulted in the last field
3035     being blocked for exactly one less than the number of frames of one player
3036     move. Additionally, even when blocking was disabled, the last field was
3037     blocked for exactly one frame.
3038     Since 3.1.1, due to changes in player movement handling, the last field
3039     is not blocked at all when blocking is disabled. When blocking is enabled,
3040     the last field is blocked for exactly the number of frames of one player
3041     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3042     last field is blocked for exactly one more than the number of frames of
3043     one player move.
3044
3045     Affected levels/tapes:
3046     (!!! yet to be determined -- probably many !!!)
3047   */
3048
3049   game.use_block_last_field_bug =
3050     (game.engine_version < VERSION_IDENT(3,1,1,0));
3051
3052   /* various special flags and settings for native Emerald Mine game engine */
3053
3054   game_em.use_single_button =
3055     (game.engine_version > VERSION_IDENT(4,0,0,2));
3056
3057   game_em.use_snap_key_bug =
3058     (game.engine_version < VERSION_IDENT(4,0,1,0));
3059
3060   game_em.use_random_bug =
3061     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3062
3063   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3064
3065   game_em.use_old_explosions            = use_old_em_engine;
3066   game_em.use_old_android               = use_old_em_engine;
3067   game_em.use_old_push_elements         = use_old_em_engine;
3068   game_em.use_old_push_into_acid        = use_old_em_engine;
3069
3070   game_em.use_wrap_around               = !use_old_em_engine;
3071
3072   // --------------------------------------------------------------------------
3073
3074   // set maximal allowed number of custom element changes per game frame
3075   game.max_num_changes_per_frame = 1;
3076
3077   // default scan direction: scan playfield from top/left to bottom/right
3078   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3079
3080   // dynamically adjust element properties according to game engine version
3081   InitElementPropertiesEngine(game.engine_version);
3082
3083   // ---------- initialize special element properties -------------------------
3084
3085   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3086   if (use_amoeba_dropping_cannot_fall_bug)
3087     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3088
3089   // ---------- initialize player's initial move delay ------------------------
3090
3091   // dynamically adjust player properties according to level information
3092   for (i = 0; i < MAX_PLAYERS; i++)
3093     game.initial_move_delay_value[i] =
3094       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3095
3096   // dynamically adjust player properties according to game engine version
3097   for (i = 0; i < MAX_PLAYERS; i++)
3098     game.initial_move_delay[i] =
3099       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3100        game.initial_move_delay_value[i] : 0);
3101
3102   // ---------- initialize player's initial push delay ------------------------
3103
3104   // dynamically adjust player properties according to game engine version
3105   game.initial_push_delay_value =
3106     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3107
3108   // ---------- initialize changing elements ----------------------------------
3109
3110   // initialize changing elements information
3111   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3112   {
3113     struct ElementInfo *ei = &element_info[i];
3114
3115     // this pointer might have been changed in the level editor
3116     ei->change = &ei->change_page[0];
3117
3118     if (!IS_CUSTOM_ELEMENT(i))
3119     {
3120       ei->change->target_element = EL_EMPTY_SPACE;
3121       ei->change->delay_fixed = 0;
3122       ei->change->delay_random = 0;
3123       ei->change->delay_frames = 1;
3124     }
3125
3126     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3127     {
3128       ei->has_change_event[j] = FALSE;
3129
3130       ei->event_page_nr[j] = 0;
3131       ei->event_page[j] = &ei->change_page[0];
3132     }
3133   }
3134
3135   // add changing elements from pre-defined list
3136   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3137   {
3138     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3139     struct ElementInfo *ei = &element_info[ch_delay->element];
3140
3141     ei->change->target_element       = ch_delay->target_element;
3142     ei->change->delay_fixed          = ch_delay->change_delay;
3143
3144     ei->change->pre_change_function  = ch_delay->pre_change_function;
3145     ei->change->change_function      = ch_delay->change_function;
3146     ei->change->post_change_function = ch_delay->post_change_function;
3147
3148     ei->change->can_change = TRUE;
3149     ei->change->can_change_or_has_action = TRUE;
3150
3151     ei->has_change_event[CE_DELAY] = TRUE;
3152
3153     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3154     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3155   }
3156
3157   // ---------- initialize internal run-time variables ------------------------
3158
3159   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3160   {
3161     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3162
3163     for (j = 0; j < ei->num_change_pages; j++)
3164     {
3165       ei->change_page[j].can_change_or_has_action =
3166         (ei->change_page[j].can_change |
3167          ei->change_page[j].has_action);
3168     }
3169   }
3170
3171   // add change events from custom element configuration
3172   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3173   {
3174     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3175
3176     for (j = 0; j < ei->num_change_pages; j++)
3177     {
3178       if (!ei->change_page[j].can_change_or_has_action)
3179         continue;
3180
3181       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3182       {
3183         // only add event page for the first page found with this event
3184         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3185         {
3186           ei->has_change_event[k] = TRUE;
3187
3188           ei->event_page_nr[k] = j;
3189           ei->event_page[k] = &ei->change_page[j];
3190         }
3191       }
3192     }
3193   }
3194
3195   // ---------- initialize reference elements in change conditions ------------
3196
3197   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3198   {
3199     int element = EL_CUSTOM_START + i;
3200     struct ElementInfo *ei = &element_info[element];
3201
3202     for (j = 0; j < ei->num_change_pages; j++)
3203     {
3204       int trigger_element = ei->change_page[j].initial_trigger_element;
3205
3206       if (trigger_element >= EL_PREV_CE_8 &&
3207           trigger_element <= EL_NEXT_CE_8)
3208         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3209
3210       ei->change_page[j].trigger_element = trigger_element;
3211     }
3212   }
3213
3214   // ---------- initialize run-time trigger player and element ----------------
3215
3216   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3217   {
3218     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3219
3220     for (j = 0; j < ei->num_change_pages; j++)
3221     {
3222       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3223       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3224       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3225       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3226       ei->change_page[j].actual_trigger_ce_value = 0;
3227       ei->change_page[j].actual_trigger_ce_score = 0;
3228     }
3229   }
3230
3231   // ---------- initialize trigger events -------------------------------------
3232
3233   // initialize trigger events information
3234   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3235     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3236       trigger_events[i][j] = FALSE;
3237
3238   // add trigger events from element change event properties
3239   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3240   {
3241     struct ElementInfo *ei = &element_info[i];
3242
3243     for (j = 0; j < ei->num_change_pages; j++)
3244     {
3245       if (!ei->change_page[j].can_change_or_has_action)
3246         continue;
3247
3248       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3249       {
3250         int trigger_element = ei->change_page[j].trigger_element;
3251
3252         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3253         {
3254           if (ei->change_page[j].has_event[k])
3255           {
3256             if (IS_GROUP_ELEMENT(trigger_element))
3257             {
3258               struct ElementGroupInfo *group =
3259                 element_info[trigger_element].group;
3260
3261               for (l = 0; l < group->num_elements_resolved; l++)
3262                 trigger_events[group->element_resolved[l]][k] = TRUE;
3263             }
3264             else if (trigger_element == EL_ANY_ELEMENT)
3265               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3266                 trigger_events[l][k] = TRUE;
3267             else
3268               trigger_events[trigger_element][k] = TRUE;
3269           }
3270         }
3271       }
3272     }
3273   }
3274
3275   // ---------- initialize push delay -----------------------------------------
3276
3277   // initialize push delay values to default
3278   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3279   {
3280     if (!IS_CUSTOM_ELEMENT(i))
3281     {
3282       // set default push delay values (corrected since version 3.0.7-1)
3283       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3284       {
3285         element_info[i].push_delay_fixed = 2;
3286         element_info[i].push_delay_random = 8;
3287       }
3288       else
3289       {
3290         element_info[i].push_delay_fixed = 8;
3291         element_info[i].push_delay_random = 8;
3292       }
3293     }
3294   }
3295
3296   // set push delay value for certain elements from pre-defined list
3297   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3298   {
3299     int e = push_delay_list[i].element;
3300
3301     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3302     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3303   }
3304
3305   // set push delay value for Supaplex elements for newer engine versions
3306   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3307   {
3308     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3309     {
3310       if (IS_SP_ELEMENT(i))
3311       {
3312         // set SP push delay to just enough to push under a falling zonk
3313         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3314
3315         element_info[i].push_delay_fixed  = delay;
3316         element_info[i].push_delay_random = 0;
3317       }
3318     }
3319   }
3320
3321   // ---------- initialize move stepsize --------------------------------------
3322
3323   // initialize move stepsize values to default
3324   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3325     if (!IS_CUSTOM_ELEMENT(i))
3326       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3327
3328   // set move stepsize value for certain elements from pre-defined list
3329   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3330   {
3331     int e = move_stepsize_list[i].element;
3332
3333     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3334
3335     // set move stepsize value for certain elements for older engine versions
3336     if (use_old_move_stepsize_for_magic_wall)
3337     {
3338       if (e == EL_MAGIC_WALL_FILLING ||
3339           e == EL_MAGIC_WALL_EMPTYING ||
3340           e == EL_BD_MAGIC_WALL_FILLING ||
3341           e == EL_BD_MAGIC_WALL_EMPTYING)
3342         element_info[e].move_stepsize *= 2;
3343     }
3344   }
3345
3346   // ---------- initialize collect score --------------------------------------
3347
3348   // initialize collect score values for custom elements from initial value
3349   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3350     if (IS_CUSTOM_ELEMENT(i))
3351       element_info[i].collect_score = element_info[i].collect_score_initial;
3352
3353   // ---------- initialize collect count --------------------------------------
3354
3355   // initialize collect count values for non-custom elements
3356   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3357     if (!IS_CUSTOM_ELEMENT(i))
3358       element_info[i].collect_count_initial = 0;
3359
3360   // add collect count values for all elements from pre-defined list
3361   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3362     element_info[collect_count_list[i].element].collect_count_initial =
3363       collect_count_list[i].count;
3364
3365   // ---------- initialize access direction -----------------------------------
3366
3367   // initialize access direction values to default (access from every side)
3368   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3369     if (!IS_CUSTOM_ELEMENT(i))
3370       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3371
3372   // set access direction value for certain elements from pre-defined list
3373   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3374     element_info[access_direction_list[i].element].access_direction =
3375       access_direction_list[i].direction;
3376
3377   // ---------- initialize explosion content ----------------------------------
3378   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379   {
3380     if (IS_CUSTOM_ELEMENT(i))
3381       continue;
3382
3383     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3384     {
3385       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3386
3387       element_info[i].content.e[x][y] =
3388         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3389          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3390          i == EL_PLAYER_3 ? EL_EMERALD :
3391          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3392          i == EL_MOLE ? EL_EMERALD_RED :
3393          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3394          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3395          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3396          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3397          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3398          i == EL_WALL_EMERALD ? EL_EMERALD :
3399          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3400          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3401          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3402          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3403          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3404          i == EL_WALL_PEARL ? EL_PEARL :
3405          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3406          EL_EMPTY);
3407     }
3408   }
3409
3410   // ---------- initialize recursion detection --------------------------------
3411   recursion_loop_depth = 0;
3412   recursion_loop_detected = FALSE;
3413   recursion_loop_element = EL_UNDEFINED;
3414
3415   // ---------- initialize graphics engine ------------------------------------
3416   game.scroll_delay_value =
3417     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3418      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3419      !setup.forced_scroll_delay           ? 0 :
3420      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3421   game.scroll_delay_value =
3422     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3423
3424   // ---------- initialize game engine snapshots ------------------------------
3425   for (i = 0; i < MAX_PLAYERS; i++)
3426     game.snapshot.last_action[i] = 0;
3427   game.snapshot.changed_action = FALSE;
3428   game.snapshot.collected_item = FALSE;
3429   game.snapshot.mode =
3430     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3431      SNAPSHOT_MODE_EVERY_STEP :
3432      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3433      SNAPSHOT_MODE_EVERY_MOVE :
3434      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3435      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3436   game.snapshot.save_snapshot = FALSE;
3437
3438   // ---------- initialize level time for Supaplex engine ---------------------
3439   // Supaplex levels with time limit currently unsupported -- should be added
3440   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3441     level.time = 0;
3442
3443   // ---------- initialize flags for handling game actions --------------------
3444
3445   // set flags for game actions to default values
3446   game.use_key_actions = TRUE;
3447   game.use_mouse_actions = FALSE;
3448
3449   // when using Mirror Magic game engine, handle mouse events only
3450   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3451   {
3452     game.use_key_actions = FALSE;
3453     game.use_mouse_actions = TRUE;
3454   }
3455
3456   // check for custom elements with mouse click events
3457   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3458   {
3459     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3460     {
3461       int element = EL_CUSTOM_START + i;
3462
3463       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3464           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3465           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3466           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3467         game.use_mouse_actions = TRUE;
3468     }
3469   }
3470 }
3471
3472 static int get_num_special_action(int element, int action_first,
3473                                   int action_last)
3474 {
3475   int num_special_action = 0;
3476   int i, j;
3477
3478   for (i = action_first; i <= action_last; i++)
3479   {
3480     boolean found = FALSE;
3481
3482     for (j = 0; j < NUM_DIRECTIONS; j++)
3483       if (el_act_dir2img(element, i, j) !=
3484           el_act_dir2img(element, ACTION_DEFAULT, j))
3485         found = TRUE;
3486
3487     if (found)
3488       num_special_action++;
3489     else
3490       break;
3491   }
3492
3493   return num_special_action;
3494 }
3495
3496
3497 // ============================================================================
3498 // InitGame()
3499 // ----------------------------------------------------------------------------
3500 // initialize and start new game
3501 // ============================================================================
3502
3503 #if DEBUG_INIT_PLAYER
3504 static void DebugPrintPlayerStatus(char *message)
3505 {
3506   int i;
3507
3508   if (!options.debug)
3509     return;
3510
3511   Debug("game:init:player", "%s:", message);
3512
3513   for (i = 0; i < MAX_PLAYERS; i++)
3514   {
3515     struct PlayerInfo *player = &stored_player[i];
3516
3517     Debug("game:init:player",
3518           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3519           i + 1,
3520           player->present,
3521           player->connected,
3522           player->connected_locally,
3523           player->connected_network,
3524           player->active,
3525           (local_player == player ? " (local player)" : ""));
3526   }
3527 }
3528 #endif
3529
3530 void InitGame(void)
3531 {
3532   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3533   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3534   int fade_mask = REDRAW_FIELD;
3535
3536   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3537   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3538   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3539   int initial_move_dir = MV_DOWN;
3540   int i, j, x, y;
3541
3542   // required here to update video display before fading (FIX THIS)
3543   DrawMaskedBorder(REDRAW_DOOR_2);
3544
3545   if (!game.restart_level)
3546     CloseDoor(DOOR_CLOSE_1);
3547
3548   SetGameStatus(GAME_MODE_PLAYING);
3549
3550   if (level_editor_test_game)
3551     FadeSkipNextFadeOut();
3552   else
3553     FadeSetEnterScreen();
3554
3555   if (CheckFadeAll())
3556     fade_mask = REDRAW_ALL;
3557
3558   FadeLevelSoundsAndMusic();
3559
3560   ExpireSoundLoops(TRUE);
3561
3562   FadeOut(fade_mask);
3563
3564   if (level_editor_test_game)
3565     FadeSkipNextFadeIn();
3566
3567   // needed if different viewport properties defined for playing
3568   ChangeViewportPropertiesIfNeeded();
3569
3570   ClearField();
3571
3572   DrawCompleteVideoDisplay();
3573
3574   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3575
3576   InitGameEngine();
3577   InitGameControlValues();
3578
3579   // initialize tape actions from game when recording tape
3580   if (tape.recording)
3581   {
3582     tape.use_key_actions   = game.use_key_actions;
3583     tape.use_mouse_actions = game.use_mouse_actions;
3584   }
3585
3586   // don't play tapes over network
3587   network_playing = (network.enabled && !tape.playing);
3588
3589   for (i = 0; i < MAX_PLAYERS; i++)
3590   {
3591     struct PlayerInfo *player = &stored_player[i];
3592
3593     player->index_nr = i;
3594     player->index_bit = (1 << i);
3595     player->element_nr = EL_PLAYER_1 + i;
3596
3597     player->present = FALSE;
3598     player->active = FALSE;
3599     player->mapped = FALSE;
3600
3601     player->killed = FALSE;
3602     player->reanimated = FALSE;
3603     player->buried = FALSE;
3604
3605     player->action = 0;
3606     player->effective_action = 0;
3607     player->programmed_action = 0;
3608     player->snap_action = 0;
3609
3610     player->mouse_action.lx = 0;
3611     player->mouse_action.ly = 0;
3612     player->mouse_action.button = 0;
3613     player->mouse_action.button_hint = 0;
3614
3615     player->effective_mouse_action.lx = 0;
3616     player->effective_mouse_action.ly = 0;
3617     player->effective_mouse_action.button = 0;
3618     player->effective_mouse_action.button_hint = 0;
3619
3620     for (j = 0; j < MAX_NUM_KEYS; j++)
3621       player->key[j] = FALSE;
3622
3623     player->num_white_keys = 0;
3624
3625     player->dynabomb_count = 0;
3626     player->dynabomb_size = 1;
3627     player->dynabombs_left = 0;
3628     player->dynabomb_xl = FALSE;
3629
3630     player->MovDir = initial_move_dir;
3631     player->MovPos = 0;
3632     player->GfxPos = 0;
3633     player->GfxDir = initial_move_dir;
3634     player->GfxAction = ACTION_DEFAULT;
3635     player->Frame = 0;
3636     player->StepFrame = 0;
3637
3638     player->initial_element = player->element_nr;
3639     player->artwork_element =
3640       (level.use_artwork_element[i] ? level.artwork_element[i] :
3641        player->element_nr);
3642     player->use_murphy = FALSE;
3643
3644     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3645     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3646
3647     player->gravity = level.initial_player_gravity[i];
3648
3649     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3650
3651     player->actual_frame_counter = 0;
3652
3653     player->step_counter = 0;
3654
3655     player->last_move_dir = initial_move_dir;
3656
3657     player->is_active = FALSE;
3658
3659     player->is_waiting = FALSE;
3660     player->is_moving = FALSE;
3661     player->is_auto_moving = FALSE;
3662     player->is_digging = FALSE;
3663     player->is_snapping = FALSE;
3664     player->is_collecting = FALSE;
3665     player->is_pushing = FALSE;
3666     player->is_switching = FALSE;
3667     player->is_dropping = FALSE;
3668     player->is_dropping_pressed = FALSE;
3669
3670     player->is_bored = FALSE;
3671     player->is_sleeping = FALSE;
3672
3673     player->was_waiting = TRUE;
3674     player->was_moving = FALSE;
3675     player->was_snapping = FALSE;
3676     player->was_dropping = FALSE;
3677
3678     player->force_dropping = FALSE;
3679
3680     player->frame_counter_bored = -1;
3681     player->frame_counter_sleeping = -1;
3682
3683     player->anim_delay_counter = 0;
3684     player->post_delay_counter = 0;
3685
3686     player->dir_waiting = initial_move_dir;
3687     player->action_waiting = ACTION_DEFAULT;
3688     player->last_action_waiting = ACTION_DEFAULT;
3689     player->special_action_bored = ACTION_DEFAULT;
3690     player->special_action_sleeping = ACTION_DEFAULT;
3691
3692     player->switch_x = -1;
3693     player->switch_y = -1;
3694
3695     player->drop_x = -1;
3696     player->drop_y = -1;
3697
3698     player->show_envelope = 0;
3699
3700     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3701
3702     player->push_delay       = -1;      // initialized when pushing starts
3703     player->push_delay_value = game.initial_push_delay_value;
3704
3705     player->drop_delay = 0;
3706     player->drop_pressed_delay = 0;
3707
3708     player->last_jx = -1;
3709     player->last_jy = -1;
3710     player->jx = -1;
3711     player->jy = -1;
3712
3713     player->shield_normal_time_left = 0;
3714     player->shield_deadly_time_left = 0;
3715
3716     player->inventory_infinite_element = EL_UNDEFINED;
3717     player->inventory_size = 0;
3718
3719     if (level.use_initial_inventory[i])
3720     {
3721       for (j = 0; j < level.initial_inventory_size[i]; j++)
3722       {
3723         int element = level.initial_inventory_content[i][j];
3724         int collect_count = element_info[element].collect_count_initial;
3725         int k;
3726
3727         if (!IS_CUSTOM_ELEMENT(element))
3728           collect_count = 1;
3729
3730         if (collect_count == 0)
3731           player->inventory_infinite_element = element;
3732         else
3733           for (k = 0; k < collect_count; k++)
3734             if (player->inventory_size < MAX_INVENTORY_SIZE)
3735               player->inventory_element[player->inventory_size++] = element;
3736       }
3737     }
3738
3739     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3740     SnapField(player, 0, 0);
3741
3742     map_player_action[i] = i;
3743   }
3744
3745   network_player_action_received = FALSE;
3746
3747   // initial null action
3748   if (network_playing)
3749     SendToServer_MovePlayer(MV_NONE);
3750
3751   FrameCounter = 0;
3752   TimeFrames = 0;
3753   TimePlayed = 0;
3754   TimeLeft = level.time;
3755   TapeTime = 0;
3756
3757   ScreenMovDir = MV_NONE;
3758   ScreenMovPos = 0;
3759   ScreenGfxPos = 0;
3760
3761   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3762
3763   game.robot_wheel_x = -1;
3764   game.robot_wheel_y = -1;
3765
3766   game.exit_x = -1;
3767   game.exit_y = -1;
3768
3769   game.all_players_gone = FALSE;
3770
3771   game.LevelSolved = FALSE;
3772   game.GameOver = FALSE;
3773
3774   game.GamePlayed = !tape.playing;
3775
3776   game.LevelSolved_GameWon = FALSE;
3777   game.LevelSolved_GameEnd = FALSE;
3778   game.LevelSolved_SaveTape = FALSE;
3779   game.LevelSolved_SaveScore = FALSE;
3780
3781   game.LevelSolved_CountingTime = 0;
3782   game.LevelSolved_CountingScore = 0;
3783   game.LevelSolved_CountingHealth = 0;
3784
3785   game.panel.active = TRUE;
3786
3787   game.no_time_limit = (level.time == 0);
3788
3789   game.yamyam_content_nr = 0;
3790   game.robot_wheel_active = FALSE;
3791   game.magic_wall_active = FALSE;
3792   game.magic_wall_time_left = 0;
3793   game.light_time_left = 0;
3794   game.timegate_time_left = 0;
3795   game.switchgate_pos = 0;
3796   game.wind_direction = level.wind_direction_initial;
3797
3798   game.score = 0;
3799   game.score_final = 0;
3800
3801   game.health = MAX_HEALTH;
3802   game.health_final = MAX_HEALTH;
3803
3804   game.gems_still_needed = level.gems_needed;
3805   game.sokoban_fields_still_needed = 0;
3806   game.sokoban_objects_still_needed = 0;
3807   game.lights_still_needed = 0;
3808   game.players_still_needed = 0;
3809   game.friends_still_needed = 0;
3810
3811   game.lenses_time_left = 0;
3812   game.magnify_time_left = 0;
3813
3814   game.ball_active = level.ball_active_initial;
3815   game.ball_content_nr = 0;
3816
3817   game.explosions_delayed = TRUE;
3818
3819   game.envelope_active = FALSE;
3820
3821   for (i = 0; i < NUM_BELTS; i++)
3822   {
3823     game.belt_dir[i] = MV_NONE;
3824     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3825   }
3826
3827   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3828     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3829
3830 #if DEBUG_INIT_PLAYER
3831   DebugPrintPlayerStatus("Player status at level initialization");
3832 #endif
3833
3834   SCAN_PLAYFIELD(x, y)
3835   {
3836     Tile[x][y] = Last[x][y] = level.field[x][y];
3837     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3838     ChangeDelay[x][y] = 0;
3839     ChangePage[x][y] = -1;
3840     CustomValue[x][y] = 0;              // initialized in InitField()
3841     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3842     AmoebaNr[x][y] = 0;
3843     WasJustMoving[x][y] = 0;
3844     WasJustFalling[x][y] = 0;
3845     CheckCollision[x][y] = 0;
3846     CheckImpact[x][y] = 0;
3847     Stop[x][y] = FALSE;
3848     Pushed[x][y] = FALSE;
3849
3850     ChangeCount[x][y] = 0;
3851     ChangeEvent[x][y] = -1;
3852
3853     ExplodePhase[x][y] = 0;
3854     ExplodeDelay[x][y] = 0;
3855     ExplodeField[x][y] = EX_TYPE_NONE;
3856
3857     RunnerVisit[x][y] = 0;
3858     PlayerVisit[x][y] = 0;
3859
3860     GfxFrame[x][y] = 0;
3861     GfxRandom[x][y] = INIT_GFX_RANDOM();
3862     GfxElement[x][y] = EL_UNDEFINED;
3863     GfxAction[x][y] = ACTION_DEFAULT;
3864     GfxDir[x][y] = MV_NONE;
3865     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3866   }
3867
3868   SCAN_PLAYFIELD(x, y)
3869   {
3870     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3871       emulate_bd = FALSE;
3872     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3873       emulate_sb = FALSE;
3874     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3875       emulate_sp = FALSE;
3876
3877     InitField(x, y, TRUE);
3878
3879     ResetGfxAnimation(x, y);
3880   }
3881
3882   InitBeltMovement();
3883
3884   for (i = 0; i < MAX_PLAYERS; i++)
3885   {
3886     struct PlayerInfo *player = &stored_player[i];
3887
3888     // set number of special actions for bored and sleeping animation
3889     player->num_special_action_bored =
3890       get_num_special_action(player->artwork_element,
3891                              ACTION_BORING_1, ACTION_BORING_LAST);
3892     player->num_special_action_sleeping =
3893       get_num_special_action(player->artwork_element,
3894                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3895   }
3896
3897   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3898                     emulate_sb ? EMU_SOKOBAN :
3899                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3900
3901   // initialize type of slippery elements
3902   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3903   {
3904     if (!IS_CUSTOM_ELEMENT(i))
3905     {
3906       // default: elements slip down either to the left or right randomly
3907       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3908
3909       // SP style elements prefer to slip down on the left side
3910       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3911         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3912
3913       // BD style elements prefer to slip down on the left side
3914       if (game.emulation == EMU_BOULDERDASH)
3915         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3916     }
3917   }
3918
3919   // initialize explosion and ignition delay
3920   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3921   {
3922     if (!IS_CUSTOM_ELEMENT(i))
3923     {
3924       int num_phase = 8;
3925       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3926                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3927                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3928       int last_phase = (num_phase + 1) * delay;
3929       int half_phase = (num_phase / 2) * delay;
3930
3931       element_info[i].explosion_delay = last_phase - 1;
3932       element_info[i].ignition_delay = half_phase;
3933
3934       if (i == EL_BLACK_ORB)
3935         element_info[i].ignition_delay = 1;
3936     }
3937   }
3938
3939   // correct non-moving belts to start moving left
3940   for (i = 0; i < NUM_BELTS; i++)
3941     if (game.belt_dir[i] == MV_NONE)
3942       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3943
3944 #if USE_NEW_PLAYER_ASSIGNMENTS
3945   // use preferred player also in local single-player mode
3946   if (!network.enabled && !game.team_mode)
3947   {
3948     int new_index_nr = setup.network_player_nr;
3949
3950     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3951     {
3952       for (i = 0; i < MAX_PLAYERS; i++)
3953         stored_player[i].connected_locally = FALSE;
3954
3955       stored_player[new_index_nr].connected_locally = TRUE;
3956     }
3957   }
3958
3959   for (i = 0; i < MAX_PLAYERS; i++)
3960   {
3961     stored_player[i].connected = FALSE;
3962
3963     // in network game mode, the local player might not be the first player
3964     if (stored_player[i].connected_locally)
3965       local_player = &stored_player[i];
3966   }
3967
3968   if (!network.enabled)
3969     local_player->connected = TRUE;
3970
3971   if (tape.playing)
3972   {
3973     for (i = 0; i < MAX_PLAYERS; i++)
3974       stored_player[i].connected = tape.player_participates[i];
3975   }
3976   else if (network.enabled)
3977   {
3978     // add team mode players connected over the network (needed for correct
3979     // assignment of player figures from level to locally playing players)
3980
3981     for (i = 0; i < MAX_PLAYERS; i++)
3982       if (stored_player[i].connected_network)
3983         stored_player[i].connected = TRUE;
3984   }
3985   else if (game.team_mode)
3986   {
3987     // try to guess locally connected team mode players (needed for correct
3988     // assignment of player figures from level to locally playing players)
3989
3990     for (i = 0; i < MAX_PLAYERS; i++)
3991       if (setup.input[i].use_joystick ||
3992           setup.input[i].key.left != KSYM_UNDEFINED)
3993         stored_player[i].connected = TRUE;
3994   }
3995
3996 #if DEBUG_INIT_PLAYER
3997   DebugPrintPlayerStatus("Player status after level initialization");
3998 #endif
3999
4000 #if DEBUG_INIT_PLAYER
4001   Debug("game:init:player", "Reassigning players ...");
4002 #endif
4003
4004   // check if any connected player was not found in playfield
4005   for (i = 0; i < MAX_PLAYERS; i++)
4006   {
4007     struct PlayerInfo *player = &stored_player[i];
4008
4009     if (player->connected && !player->present)
4010     {
4011       struct PlayerInfo *field_player = NULL;
4012
4013 #if DEBUG_INIT_PLAYER
4014       Debug("game:init:player",
4015             "- looking for field player for player %d ...", i + 1);
4016 #endif
4017
4018       // assign first free player found that is present in the playfield
4019
4020       // first try: look for unmapped playfield player that is not connected
4021       for (j = 0; j < MAX_PLAYERS; j++)
4022         if (field_player == NULL &&
4023             stored_player[j].present &&
4024             !stored_player[j].mapped &&
4025             !stored_player[j].connected)
4026           field_player = &stored_player[j];
4027
4028       // second try: look for *any* unmapped playfield player
4029       for (j = 0; j < MAX_PLAYERS; j++)
4030         if (field_player == NULL &&
4031             stored_player[j].present &&
4032             !stored_player[j].mapped)
4033           field_player = &stored_player[j];
4034
4035       if (field_player != NULL)
4036       {
4037         int jx = field_player->jx, jy = field_player->jy;
4038
4039 #if DEBUG_INIT_PLAYER
4040         Debug("game:init:player", "- found player %d",
4041               field_player->index_nr + 1);
4042 #endif
4043
4044         player->present = FALSE;
4045         player->active = FALSE;
4046
4047         field_player->present = TRUE;
4048         field_player->active = TRUE;
4049
4050         /*
4051         player->initial_element = field_player->initial_element;
4052         player->artwork_element = field_player->artwork_element;
4053
4054         player->block_last_field       = field_player->block_last_field;
4055         player->block_delay_adjustment = field_player->block_delay_adjustment;
4056         */
4057
4058         StorePlayer[jx][jy] = field_player->element_nr;
4059
4060         field_player->jx = field_player->last_jx = jx;
4061         field_player->jy = field_player->last_jy = jy;
4062
4063         if (local_player == player)
4064           local_player = field_player;
4065
4066         map_player_action[field_player->index_nr] = i;
4067
4068         field_player->mapped = TRUE;
4069
4070 #if DEBUG_INIT_PLAYER
4071         Debug("game:init:player", "- map_player_action[%d] == %d",
4072               field_player->index_nr + 1, i + 1);
4073 #endif
4074       }
4075     }
4076
4077     if (player->connected && player->present)
4078       player->mapped = TRUE;
4079   }
4080
4081 #if DEBUG_INIT_PLAYER
4082   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4083 #endif
4084
4085 #else
4086
4087   // check if any connected player was not found in playfield
4088   for (i = 0; i < MAX_PLAYERS; i++)
4089   {
4090     struct PlayerInfo *player = &stored_player[i];
4091
4092     if (player->connected && !player->present)
4093     {
4094       for (j = 0; j < MAX_PLAYERS; j++)
4095       {
4096         struct PlayerInfo *field_player = &stored_player[j];
4097         int jx = field_player->jx, jy = field_player->jy;
4098
4099         // assign first free player found that is present in the playfield
4100         if (field_player->present && !field_player->connected)
4101         {
4102           player->present = TRUE;
4103           player->active = TRUE;
4104
4105           field_player->present = FALSE;
4106           field_player->active = FALSE;
4107
4108           player->initial_element = field_player->initial_element;
4109           player->artwork_element = field_player->artwork_element;
4110
4111           player->block_last_field       = field_player->block_last_field;
4112           player->block_delay_adjustment = field_player->block_delay_adjustment;
4113
4114           StorePlayer[jx][jy] = player->element_nr;
4115
4116           player->jx = player->last_jx = jx;
4117           player->jy = player->last_jy = jy;
4118
4119           break;
4120         }
4121       }
4122     }
4123   }
4124 #endif
4125
4126 #if 0
4127   Debug("game:init:player", "local_player->present == %d",
4128         local_player->present);
4129 #endif
4130
4131   // set focus to local player for network games, else to all players
4132   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4133   game.centered_player_nr_next = game.centered_player_nr;
4134   game.set_centered_player = FALSE;
4135   game.set_centered_player_wrap = FALSE;
4136
4137   if (network_playing && tape.recording)
4138   {
4139     // store client dependent player focus when recording network games
4140     tape.centered_player_nr_next = game.centered_player_nr_next;
4141     tape.set_centered_player = TRUE;
4142   }
4143
4144   if (tape.playing)
4145   {
4146     // when playing a tape, eliminate all players who do not participate
4147
4148 #if USE_NEW_PLAYER_ASSIGNMENTS
4149
4150     if (!game.team_mode)
4151     {
4152       for (i = 0; i < MAX_PLAYERS; i++)
4153       {
4154         if (stored_player[i].active &&
4155             !tape.player_participates[map_player_action[i]])
4156         {
4157           struct PlayerInfo *player = &stored_player[i];
4158           int jx = player->jx, jy = player->jy;
4159
4160 #if DEBUG_INIT_PLAYER
4161           Debug("game:init:player", "Removing player %d at (%d, %d)",
4162                 i + 1, jx, jy);
4163 #endif
4164
4165           player->active = FALSE;
4166           StorePlayer[jx][jy] = 0;
4167           Tile[jx][jy] = EL_EMPTY;
4168         }
4169       }
4170     }
4171
4172 #else
4173
4174     for (i = 0; i < MAX_PLAYERS; i++)
4175     {
4176       if (stored_player[i].active &&
4177           !tape.player_participates[i])
4178       {
4179         struct PlayerInfo *player = &stored_player[i];
4180         int jx = player->jx, jy = player->jy;
4181
4182         player->active = FALSE;
4183         StorePlayer[jx][jy] = 0;
4184         Tile[jx][jy] = EL_EMPTY;
4185       }
4186     }
4187 #endif
4188   }
4189   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4190   {
4191     // when in single player mode, eliminate all but the local player
4192
4193     for (i = 0; i < MAX_PLAYERS; i++)
4194     {
4195       struct PlayerInfo *player = &stored_player[i];
4196
4197       if (player->active && player != local_player)
4198       {
4199         int jx = player->jx, jy = player->jy;
4200
4201         player->active = FALSE;
4202         player->present = FALSE;
4203
4204         StorePlayer[jx][jy] = 0;
4205         Tile[jx][jy] = EL_EMPTY;
4206       }
4207     }
4208   }
4209
4210   for (i = 0; i < MAX_PLAYERS; i++)
4211     if (stored_player[i].active)
4212       game.players_still_needed++;
4213
4214   if (level.solved_by_one_player)
4215     game.players_still_needed = 1;
4216
4217   // when recording the game, store which players take part in the game
4218   if (tape.recording)
4219   {
4220 #if USE_NEW_PLAYER_ASSIGNMENTS
4221     for (i = 0; i < MAX_PLAYERS; i++)
4222       if (stored_player[i].connected)
4223         tape.player_participates[i] = TRUE;
4224 #else
4225     for (i = 0; i < MAX_PLAYERS; i++)
4226       if (stored_player[i].active)
4227         tape.player_participates[i] = TRUE;
4228 #endif
4229   }
4230
4231 #if DEBUG_INIT_PLAYER
4232   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4233 #endif
4234
4235   if (BorderElement == EL_EMPTY)
4236   {
4237     SBX_Left = 0;
4238     SBX_Right = lev_fieldx - SCR_FIELDX;
4239     SBY_Upper = 0;
4240     SBY_Lower = lev_fieldy - SCR_FIELDY;
4241   }
4242   else
4243   {
4244     SBX_Left = -1;
4245     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4246     SBY_Upper = -1;
4247     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4248   }
4249
4250   if (full_lev_fieldx <= SCR_FIELDX)
4251     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4252   if (full_lev_fieldy <= SCR_FIELDY)
4253     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4254
4255   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4256     SBX_Left--;
4257   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4258     SBY_Upper--;
4259
4260   // if local player not found, look for custom element that might create
4261   // the player (make some assumptions about the right custom element)
4262   if (!local_player->present)
4263   {
4264     int start_x = 0, start_y = 0;
4265     int found_rating = 0;
4266     int found_element = EL_UNDEFINED;
4267     int player_nr = local_player->index_nr;
4268
4269     SCAN_PLAYFIELD(x, y)
4270     {
4271       int element = Tile[x][y];
4272       int content;
4273       int xx, yy;
4274       boolean is_player;
4275
4276       if (level.use_start_element[player_nr] &&
4277           level.start_element[player_nr] == element &&
4278           found_rating < 4)
4279       {
4280         start_x = x;
4281         start_y = y;
4282
4283         found_rating = 4;
4284         found_element = element;
4285       }
4286
4287       if (!IS_CUSTOM_ELEMENT(element))
4288         continue;
4289
4290       if (CAN_CHANGE(element))
4291       {
4292         for (i = 0; i < element_info[element].num_change_pages; i++)
4293         {
4294           // check for player created from custom element as single target
4295           content = element_info[element].change_page[i].target_element;
4296           is_player = ELEM_IS_PLAYER(content);
4297
4298           if (is_player && (found_rating < 3 ||
4299                             (found_rating == 3 && element < found_element)))
4300           {
4301             start_x = x;
4302             start_y = y;
4303
4304             found_rating = 3;
4305             found_element = element;
4306           }
4307         }
4308       }
4309
4310       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4311       {
4312         // check for player created from custom element as explosion content
4313         content = element_info[element].content.e[xx][yy];
4314         is_player = ELEM_IS_PLAYER(content);
4315
4316         if (is_player && (found_rating < 2 ||
4317                           (found_rating == 2 && element < found_element)))
4318         {
4319           start_x = x + xx - 1;
4320           start_y = y + yy - 1;
4321
4322           found_rating = 2;
4323           found_element = element;
4324         }
4325
4326         if (!CAN_CHANGE(element))
4327           continue;
4328
4329         for (i = 0; i < element_info[element].num_change_pages; i++)
4330         {
4331           // check for player created from custom element as extended target
4332           content =
4333             element_info[element].change_page[i].target_content.e[xx][yy];
4334
4335           is_player = ELEM_IS_PLAYER(content);
4336
4337           if (is_player && (found_rating < 1 ||
4338                             (found_rating == 1 && element < found_element)))
4339           {
4340             start_x = x + xx - 1;
4341             start_y = y + yy - 1;
4342
4343             found_rating = 1;
4344             found_element = element;
4345           }
4346         }
4347       }
4348     }
4349
4350     scroll_x = SCROLL_POSITION_X(start_x);
4351     scroll_y = SCROLL_POSITION_Y(start_y);
4352   }
4353   else
4354   {
4355     scroll_x = SCROLL_POSITION_X(local_player->jx);
4356     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4357   }
4358
4359   // !!! FIX THIS (START) !!!
4360   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4361   {
4362     InitGameEngine_EM();
4363   }
4364   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4365   {
4366     InitGameEngine_SP();
4367   }
4368   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4369   {
4370     InitGameEngine_MM();
4371   }
4372   else
4373   {
4374     DrawLevel(REDRAW_FIELD);
4375     DrawAllPlayers();
4376
4377     // after drawing the level, correct some elements
4378     if (game.timegate_time_left == 0)
4379       CloseAllOpenTimegates();
4380   }
4381
4382   // blit playfield from scroll buffer to normal back buffer for fading in
4383   BlitScreenToBitmap(backbuffer);
4384   // !!! FIX THIS (END) !!!
4385
4386   DrawMaskedBorder(fade_mask);
4387
4388   FadeIn(fade_mask);
4389
4390 #if 1
4391   // full screen redraw is required at this point in the following cases:
4392   // - special editor door undrawn when game was started from level editor
4393   // - drawing area (playfield) was changed and has to be removed completely
4394   redraw_mask = REDRAW_ALL;
4395   BackToFront();
4396 #endif
4397
4398   if (!game.restart_level)
4399   {
4400     // copy default game door content to main double buffer
4401
4402     // !!! CHECK AGAIN !!!
4403     SetPanelBackground();
4404     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4405     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4406   }
4407
4408   SetPanelBackground();
4409   SetDrawBackgroundMask(REDRAW_DOOR_1);
4410
4411   UpdateAndDisplayGameControlValues();
4412
4413   if (!game.restart_level)
4414   {
4415     UnmapGameButtons();
4416     UnmapTapeButtons();
4417
4418     FreeGameButtons();
4419     CreateGameButtons();
4420
4421     MapGameButtons();
4422     MapTapeButtons();
4423
4424     // copy actual game door content to door double buffer for OpenDoor()
4425     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4426
4427     OpenDoor(DOOR_OPEN_ALL);
4428
4429     KeyboardAutoRepeatOffUnlessAutoplay();
4430
4431 #if DEBUG_INIT_PLAYER
4432     DebugPrintPlayerStatus("Player status (final)");
4433 #endif
4434   }
4435
4436   UnmapAllGadgets();
4437
4438   MapGameButtons();
4439   MapTapeButtons();
4440
4441   if (!game.restart_level && !tape.playing)
4442   {
4443     LevelStats_incPlayed(level_nr);
4444
4445     SaveLevelSetup_SeriesInfo();
4446   }
4447
4448   game.restart_level = FALSE;
4449   game.restart_game_message = NULL;
4450   game.request_active = FALSE;
4451
4452   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4453     InitGameActions_MM();
4454
4455   SaveEngineSnapshotToListInitial();
4456
4457   if (!game.restart_level)
4458   {
4459     PlaySound(SND_GAME_STARTING);
4460
4461     if (setup.sound_music)
4462       PlayLevelMusic();
4463   }
4464 }
4465
4466 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4467                         int actual_player_x, int actual_player_y)
4468 {
4469   // this is used for non-R'n'D game engines to update certain engine values
4470
4471   // needed to determine if sounds are played within the visible screen area
4472   scroll_x = actual_scroll_x;
4473   scroll_y = actual_scroll_y;
4474
4475   // needed to get player position for "follow finger" playing input method
4476   local_player->jx = actual_player_x;
4477   local_player->jy = actual_player_y;
4478 }
4479
4480 void InitMovDir(int x, int y)
4481 {
4482   int i, element = Tile[x][y];
4483   static int xy[4][2] =
4484   {
4485     {  0, +1 },
4486     { +1,  0 },
4487     {  0, -1 },
4488     { -1,  0 }
4489   };
4490   static int direction[3][4] =
4491   {
4492     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4493     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4494     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4495   };
4496
4497   switch (element)
4498   {
4499     case EL_BUG_RIGHT:
4500     case EL_BUG_UP:
4501     case EL_BUG_LEFT:
4502     case EL_BUG_DOWN:
4503       Tile[x][y] = EL_BUG;
4504       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4505       break;
4506
4507     case EL_SPACESHIP_RIGHT:
4508     case EL_SPACESHIP_UP:
4509     case EL_SPACESHIP_LEFT:
4510     case EL_SPACESHIP_DOWN:
4511       Tile[x][y] = EL_SPACESHIP;
4512       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4513       break;
4514
4515     case EL_BD_BUTTERFLY_RIGHT:
4516     case EL_BD_BUTTERFLY_UP:
4517     case EL_BD_BUTTERFLY_LEFT:
4518     case EL_BD_BUTTERFLY_DOWN:
4519       Tile[x][y] = EL_BD_BUTTERFLY;
4520       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4521       break;
4522
4523     case EL_BD_FIREFLY_RIGHT:
4524     case EL_BD_FIREFLY_UP:
4525     case EL_BD_FIREFLY_LEFT:
4526     case EL_BD_FIREFLY_DOWN:
4527       Tile[x][y] = EL_BD_FIREFLY;
4528       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4529       break;
4530
4531     case EL_PACMAN_RIGHT:
4532     case EL_PACMAN_UP:
4533     case EL_PACMAN_LEFT:
4534     case EL_PACMAN_DOWN:
4535       Tile[x][y] = EL_PACMAN;
4536       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4537       break;
4538
4539     case EL_YAMYAM_LEFT:
4540     case EL_YAMYAM_RIGHT:
4541     case EL_YAMYAM_UP:
4542     case EL_YAMYAM_DOWN:
4543       Tile[x][y] = EL_YAMYAM;
4544       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4545       break;
4546
4547     case EL_SP_SNIKSNAK:
4548       MovDir[x][y] = MV_UP;
4549       break;
4550
4551     case EL_SP_ELECTRON:
4552       MovDir[x][y] = MV_LEFT;
4553       break;
4554
4555     case EL_MOLE_LEFT:
4556     case EL_MOLE_RIGHT:
4557     case EL_MOLE_UP:
4558     case EL_MOLE_DOWN:
4559       Tile[x][y] = EL_MOLE;
4560       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4561       break;
4562
4563     case EL_SPRING_LEFT:
4564     case EL_SPRING_RIGHT:
4565       Tile[x][y] = EL_SPRING;
4566       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4567       break;
4568
4569     default:
4570       if (IS_CUSTOM_ELEMENT(element))
4571       {
4572         struct ElementInfo *ei = &element_info[element];
4573         int move_direction_initial = ei->move_direction_initial;
4574         int move_pattern = ei->move_pattern;
4575
4576         if (move_direction_initial == MV_START_PREVIOUS)
4577         {
4578           if (MovDir[x][y] != MV_NONE)
4579             return;
4580
4581           move_direction_initial = MV_START_AUTOMATIC;
4582         }
4583
4584         if (move_direction_initial == MV_START_RANDOM)
4585           MovDir[x][y] = 1 << RND(4);
4586         else if (move_direction_initial & MV_ANY_DIRECTION)
4587           MovDir[x][y] = move_direction_initial;
4588         else if (move_pattern == MV_ALL_DIRECTIONS ||
4589                  move_pattern == MV_TURNING_LEFT ||
4590                  move_pattern == MV_TURNING_RIGHT ||
4591                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4592                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4593                  move_pattern == MV_TURNING_RANDOM)
4594           MovDir[x][y] = 1 << RND(4);
4595         else if (move_pattern == MV_HORIZONTAL)
4596           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4597         else if (move_pattern == MV_VERTICAL)
4598           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4599         else if (move_pattern & MV_ANY_DIRECTION)
4600           MovDir[x][y] = element_info[element].move_pattern;
4601         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4602                  move_pattern == MV_ALONG_RIGHT_SIDE)
4603         {
4604           // use random direction as default start direction
4605           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4606             MovDir[x][y] = 1 << RND(4);
4607
4608           for (i = 0; i < NUM_DIRECTIONS; i++)
4609           {
4610             int x1 = x + xy[i][0];
4611             int y1 = y + xy[i][1];
4612
4613             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4614             {
4615               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4616                 MovDir[x][y] = direction[0][i];
4617               else
4618                 MovDir[x][y] = direction[1][i];
4619
4620               break;
4621             }
4622           }
4623         }                
4624       }
4625       else
4626       {
4627         MovDir[x][y] = 1 << RND(4);
4628
4629         if (element != EL_BUG &&
4630             element != EL_SPACESHIP &&
4631             element != EL_BD_BUTTERFLY &&
4632             element != EL_BD_FIREFLY)
4633           break;
4634
4635         for (i = 0; i < NUM_DIRECTIONS; i++)
4636         {
4637           int x1 = x + xy[i][0];
4638           int y1 = y + xy[i][1];
4639
4640           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4641           {
4642             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4643             {
4644               MovDir[x][y] = direction[0][i];
4645               break;
4646             }
4647             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4648                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4649             {
4650               MovDir[x][y] = direction[1][i];
4651               break;
4652             }
4653           }
4654         }
4655       }
4656       break;
4657   }
4658
4659   GfxDir[x][y] = MovDir[x][y];
4660 }
4661
4662 void InitAmoebaNr(int x, int y)
4663 {
4664   int i;
4665   int group_nr = AmoebaNeighbourNr(x, y);
4666
4667   if (group_nr == 0)
4668   {
4669     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4670     {
4671       if (AmoebaCnt[i] == 0)
4672       {
4673         group_nr = i;
4674         break;
4675       }
4676     }
4677   }
4678
4679   AmoebaNr[x][y] = group_nr;
4680   AmoebaCnt[group_nr]++;
4681   AmoebaCnt2[group_nr]++;
4682 }
4683
4684 static void LevelSolved(void)
4685 {
4686   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4687       game.players_still_needed > 0)
4688     return;
4689
4690   game.LevelSolved = TRUE;
4691   game.GameOver = TRUE;
4692
4693   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4694                       game_em.lev->score :
4695                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4696                       game_mm.score :
4697                       game.score);
4698   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4699                        MM_HEALTH(game_mm.laser_overload_value) :
4700                        game.health);
4701
4702   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4703   game.LevelSolved_CountingScore = game.score_final;
4704   game.LevelSolved_CountingHealth = game.health_final;
4705 }
4706
4707 void GameWon(void)
4708 {
4709   static int time_count_steps;
4710   static int time, time_final;
4711   static int score, score_final;
4712   static int health, health_final;
4713   static int game_over_delay_1 = 0;
4714   static int game_over_delay_2 = 0;
4715   static int game_over_delay_3 = 0;
4716   int game_over_delay_value_1 = 50;
4717   int game_over_delay_value_2 = 25;
4718   int game_over_delay_value_3 = 50;
4719
4720   if (!game.LevelSolved_GameWon)
4721   {
4722     int i;
4723
4724     // do not start end game actions before the player stops moving (to exit)
4725     if (local_player->active && local_player->MovPos)
4726       return;
4727
4728     game.LevelSolved_GameWon = TRUE;
4729     game.LevelSolved_SaveTape = tape.recording;
4730     game.LevelSolved_SaveScore = !tape.playing;
4731
4732     if (!tape.playing)
4733     {
4734       LevelStats_incSolved(level_nr);
4735
4736       SaveLevelSetup_SeriesInfo();
4737     }
4738
4739     if (tape.auto_play)         // tape might already be stopped here
4740       tape.auto_play_level_solved = TRUE;
4741
4742     TapeStop();
4743
4744     game_over_delay_1 = 0;
4745     game_over_delay_2 = 0;
4746     game_over_delay_3 = game_over_delay_value_3;
4747
4748     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4749     score = score_final = game.score_final;
4750     health = health_final = game.health_final;
4751
4752     if (level.score[SC_TIME_BONUS] > 0)
4753     {
4754       if (TimeLeft > 0)
4755       {
4756         time_final = 0;
4757         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4758       }
4759       else if (game.no_time_limit && TimePlayed < 999)
4760       {
4761         time_final = 999;
4762         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4763       }
4764
4765       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4766
4767       game_over_delay_1 = game_over_delay_value_1;
4768
4769       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4770       {
4771         health_final = 0;
4772         score_final += health * level.score[SC_TIME_BONUS];
4773
4774         game_over_delay_2 = game_over_delay_value_2;
4775       }
4776
4777       game.score_final = score_final;
4778       game.health_final = health_final;
4779     }
4780
4781     if (level_editor_test_game)
4782     {
4783       time = time_final;
4784       score = score_final;
4785
4786       game.LevelSolved_CountingTime = time;
4787       game.LevelSolved_CountingScore = score;
4788
4789       game_panel_controls[GAME_PANEL_TIME].value = time;
4790       game_panel_controls[GAME_PANEL_SCORE].value = score;
4791
4792       DisplayGameControlValues();
4793     }
4794
4795     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4796     {
4797       // check if last player has left the level
4798       if (game.exit_x >= 0 &&
4799           game.exit_y >= 0)
4800       {
4801         int x = game.exit_x;
4802         int y = game.exit_y;
4803         int element = Tile[x][y];
4804
4805         // close exit door after last player
4806         if ((game.all_players_gone &&
4807              (element == EL_EXIT_OPEN ||
4808               element == EL_SP_EXIT_OPEN ||
4809               element == EL_STEEL_EXIT_OPEN)) ||
4810             element == EL_EM_EXIT_OPEN ||
4811             element == EL_EM_STEEL_EXIT_OPEN)
4812         {
4813
4814           Tile[x][y] =
4815             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4816              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4817              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4818              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4819              EL_EM_STEEL_EXIT_CLOSING);
4820
4821           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4822         }
4823
4824         // player disappears
4825         DrawLevelField(x, y);
4826       }
4827
4828       for (i = 0; i < MAX_PLAYERS; i++)
4829       {
4830         struct PlayerInfo *player = &stored_player[i];
4831
4832         if (player->present)
4833         {
4834           RemovePlayer(player);
4835
4836           // player disappears
4837           DrawLevelField(player->jx, player->jy);
4838         }
4839       }
4840     }
4841
4842     PlaySound(SND_GAME_WINNING);
4843   }
4844
4845   if (game_over_delay_1 > 0)
4846   {
4847     game_over_delay_1--;
4848
4849     return;
4850   }
4851
4852   if (time != time_final)
4853   {
4854     int time_to_go = ABS(time_final - time);
4855     int time_count_dir = (time < time_final ? +1 : -1);
4856
4857     if (time_to_go < time_count_steps)
4858       time_count_steps = 1;
4859
4860     time  += time_count_steps * time_count_dir;
4861     score += time_count_steps * level.score[SC_TIME_BONUS];
4862
4863     game.LevelSolved_CountingTime = time;
4864     game.LevelSolved_CountingScore = score;
4865
4866     game_panel_controls[GAME_PANEL_TIME].value = time;
4867     game_panel_controls[GAME_PANEL_SCORE].value = score;
4868
4869     DisplayGameControlValues();
4870
4871     if (time == time_final)
4872       StopSound(SND_GAME_LEVELTIME_BONUS);
4873     else if (setup.sound_loops)
4874       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4875     else
4876       PlaySound(SND_GAME_LEVELTIME_BONUS);
4877
4878     return;
4879   }
4880
4881   if (game_over_delay_2 > 0)
4882   {
4883     game_over_delay_2--;
4884
4885     return;
4886   }
4887
4888   if (health != health_final)
4889   {
4890     int health_count_dir = (health < health_final ? +1 : -1);
4891
4892     health += health_count_dir;
4893     score  += level.score[SC_TIME_BONUS];
4894
4895     game.LevelSolved_CountingHealth = health;
4896     game.LevelSolved_CountingScore = score;
4897
4898     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4899     game_panel_controls[GAME_PANEL_SCORE].value = score;
4900
4901     DisplayGameControlValues();
4902
4903     if (health == health_final)
4904       StopSound(SND_GAME_LEVELTIME_BONUS);
4905     else if (setup.sound_loops)
4906       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4907     else
4908       PlaySound(SND_GAME_LEVELTIME_BONUS);
4909
4910     return;
4911   }
4912
4913   game.panel.active = FALSE;
4914
4915   if (game_over_delay_3 > 0)
4916   {
4917     game_over_delay_3--;
4918
4919     return;
4920   }
4921
4922   GameEnd();
4923 }
4924
4925 void GameEnd(void)
4926 {
4927   // used instead of "level_nr" (needed for network games)
4928   int last_level_nr = levelset.level_nr;
4929   int hi_pos;
4930
4931   game.LevelSolved_GameEnd = TRUE;
4932
4933   if (game.LevelSolved_SaveTape)
4934   {
4935     // make sure that request dialog to save tape does not open door again
4936     if (!global.use_envelope_request)
4937       CloseDoor(DOOR_CLOSE_1);
4938
4939     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4940   }
4941
4942   // if no tape is to be saved, close both doors simultaneously
4943   CloseDoor(DOOR_CLOSE_ALL);
4944
4945   if (level_editor_test_game)
4946   {
4947     SetGameStatus(GAME_MODE_MAIN);
4948
4949     DrawMainMenu();
4950
4951     return;
4952   }
4953
4954   if (!game.LevelSolved_SaveScore)
4955   {
4956     SetGameStatus(GAME_MODE_MAIN);
4957
4958     DrawMainMenu();
4959
4960     return;
4961   }
4962
4963   if (level_nr == leveldir_current->handicap_level)
4964   {
4965     leveldir_current->handicap_level++;
4966
4967     SaveLevelSetup_SeriesInfo();
4968   }
4969
4970   if (setup.increment_levels &&
4971       level_nr < leveldir_current->last_level &&
4972       !network_playing)
4973   {
4974     level_nr++;         // advance to next level
4975     TapeErase();        // start with empty tape
4976
4977     if (setup.auto_play_next_level)
4978     {
4979       LoadLevel(level_nr);
4980
4981       SaveLevelSetup_SeriesInfo();
4982     }
4983   }
4984
4985   hi_pos = NewHiScore(last_level_nr);
4986
4987   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4988   {
4989     SetGameStatus(GAME_MODE_SCORES);
4990
4991     DrawHallOfFame(last_level_nr, hi_pos);
4992   }
4993   else if (setup.auto_play_next_level && setup.increment_levels &&
4994            last_level_nr < leveldir_current->last_level &&
4995            !network_playing)
4996   {
4997     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4998   }
4999   else
5000   {
5001     SetGameStatus(GAME_MODE_MAIN);
5002
5003     DrawMainMenu();
5004   }
5005 }
5006
5007 int NewHiScore(int level_nr)
5008 {
5009   int k, l;
5010   int position = -1;
5011   boolean one_score_entry_per_name = !program.many_scores_per_name;
5012
5013   LoadScore(level_nr);
5014
5015   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5016       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5017     return -1;
5018
5019   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5020   {
5021     if (game.score_final > highscore[k].Score)
5022     {
5023       // player has made it to the hall of fame
5024
5025       if (k < MAX_SCORE_ENTRIES - 1)
5026       {
5027         int m = MAX_SCORE_ENTRIES - 1;
5028
5029         if (one_score_entry_per_name)
5030         {
5031           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5032             if (strEqual(setup.player_name, highscore[l].Name))
5033               m = l;
5034
5035           if (m == k)   // player's new highscore overwrites his old one
5036             goto put_into_list;
5037         }
5038
5039         for (l = m; l > k; l--)
5040         {
5041           strcpy(highscore[l].Name, highscore[l - 1].Name);
5042           highscore[l].Score = highscore[l - 1].Score;
5043         }
5044       }
5045
5046       put_into_list:
5047
5048       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5049       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5050       highscore[k].Score = game.score_final;
5051       position = k;
5052
5053       break;
5054     }
5055     else if (one_score_entry_per_name &&
5056              !strncmp(setup.player_name, highscore[k].Name,
5057                       MAX_PLAYER_NAME_LEN))
5058       break;    // player already there with a higher score
5059   }
5060
5061   if (position >= 0) 
5062     SaveScore(level_nr);
5063
5064   return position;
5065 }
5066
5067 static int getElementMoveStepsizeExt(int x, int y, int direction)
5068 {
5069   int element = Tile[x][y];
5070   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5071   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5072   int horiz_move = (dx != 0);
5073   int sign = (horiz_move ? dx : dy);
5074   int step = sign * element_info[element].move_stepsize;
5075
5076   // special values for move stepsize for spring and things on conveyor belt
5077   if (horiz_move)
5078   {
5079     if (CAN_FALL(element) &&
5080         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5081       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5082     else if (element == EL_SPRING)
5083       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5084   }
5085
5086   return step;
5087 }
5088
5089 static int getElementMoveStepsize(int x, int y)
5090 {
5091   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5092 }
5093
5094 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5095 {
5096   if (player->GfxAction != action || player->GfxDir != dir)
5097   {
5098     player->GfxAction = action;
5099     player->GfxDir = dir;
5100     player->Frame = 0;
5101     player->StepFrame = 0;
5102   }
5103 }
5104
5105 static void ResetGfxFrame(int x, int y)
5106 {
5107   // profiling showed that "autotest" spends 10~20% of its time in this function
5108   if (DrawingDeactivatedField())
5109     return;
5110
5111   int element = Tile[x][y];
5112   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5113
5114   if (graphic_info[graphic].anim_global_sync)
5115     GfxFrame[x][y] = FrameCounter;
5116   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5117     GfxFrame[x][y] = CustomValue[x][y];
5118   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5119     GfxFrame[x][y] = element_info[element].collect_score;
5120   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5121     GfxFrame[x][y] = ChangeDelay[x][y];
5122 }
5123
5124 static void ResetGfxAnimation(int x, int y)
5125 {
5126   GfxAction[x][y] = ACTION_DEFAULT;
5127   GfxDir[x][y] = MovDir[x][y];
5128   GfxFrame[x][y] = 0;
5129
5130   ResetGfxFrame(x, y);
5131 }
5132
5133 static void ResetRandomAnimationValue(int x, int y)
5134 {
5135   GfxRandom[x][y] = INIT_GFX_RANDOM();
5136 }
5137
5138 static void InitMovingField(int x, int y, int direction)
5139 {
5140   int element = Tile[x][y];
5141   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5142   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5143   int newx = x + dx;
5144   int newy = y + dy;
5145   boolean is_moving_before, is_moving_after;
5146
5147   // check if element was/is moving or being moved before/after mode change
5148   is_moving_before = (WasJustMoving[x][y] != 0);
5149   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5150
5151   // reset animation only for moving elements which change direction of moving
5152   // or which just started or stopped moving
5153   // (else CEs with property "can move" / "not moving" are reset each frame)
5154   if (is_moving_before != is_moving_after ||
5155       direction != MovDir[x][y])
5156     ResetGfxAnimation(x, y);
5157
5158   MovDir[x][y] = direction;
5159   GfxDir[x][y] = direction;
5160
5161   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5162                      direction == MV_DOWN && CAN_FALL(element) ?
5163                      ACTION_FALLING : ACTION_MOVING);
5164
5165   // this is needed for CEs with property "can move" / "not moving"
5166
5167   if (is_moving_after)
5168   {
5169     if (Tile[newx][newy] == EL_EMPTY)
5170       Tile[newx][newy] = EL_BLOCKED;
5171
5172     MovDir[newx][newy] = MovDir[x][y];
5173
5174     CustomValue[newx][newy] = CustomValue[x][y];
5175
5176     GfxFrame[newx][newy] = GfxFrame[x][y];
5177     GfxRandom[newx][newy] = GfxRandom[x][y];
5178     GfxAction[newx][newy] = GfxAction[x][y];
5179     GfxDir[newx][newy] = GfxDir[x][y];
5180   }
5181 }
5182
5183 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5184 {
5185   int direction = MovDir[x][y];
5186   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5187   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5188
5189   *goes_to_x = newx;
5190   *goes_to_y = newy;
5191 }
5192
5193 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5194 {
5195   int oldx = x, oldy = y;
5196   int direction = MovDir[x][y];
5197
5198   if (direction == MV_LEFT)
5199     oldx++;
5200   else if (direction == MV_RIGHT)
5201     oldx--;
5202   else if (direction == MV_UP)
5203     oldy++;
5204   else if (direction == MV_DOWN)
5205     oldy--;
5206
5207   *comes_from_x = oldx;
5208   *comes_from_y = oldy;
5209 }
5210
5211 static int MovingOrBlocked2Element(int x, int y)
5212 {
5213   int element = Tile[x][y];
5214
5215   if (element == EL_BLOCKED)
5216   {
5217     int oldx, oldy;
5218
5219     Blocked2Moving(x, y, &oldx, &oldy);
5220     return Tile[oldx][oldy];
5221   }
5222   else
5223     return element;
5224 }
5225
5226 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5227 {
5228   // like MovingOrBlocked2Element(), but if element is moving
5229   // and (x,y) is the field the moving element is just leaving,
5230   // return EL_BLOCKED instead of the element value
5231   int element = Tile[x][y];
5232
5233   if (IS_MOVING(x, y))
5234   {
5235     if (element == EL_BLOCKED)
5236     {
5237       int oldx, oldy;
5238
5239       Blocked2Moving(x, y, &oldx, &oldy);
5240       return Tile[oldx][oldy];
5241     }
5242     else
5243       return EL_BLOCKED;
5244   }
5245   else
5246     return element;
5247 }
5248
5249 static void RemoveField(int x, int y)
5250 {
5251   Tile[x][y] = EL_EMPTY;
5252
5253   MovPos[x][y] = 0;
5254   MovDir[x][y] = 0;
5255   MovDelay[x][y] = 0;
5256
5257   CustomValue[x][y] = 0;
5258
5259   AmoebaNr[x][y] = 0;
5260   ChangeDelay[x][y] = 0;
5261   ChangePage[x][y] = -1;
5262   Pushed[x][y] = FALSE;
5263
5264   GfxElement[x][y] = EL_UNDEFINED;
5265   GfxAction[x][y] = ACTION_DEFAULT;
5266   GfxDir[x][y] = MV_NONE;
5267 }
5268
5269 static void RemoveMovingField(int x, int y)
5270 {
5271   int oldx = x, oldy = y, newx = x, newy = y;
5272   int element = Tile[x][y];
5273   int next_element = EL_UNDEFINED;
5274
5275   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5276     return;
5277
5278   if (IS_MOVING(x, y))
5279   {
5280     Moving2Blocked(x, y, &newx, &newy);
5281
5282     if (Tile[newx][newy] != EL_BLOCKED)
5283     {
5284       // element is moving, but target field is not free (blocked), but
5285       // already occupied by something different (example: acid pool);
5286       // in this case, only remove the moving field, but not the target
5287
5288       RemoveField(oldx, oldy);
5289
5290       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5291
5292       TEST_DrawLevelField(oldx, oldy);
5293
5294       return;
5295     }
5296   }
5297   else if (element == EL_BLOCKED)
5298   {
5299     Blocked2Moving(x, y, &oldx, &oldy);
5300     if (!IS_MOVING(oldx, oldy))
5301       return;
5302   }
5303
5304   if (element == EL_BLOCKED &&
5305       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5306        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5307        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5308        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5309        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5310        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5311     next_element = get_next_element(Tile[oldx][oldy]);
5312
5313   RemoveField(oldx, oldy);
5314   RemoveField(newx, newy);
5315
5316   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5317
5318   if (next_element != EL_UNDEFINED)
5319     Tile[oldx][oldy] = next_element;
5320
5321   TEST_DrawLevelField(oldx, oldy);
5322   TEST_DrawLevelField(newx, newy);
5323 }
5324
5325 void DrawDynamite(int x, int y)
5326 {
5327   int sx = SCREENX(x), sy = SCREENY(y);
5328   int graphic = el2img(Tile[x][y]);
5329   int frame;
5330
5331   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5332     return;
5333
5334   if (IS_WALKABLE_INSIDE(Back[x][y]))
5335     return;
5336
5337   if (Back[x][y])
5338     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5339   else if (Store[x][y])
5340     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5341
5342   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5343
5344   if (Back[x][y] || Store[x][y])
5345     DrawGraphicThruMask(sx, sy, graphic, frame);
5346   else
5347     DrawGraphic(sx, sy, graphic, frame);
5348 }
5349
5350 static void CheckDynamite(int x, int y)
5351 {
5352   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5353   {
5354     MovDelay[x][y]--;
5355
5356     if (MovDelay[x][y] != 0)
5357     {
5358       DrawDynamite(x, y);
5359       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5360
5361       return;
5362     }
5363   }
5364
5365   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5366
5367   Bang(x, y);
5368 }
5369
5370 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5371 {
5372   boolean num_checked_players = 0;
5373   int i;
5374
5375   for (i = 0; i < MAX_PLAYERS; i++)
5376   {
5377     if (stored_player[i].active)
5378     {
5379       int sx = stored_player[i].jx;
5380       int sy = stored_player[i].jy;
5381
5382       if (num_checked_players == 0)
5383       {
5384         *sx1 = *sx2 = sx;
5385         *sy1 = *sy2 = sy;
5386       }
5387       else
5388       {
5389         *sx1 = MIN(*sx1, sx);
5390         *sy1 = MIN(*sy1, sy);
5391         *sx2 = MAX(*sx2, sx);
5392         *sy2 = MAX(*sy2, sy);
5393       }
5394
5395       num_checked_players++;
5396     }
5397   }
5398 }
5399
5400 static boolean checkIfAllPlayersFitToScreen_RND(void)
5401 {
5402   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5403
5404   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5405
5406   return (sx2 - sx1 < SCR_FIELDX &&
5407           sy2 - sy1 < SCR_FIELDY);
5408 }
5409
5410 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5411 {
5412   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5413
5414   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5415
5416   *sx = (sx1 + sx2) / 2;
5417   *sy = (sy1 + sy2) / 2;
5418 }
5419
5420 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5421                                boolean center_screen, boolean quick_relocation)
5422 {
5423   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5424   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5425   boolean no_delay = (tape.warp_forward);
5426   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5427   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5428   int new_scroll_x, new_scroll_y;
5429
5430   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5431   {
5432     // case 1: quick relocation inside visible screen (without scrolling)
5433
5434     RedrawPlayfield();
5435
5436     return;
5437   }
5438
5439   if (!level.shifted_relocation || center_screen)
5440   {
5441     // relocation _with_ centering of screen
5442
5443     new_scroll_x = SCROLL_POSITION_X(x);
5444     new_scroll_y = SCROLL_POSITION_Y(y);
5445   }
5446   else
5447   {
5448     // relocation _without_ centering of screen
5449
5450     int center_scroll_x = SCROLL_POSITION_X(old_x);
5451     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5452     int offset_x = x + (scroll_x - center_scroll_x);
5453     int offset_y = y + (scroll_y - center_scroll_y);
5454
5455     // for new screen position, apply previous offset to center position
5456     new_scroll_x = SCROLL_POSITION_X(offset_x);
5457     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5458   }
5459
5460   if (quick_relocation)
5461   {
5462     // case 2: quick relocation (redraw without visible scrolling)
5463
5464     scroll_x = new_scroll_x;
5465     scroll_y = new_scroll_y;
5466
5467     RedrawPlayfield();
5468
5469     return;
5470   }
5471
5472   // case 3: visible relocation (with scrolling to new position)
5473
5474   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5475
5476   SetVideoFrameDelay(wait_delay_value);
5477
5478   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5479   {
5480     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5481     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5482
5483     if (dx == 0 && dy == 0)             // no scrolling needed at all
5484       break;
5485
5486     scroll_x -= dx;
5487     scroll_y -= dy;
5488
5489     // set values for horizontal/vertical screen scrolling (half tile size)
5490     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5491     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5492     int pos_x = dx * TILEX / 2;
5493     int pos_y = dy * TILEY / 2;
5494     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5495     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5496
5497     ScrollLevel(dx, dy);
5498     DrawAllPlayers();
5499
5500     // scroll in two steps of half tile size to make things smoother
5501     BlitScreenToBitmapExt_RND(window, fx, fy);
5502
5503     // scroll second step to align at full tile size
5504     BlitScreenToBitmap(window);
5505   }
5506
5507   DrawAllPlayers();
5508   BackToFront();
5509
5510   SetVideoFrameDelay(frame_delay_value_old);
5511 }
5512
5513 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5514 {
5515   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5516   int player_nr = GET_PLAYER_NR(el_player);
5517   struct PlayerInfo *player = &stored_player[player_nr];
5518   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5519   boolean no_delay = (tape.warp_forward);
5520   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5521   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5522   int old_jx = player->jx;
5523   int old_jy = player->jy;
5524   int old_element = Tile[old_jx][old_jy];
5525   int element = Tile[jx][jy];
5526   boolean player_relocated = (old_jx != jx || old_jy != jy);
5527
5528   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5529   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5530   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5531   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5532   int leave_side_horiz = move_dir_horiz;
5533   int leave_side_vert  = move_dir_vert;
5534   int enter_side = enter_side_horiz | enter_side_vert;
5535   int leave_side = leave_side_horiz | leave_side_vert;
5536
5537   if (player->buried)           // do not reanimate dead player
5538     return;
5539
5540   if (!player_relocated)        // no need to relocate the player
5541     return;
5542
5543   if (IS_PLAYER(jx, jy))        // player already placed at new position
5544   {
5545     RemoveField(jx, jy);        // temporarily remove newly placed player
5546     DrawLevelField(jx, jy);
5547   }
5548
5549   if (player->present)
5550   {
5551     while (player->MovPos)
5552     {
5553       ScrollPlayer(player, SCROLL_GO_ON);
5554       ScrollScreen(NULL, SCROLL_GO_ON);
5555
5556       AdvanceFrameAndPlayerCounters(player->index_nr);
5557
5558       DrawPlayer(player);
5559
5560       BackToFront_WithFrameDelay(wait_delay_value);
5561     }
5562
5563     DrawPlayer(player);         // needed here only to cleanup last field
5564     DrawLevelField(player->jx, player->jy);     // remove player graphic
5565
5566     player->is_moving = FALSE;
5567   }
5568
5569   if (IS_CUSTOM_ELEMENT(old_element))
5570     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5571                                CE_LEFT_BY_PLAYER,
5572                                player->index_bit, leave_side);
5573
5574   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5575                                       CE_PLAYER_LEAVES_X,
5576                                       player->index_bit, leave_side);
5577
5578   Tile[jx][jy] = el_player;
5579   InitPlayerField(jx, jy, el_player, TRUE);
5580
5581   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5582      possible that the relocation target field did not contain a player element,
5583      but a walkable element, to which the new player was relocated -- in this
5584      case, restore that (already initialized!) element on the player field */
5585   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5586   {
5587     Tile[jx][jy] = element;     // restore previously existing element
5588   }
5589
5590   // only visually relocate centered player
5591   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5592                      FALSE, level.instant_relocation);
5593
5594   TestIfPlayerTouchesBadThing(jx, jy);
5595   TestIfPlayerTouchesCustomElement(jx, jy);
5596
5597   if (IS_CUSTOM_ELEMENT(element))
5598     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5599                                player->index_bit, enter_side);
5600
5601   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5602                                       player->index_bit, enter_side);
5603
5604   if (player->is_switching)
5605   {
5606     /* ensure that relocation while still switching an element does not cause
5607        a new element to be treated as also switched directly after relocation
5608        (this is important for teleporter switches that teleport the player to
5609        a place where another teleporter switch is in the same direction, which
5610        would then incorrectly be treated as immediately switched before the
5611        direction key that caused the switch was released) */
5612
5613     player->switch_x += jx - old_jx;
5614     player->switch_y += jy - old_jy;
5615   }
5616 }
5617
5618 static void Explode(int ex, int ey, int phase, int mode)
5619 {
5620   int x, y;
5621   int last_phase;
5622   int border_element;
5623
5624   // !!! eliminate this variable !!!
5625   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5626
5627   if (game.explosions_delayed)
5628   {
5629     ExplodeField[ex][ey] = mode;
5630     return;
5631   }
5632
5633   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5634   {
5635     int center_element = Tile[ex][ey];
5636     int artwork_element, explosion_element;     // set these values later
5637
5638     // remove things displayed in background while burning dynamite
5639     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5640       Back[ex][ey] = 0;
5641
5642     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5643     {
5644       // put moving element to center field (and let it explode there)
5645       center_element = MovingOrBlocked2Element(ex, ey);
5646       RemoveMovingField(ex, ey);
5647       Tile[ex][ey] = center_element;
5648     }
5649
5650     // now "center_element" is finally determined -- set related values now
5651     artwork_element = center_element;           // for custom player artwork
5652     explosion_element = center_element;         // for custom player artwork
5653
5654     if (IS_PLAYER(ex, ey))
5655     {
5656       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5657
5658       artwork_element = stored_player[player_nr].artwork_element;
5659
5660       if (level.use_explosion_element[player_nr])
5661       {
5662         explosion_element = level.explosion_element[player_nr];
5663         artwork_element = explosion_element;
5664       }
5665     }
5666
5667     if (mode == EX_TYPE_NORMAL ||
5668         mode == EX_TYPE_CENTER ||
5669         mode == EX_TYPE_CROSS)
5670       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5671
5672     last_phase = element_info[explosion_element].explosion_delay + 1;
5673
5674     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5675     {
5676       int xx = x - ex + 1;
5677       int yy = y - ey + 1;
5678       int element;
5679
5680       if (!IN_LEV_FIELD(x, y) ||
5681           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5682           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5683         continue;
5684
5685       element = Tile[x][y];
5686
5687       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5688       {
5689         element = MovingOrBlocked2Element(x, y);
5690
5691         if (!IS_EXPLOSION_PROOF(element))
5692           RemoveMovingField(x, y);
5693       }
5694
5695       // indestructible elements can only explode in center (but not flames)
5696       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5697                                            mode == EX_TYPE_BORDER)) ||
5698           element == EL_FLAMES)
5699         continue;
5700
5701       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5702          behaviour, for example when touching a yamyam that explodes to rocks
5703          with active deadly shield, a rock is created under the player !!! */
5704       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5705 #if 0
5706       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5707           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5708            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5709 #else
5710       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5711 #endif
5712       {
5713         if (IS_ACTIVE_BOMB(element))
5714         {
5715           // re-activate things under the bomb like gate or penguin
5716           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5717           Back[x][y] = 0;
5718         }
5719
5720         continue;
5721       }
5722
5723       // save walkable background elements while explosion on same tile
5724       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5725           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5726         Back[x][y] = element;
5727
5728       // ignite explodable elements reached by other explosion
5729       if (element == EL_EXPLOSION)
5730         element = Store2[x][y];
5731
5732       if (AmoebaNr[x][y] &&
5733           (element == EL_AMOEBA_FULL ||
5734            element == EL_BD_AMOEBA ||
5735            element == EL_AMOEBA_GROWING))
5736       {
5737         AmoebaCnt[AmoebaNr[x][y]]--;
5738         AmoebaCnt2[AmoebaNr[x][y]]--;
5739       }
5740
5741       RemoveField(x, y);
5742
5743       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5744       {
5745         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5746
5747         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5748
5749         if (PLAYERINFO(ex, ey)->use_murphy)
5750           Store[x][y] = EL_EMPTY;
5751       }
5752
5753       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5754       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5755       else if (ELEM_IS_PLAYER(center_element))
5756         Store[x][y] = EL_EMPTY;
5757       else if (center_element == EL_YAMYAM)
5758         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5759       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5760         Store[x][y] = element_info[center_element].content.e[xx][yy];
5761 #if 1
5762       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5763       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5764       // otherwise) -- FIX THIS !!!
5765       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5766         Store[x][y] = element_info[element].content.e[1][1];
5767 #else
5768       else if (!CAN_EXPLODE(element))
5769         Store[x][y] = element_info[element].content.e[1][1];
5770 #endif
5771       else
5772         Store[x][y] = EL_EMPTY;
5773
5774       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5775           center_element == EL_AMOEBA_TO_DIAMOND)
5776         Store2[x][y] = element;
5777
5778       Tile[x][y] = EL_EXPLOSION;
5779       GfxElement[x][y] = artwork_element;
5780
5781       ExplodePhase[x][y] = 1;
5782       ExplodeDelay[x][y] = last_phase;
5783
5784       Stop[x][y] = TRUE;
5785     }
5786
5787     if (center_element == EL_YAMYAM)
5788       game.yamyam_content_nr =
5789         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5790
5791     return;
5792   }
5793
5794   if (Stop[ex][ey])
5795     return;
5796
5797   x = ex;
5798   y = ey;
5799
5800   if (phase == 1)
5801     GfxFrame[x][y] = 0;         // restart explosion animation
5802
5803   last_phase = ExplodeDelay[x][y];
5804
5805   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5806
5807   // this can happen if the player leaves an explosion just in time
5808   if (GfxElement[x][y] == EL_UNDEFINED)
5809     GfxElement[x][y] = EL_EMPTY;
5810
5811   border_element = Store2[x][y];
5812   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5813     border_element = StorePlayer[x][y];
5814
5815   if (phase == element_info[border_element].ignition_delay ||
5816       phase == last_phase)
5817   {
5818     boolean border_explosion = FALSE;
5819
5820     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5821         !PLAYER_EXPLOSION_PROTECTED(x, y))
5822     {
5823       KillPlayerUnlessExplosionProtected(x, y);
5824       border_explosion = TRUE;
5825     }
5826     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5827     {
5828       Tile[x][y] = Store2[x][y];
5829       Store2[x][y] = 0;
5830       Bang(x, y);
5831       border_explosion = TRUE;
5832     }
5833     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5834     {
5835       AmoebaToDiamond(x, y);
5836       Store2[x][y] = 0;
5837       border_explosion = TRUE;
5838     }
5839
5840     // if an element just explodes due to another explosion (chain-reaction),
5841     // do not immediately end the new explosion when it was the last frame of
5842     // the explosion (as it would be done in the following "if"-statement!)
5843     if (border_explosion && phase == last_phase)
5844       return;
5845   }
5846
5847   if (phase == last_phase)
5848   {
5849     int element;
5850
5851     element = Tile[x][y] = Store[x][y];
5852     Store[x][y] = Store2[x][y] = 0;
5853     GfxElement[x][y] = EL_UNDEFINED;
5854
5855     // player can escape from explosions and might therefore be still alive
5856     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5857         element <= EL_PLAYER_IS_EXPLODING_4)
5858     {
5859       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5860       int explosion_element = EL_PLAYER_1 + player_nr;
5861       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5862       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5863
5864       if (level.use_explosion_element[player_nr])
5865         explosion_element = level.explosion_element[player_nr];
5866
5867       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5868                     element_info[explosion_element].content.e[xx][yy]);
5869     }
5870
5871     // restore probably existing indestructible background element
5872     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5873       element = Tile[x][y] = Back[x][y];
5874     Back[x][y] = 0;
5875
5876     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5877     GfxDir[x][y] = MV_NONE;
5878     ChangeDelay[x][y] = 0;
5879     ChangePage[x][y] = -1;
5880
5881     CustomValue[x][y] = 0;
5882
5883     InitField_WithBug2(x, y, FALSE);
5884
5885     TEST_DrawLevelField(x, y);
5886
5887     TestIfElementTouchesCustomElement(x, y);
5888
5889     if (GFX_CRUMBLED(element))
5890       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5891
5892     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5893       StorePlayer[x][y] = 0;
5894
5895     if (ELEM_IS_PLAYER(element))
5896       RelocatePlayer(x, y, element);
5897   }
5898   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5899   {
5900     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5901     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5902
5903     if (phase == delay)
5904       TEST_DrawLevelFieldCrumbled(x, y);
5905
5906     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5907     {
5908       DrawLevelElement(x, y, Back[x][y]);
5909       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5910     }
5911     else if (IS_WALKABLE_UNDER(Back[x][y]))
5912     {
5913       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5914       DrawLevelElementThruMask(x, y, Back[x][y]);
5915     }
5916     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5917       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5918   }
5919 }
5920
5921 static void DynaExplode(int ex, int ey)
5922 {
5923   int i, j;
5924   int dynabomb_element = Tile[ex][ey];
5925   int dynabomb_size = 1;
5926   boolean dynabomb_xl = FALSE;
5927   struct PlayerInfo *player;
5928   static int xy[4][2] =
5929   {
5930     { 0, -1 },
5931     { -1, 0 },
5932     { +1, 0 },
5933     { 0, +1 }
5934   };
5935
5936   if (IS_ACTIVE_BOMB(dynabomb_element))
5937   {
5938     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5939     dynabomb_size = player->dynabomb_size;
5940     dynabomb_xl = player->dynabomb_xl;
5941     player->dynabombs_left++;
5942   }
5943
5944   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5945
5946   for (i = 0; i < NUM_DIRECTIONS; i++)
5947   {
5948     for (j = 1; j <= dynabomb_size; j++)
5949     {
5950       int x = ex + j * xy[i][0];
5951       int y = ey + j * xy[i][1];
5952       int element;
5953
5954       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5955         break;
5956
5957       element = Tile[x][y];
5958
5959       // do not restart explosions of fields with active bombs
5960       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5961         continue;
5962
5963       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5964
5965       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5966           !IS_DIGGABLE(element) && !dynabomb_xl)
5967         break;
5968     }
5969   }
5970 }
5971
5972 void Bang(int x, int y)
5973 {
5974   int element = MovingOrBlocked2Element(x, y);
5975   int explosion_type = EX_TYPE_NORMAL;
5976
5977   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5978   {
5979     struct PlayerInfo *player = PLAYERINFO(x, y);
5980
5981     element = Tile[x][y] = player->initial_element;
5982
5983     if (level.use_explosion_element[player->index_nr])
5984     {
5985       int explosion_element = level.explosion_element[player->index_nr];
5986
5987       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5988         explosion_type = EX_TYPE_CROSS;
5989       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5990         explosion_type = EX_TYPE_CENTER;
5991     }
5992   }
5993
5994   switch (element)
5995   {
5996     case EL_BUG:
5997     case EL_SPACESHIP:
5998     case EL_BD_BUTTERFLY:
5999     case EL_BD_FIREFLY:
6000     case EL_YAMYAM:
6001     case EL_DARK_YAMYAM:
6002     case EL_ROBOT:
6003     case EL_PACMAN:
6004     case EL_MOLE:
6005       RaiseScoreElement(element);
6006       break;
6007
6008     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6009     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6010     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6011     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6012     case EL_DYNABOMB_INCREASE_NUMBER:
6013     case EL_DYNABOMB_INCREASE_SIZE:
6014     case EL_DYNABOMB_INCREASE_POWER:
6015       explosion_type = EX_TYPE_DYNA;
6016       break;
6017
6018     case EL_DC_LANDMINE:
6019       explosion_type = EX_TYPE_CENTER;
6020       break;
6021
6022     case EL_PENGUIN:
6023     case EL_LAMP:
6024     case EL_LAMP_ACTIVE:
6025     case EL_AMOEBA_TO_DIAMOND:
6026       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6027         explosion_type = EX_TYPE_CENTER;
6028       break;
6029
6030     default:
6031       if (element_info[element].explosion_type == EXPLODES_CROSS)
6032         explosion_type = EX_TYPE_CROSS;
6033       else if (element_info[element].explosion_type == EXPLODES_1X1)
6034         explosion_type = EX_TYPE_CENTER;
6035       break;
6036   }
6037
6038   if (explosion_type == EX_TYPE_DYNA)
6039     DynaExplode(x, y);
6040   else
6041     Explode(x, y, EX_PHASE_START, explosion_type);
6042
6043   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6044 }
6045
6046 static void SplashAcid(int x, int y)
6047 {
6048   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6049       (!IN_LEV_FIELD(x - 1, y - 2) ||
6050        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6051     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6052
6053   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6054       (!IN_LEV_FIELD(x + 1, y - 2) ||
6055        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6056     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6057
6058   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6059 }
6060
6061 static void InitBeltMovement(void)
6062 {
6063   static int belt_base_element[4] =
6064   {
6065     EL_CONVEYOR_BELT_1_LEFT,
6066     EL_CONVEYOR_BELT_2_LEFT,
6067     EL_CONVEYOR_BELT_3_LEFT,
6068     EL_CONVEYOR_BELT_4_LEFT
6069   };
6070   static int belt_base_active_element[4] =
6071   {
6072     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6073     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6074     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6075     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6076   };
6077
6078   int x, y, i, j;
6079
6080   // set frame order for belt animation graphic according to belt direction
6081   for (i = 0; i < NUM_BELTS; i++)
6082   {
6083     int belt_nr = i;
6084
6085     for (j = 0; j < NUM_BELT_PARTS; j++)
6086     {
6087       int element = belt_base_active_element[belt_nr] + j;
6088       int graphic_1 = el2img(element);
6089       int graphic_2 = el2panelimg(element);
6090
6091       if (game.belt_dir[i] == MV_LEFT)
6092       {
6093         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6094         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6095       }
6096       else
6097       {
6098         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6099         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6100       }
6101     }
6102   }
6103
6104   SCAN_PLAYFIELD(x, y)
6105   {
6106     int element = Tile[x][y];
6107
6108     for (i = 0; i < NUM_BELTS; i++)
6109     {
6110       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6111       {
6112         int e_belt_nr = getBeltNrFromBeltElement(element);
6113         int belt_nr = i;
6114
6115         if (e_belt_nr == belt_nr)
6116         {
6117           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6118
6119           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6120         }
6121       }
6122     }
6123   }
6124 }
6125
6126 static void ToggleBeltSwitch(int x, int y)
6127 {
6128   static int belt_base_element[4] =
6129   {
6130     EL_CONVEYOR_BELT_1_LEFT,
6131     EL_CONVEYOR_BELT_2_LEFT,
6132     EL_CONVEYOR_BELT_3_LEFT,
6133     EL_CONVEYOR_BELT_4_LEFT
6134   };
6135   static int belt_base_active_element[4] =
6136   {
6137     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6138     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6139     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6140     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6141   };
6142   static int belt_base_switch_element[4] =
6143   {
6144     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6145     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6146     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6147     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6148   };
6149   static int belt_move_dir[4] =
6150   {
6151     MV_LEFT,
6152     MV_NONE,
6153     MV_RIGHT,
6154     MV_NONE,
6155   };
6156
6157   int element = Tile[x][y];
6158   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6159   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6160   int belt_dir = belt_move_dir[belt_dir_nr];
6161   int xx, yy, i;
6162
6163   if (!IS_BELT_SWITCH(element))
6164     return;
6165
6166   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6167   game.belt_dir[belt_nr] = belt_dir;
6168
6169   if (belt_dir_nr == 3)
6170     belt_dir_nr = 1;
6171
6172   // set frame order for belt animation graphic according to belt direction
6173   for (i = 0; i < NUM_BELT_PARTS; i++)
6174   {
6175     int element = belt_base_active_element[belt_nr] + i;
6176     int graphic_1 = el2img(element);
6177     int graphic_2 = el2panelimg(element);
6178
6179     if (belt_dir == MV_LEFT)
6180     {
6181       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6182       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6183     }
6184     else
6185     {
6186       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6187       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6188     }
6189   }
6190
6191   SCAN_PLAYFIELD(xx, yy)
6192   {
6193     int element = Tile[xx][yy];
6194
6195     if (IS_BELT_SWITCH(element))
6196     {
6197       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6198
6199       if (e_belt_nr == belt_nr)
6200       {
6201         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6202         TEST_DrawLevelField(xx, yy);
6203       }
6204     }
6205     else if (IS_BELT(element) && belt_dir != MV_NONE)
6206     {
6207       int e_belt_nr = getBeltNrFromBeltElement(element);
6208
6209       if (e_belt_nr == belt_nr)
6210       {
6211         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6212
6213         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6214         TEST_DrawLevelField(xx, yy);
6215       }
6216     }
6217     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6218     {
6219       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6220
6221       if (e_belt_nr == belt_nr)
6222       {
6223         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6224
6225         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6226         TEST_DrawLevelField(xx, yy);
6227       }
6228     }
6229   }
6230 }
6231
6232 static void ToggleSwitchgateSwitch(int x, int y)
6233 {
6234   int xx, yy;
6235
6236   game.switchgate_pos = !game.switchgate_pos;
6237
6238   SCAN_PLAYFIELD(xx, yy)
6239   {
6240     int element = Tile[xx][yy];
6241
6242     if (element == EL_SWITCHGATE_SWITCH_UP)
6243     {
6244       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6245       TEST_DrawLevelField(xx, yy);
6246     }
6247     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6248     {
6249       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6250       TEST_DrawLevelField(xx, yy);
6251     }
6252     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6253     {
6254       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6255       TEST_DrawLevelField(xx, yy);
6256     }
6257     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6258     {
6259       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6260       TEST_DrawLevelField(xx, yy);
6261     }
6262     else if (element == EL_SWITCHGATE_OPEN ||
6263              element == EL_SWITCHGATE_OPENING)
6264     {
6265       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6266
6267       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6268     }
6269     else if (element == EL_SWITCHGATE_CLOSED ||
6270              element == EL_SWITCHGATE_CLOSING)
6271     {
6272       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6273
6274       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6275     }
6276   }
6277 }
6278
6279 static int getInvisibleActiveFromInvisibleElement(int element)
6280 {
6281   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6282           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6283           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6284           element);
6285 }
6286
6287 static int getInvisibleFromInvisibleActiveElement(int element)
6288 {
6289   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6290           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6291           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6292           element);
6293 }
6294
6295 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6296 {
6297   int x, y;
6298
6299   SCAN_PLAYFIELD(x, y)
6300   {
6301     int element = Tile[x][y];
6302
6303     if (element == EL_LIGHT_SWITCH &&
6304         game.light_time_left > 0)
6305     {
6306       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6307       TEST_DrawLevelField(x, y);
6308     }
6309     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6310              game.light_time_left == 0)
6311     {
6312       Tile[x][y] = EL_LIGHT_SWITCH;
6313       TEST_DrawLevelField(x, y);
6314     }
6315     else if (element == EL_EMC_DRIPPER &&
6316              game.light_time_left > 0)
6317     {
6318       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6319       TEST_DrawLevelField(x, y);
6320     }
6321     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6322              game.light_time_left == 0)
6323     {
6324       Tile[x][y] = EL_EMC_DRIPPER;
6325       TEST_DrawLevelField(x, y);
6326     }
6327     else if (element == EL_INVISIBLE_STEELWALL ||
6328              element == EL_INVISIBLE_WALL ||
6329              element == EL_INVISIBLE_SAND)
6330     {
6331       if (game.light_time_left > 0)
6332         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6333
6334       TEST_DrawLevelField(x, y);
6335
6336       // uncrumble neighbour fields, if needed
6337       if (element == EL_INVISIBLE_SAND)
6338         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6339     }
6340     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6341              element == EL_INVISIBLE_WALL_ACTIVE ||
6342              element == EL_INVISIBLE_SAND_ACTIVE)
6343     {
6344       if (game.light_time_left == 0)
6345         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6346
6347       TEST_DrawLevelField(x, y);
6348
6349       // re-crumble neighbour fields, if needed
6350       if (element == EL_INVISIBLE_SAND)
6351         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6352     }
6353   }
6354 }
6355
6356 static void RedrawAllInvisibleElementsForLenses(void)
6357 {
6358   int x, y;
6359
6360   SCAN_PLAYFIELD(x, y)
6361   {
6362     int element = Tile[x][y];
6363
6364     if (element == EL_EMC_DRIPPER &&
6365         game.lenses_time_left > 0)
6366     {
6367       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6368       TEST_DrawLevelField(x, y);
6369     }
6370     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6371              game.lenses_time_left == 0)
6372     {
6373       Tile[x][y] = EL_EMC_DRIPPER;
6374       TEST_DrawLevelField(x, y);
6375     }
6376     else if (element == EL_INVISIBLE_STEELWALL ||
6377              element == EL_INVISIBLE_WALL ||
6378              element == EL_INVISIBLE_SAND)
6379     {
6380       if (game.lenses_time_left > 0)
6381         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6382
6383       TEST_DrawLevelField(x, y);
6384
6385       // uncrumble neighbour fields, if needed
6386       if (element == EL_INVISIBLE_SAND)
6387         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6388     }
6389     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6390              element == EL_INVISIBLE_WALL_ACTIVE ||
6391              element == EL_INVISIBLE_SAND_ACTIVE)
6392     {
6393       if (game.lenses_time_left == 0)
6394         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6395
6396       TEST_DrawLevelField(x, y);
6397
6398       // re-crumble neighbour fields, if needed
6399       if (element == EL_INVISIBLE_SAND)
6400         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6401     }
6402   }
6403 }
6404
6405 static void RedrawAllInvisibleElementsForMagnifier(void)
6406 {
6407   int x, y;
6408
6409   SCAN_PLAYFIELD(x, y)
6410   {
6411     int element = Tile[x][y];
6412
6413     if (element == EL_EMC_FAKE_GRASS &&
6414         game.magnify_time_left > 0)
6415     {
6416       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6417       TEST_DrawLevelField(x, y);
6418     }
6419     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6420              game.magnify_time_left == 0)
6421     {
6422       Tile[x][y] = EL_EMC_FAKE_GRASS;
6423       TEST_DrawLevelField(x, y);
6424     }
6425     else if (IS_GATE_GRAY(element) &&
6426              game.magnify_time_left > 0)
6427     {
6428       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6429                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6430                     IS_EM_GATE_GRAY(element) ?
6431                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6432                     IS_EMC_GATE_GRAY(element) ?
6433                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6434                     IS_DC_GATE_GRAY(element) ?
6435                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6436                     element);
6437       TEST_DrawLevelField(x, y);
6438     }
6439     else if (IS_GATE_GRAY_ACTIVE(element) &&
6440              game.magnify_time_left == 0)
6441     {
6442       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6443                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6444                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6445                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6446                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6447                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6448                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6449                     EL_DC_GATE_WHITE_GRAY :
6450                     element);
6451       TEST_DrawLevelField(x, y);
6452     }
6453   }
6454 }
6455
6456 static void ToggleLightSwitch(int x, int y)
6457 {
6458   int element = Tile[x][y];
6459
6460   game.light_time_left =
6461     (element == EL_LIGHT_SWITCH ?
6462      level.time_light * FRAMES_PER_SECOND : 0);
6463
6464   RedrawAllLightSwitchesAndInvisibleElements();
6465 }
6466
6467 static void ActivateTimegateSwitch(int x, int y)
6468 {
6469   int xx, yy;
6470
6471   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6472
6473   SCAN_PLAYFIELD(xx, yy)
6474   {
6475     int element = Tile[xx][yy];
6476
6477     if (element == EL_TIMEGATE_CLOSED ||
6478         element == EL_TIMEGATE_CLOSING)
6479     {
6480       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6481       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6482     }
6483
6484     /*
6485     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6486     {
6487       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6488       TEST_DrawLevelField(xx, yy);
6489     }
6490     */
6491
6492   }
6493
6494   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6495                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6496 }
6497
6498 static void Impact(int x, int y)
6499 {
6500   boolean last_line = (y == lev_fieldy - 1);
6501   boolean object_hit = FALSE;
6502   boolean impact = (last_line || object_hit);
6503   int element = Tile[x][y];
6504   int smashed = EL_STEELWALL;
6505
6506   if (!last_line)       // check if element below was hit
6507   {
6508     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6509       return;
6510
6511     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6512                                          MovDir[x][y + 1] != MV_DOWN ||
6513                                          MovPos[x][y + 1] <= TILEY / 2));
6514
6515     // do not smash moving elements that left the smashed field in time
6516     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6517         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6518       object_hit = FALSE;
6519
6520 #if USE_QUICKSAND_IMPACT_BUGFIX
6521     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6522     {
6523       RemoveMovingField(x, y + 1);
6524       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6525       Tile[x][y + 2] = EL_ROCK;
6526       TEST_DrawLevelField(x, y + 2);
6527
6528       object_hit = TRUE;
6529     }
6530
6531     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6532     {
6533       RemoveMovingField(x, y + 1);
6534       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6535       Tile[x][y + 2] = EL_ROCK;
6536       TEST_DrawLevelField(x, y + 2);
6537
6538       object_hit = TRUE;
6539     }
6540 #endif
6541
6542     if (object_hit)
6543       smashed = MovingOrBlocked2Element(x, y + 1);
6544
6545     impact = (last_line || object_hit);
6546   }
6547
6548   if (!last_line && smashed == EL_ACID) // element falls into acid
6549   {
6550     SplashAcid(x, y + 1);
6551     return;
6552   }
6553
6554   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6555   // only reset graphic animation if graphic really changes after impact
6556   if (impact &&
6557       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6558   {
6559     ResetGfxAnimation(x, y);
6560     TEST_DrawLevelField(x, y);
6561   }
6562
6563   if (impact && CAN_EXPLODE_IMPACT(element))
6564   {
6565     Bang(x, y);
6566     return;
6567   }
6568   else if (impact && element == EL_PEARL &&
6569            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6570   {
6571     ResetGfxAnimation(x, y);
6572
6573     Tile[x][y] = EL_PEARL_BREAKING;
6574     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6575     return;
6576   }
6577   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6578   {
6579     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6580
6581     return;
6582   }
6583
6584   if (impact && element == EL_AMOEBA_DROP)
6585   {
6586     if (object_hit && IS_PLAYER(x, y + 1))
6587       KillPlayerUnlessEnemyProtected(x, y + 1);
6588     else if (object_hit && smashed == EL_PENGUIN)
6589       Bang(x, y + 1);
6590     else
6591     {
6592       Tile[x][y] = EL_AMOEBA_GROWING;
6593       Store[x][y] = EL_AMOEBA_WET;
6594
6595       ResetRandomAnimationValue(x, y);
6596     }
6597     return;
6598   }
6599
6600   if (object_hit)               // check which object was hit
6601   {
6602     if ((CAN_PASS_MAGIC_WALL(element) && 
6603          (smashed == EL_MAGIC_WALL ||
6604           smashed == EL_BD_MAGIC_WALL)) ||
6605         (CAN_PASS_DC_MAGIC_WALL(element) &&
6606          smashed == EL_DC_MAGIC_WALL))
6607     {
6608       int xx, yy;
6609       int activated_magic_wall =
6610         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6611          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6612          EL_DC_MAGIC_WALL_ACTIVE);
6613
6614       // activate magic wall / mill
6615       SCAN_PLAYFIELD(xx, yy)
6616       {
6617         if (Tile[xx][yy] == smashed)
6618           Tile[xx][yy] = activated_magic_wall;
6619       }
6620
6621       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6622       game.magic_wall_active = TRUE;
6623
6624       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6625                             SND_MAGIC_WALL_ACTIVATING :
6626                             smashed == EL_BD_MAGIC_WALL ?
6627                             SND_BD_MAGIC_WALL_ACTIVATING :
6628                             SND_DC_MAGIC_WALL_ACTIVATING));
6629     }
6630
6631     if (IS_PLAYER(x, y + 1))
6632     {
6633       if (CAN_SMASH_PLAYER(element))
6634       {
6635         KillPlayerUnlessEnemyProtected(x, y + 1);
6636         return;
6637       }
6638     }
6639     else if (smashed == EL_PENGUIN)
6640     {
6641       if (CAN_SMASH_PLAYER(element))
6642       {
6643         Bang(x, y + 1);
6644         return;
6645       }
6646     }
6647     else if (element == EL_BD_DIAMOND)
6648     {
6649       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6650       {
6651         Bang(x, y + 1);
6652         return;
6653       }
6654     }
6655     else if (((element == EL_SP_INFOTRON ||
6656                element == EL_SP_ZONK) &&
6657               (smashed == EL_SP_SNIKSNAK ||
6658                smashed == EL_SP_ELECTRON ||
6659                smashed == EL_SP_DISK_ORANGE)) ||
6660              (element == EL_SP_INFOTRON &&
6661               smashed == EL_SP_DISK_YELLOW))
6662     {
6663       Bang(x, y + 1);
6664       return;
6665     }
6666     else if (CAN_SMASH_EVERYTHING(element))
6667     {
6668       if (IS_CLASSIC_ENEMY(smashed) ||
6669           CAN_EXPLODE_SMASHED(smashed))
6670       {
6671         Bang(x, y + 1);
6672         return;
6673       }
6674       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6675       {
6676         if (smashed == EL_LAMP ||
6677             smashed == EL_LAMP_ACTIVE)
6678         {
6679           Bang(x, y + 1);
6680           return;
6681         }
6682         else if (smashed == EL_NUT)
6683         {
6684           Tile[x][y + 1] = EL_NUT_BREAKING;
6685           PlayLevelSound(x, y, SND_NUT_BREAKING);
6686           RaiseScoreElement(EL_NUT);
6687           return;
6688         }
6689         else if (smashed == EL_PEARL)
6690         {
6691           ResetGfxAnimation(x, y);
6692
6693           Tile[x][y + 1] = EL_PEARL_BREAKING;
6694           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6695           return;
6696         }
6697         else if (smashed == EL_DIAMOND)
6698         {
6699           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6700           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6701           return;
6702         }
6703         else if (IS_BELT_SWITCH(smashed))
6704         {
6705           ToggleBeltSwitch(x, y + 1);
6706         }
6707         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6708                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6709                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6710                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6711         {
6712           ToggleSwitchgateSwitch(x, y + 1);
6713         }
6714         else if (smashed == EL_LIGHT_SWITCH ||
6715                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6716         {
6717           ToggleLightSwitch(x, y + 1);
6718         }
6719         else
6720         {
6721           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6722
6723           CheckElementChangeBySide(x, y + 1, smashed, element,
6724                                    CE_SWITCHED, CH_SIDE_TOP);
6725           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6726                                             CH_SIDE_TOP);
6727         }
6728       }
6729       else
6730       {
6731         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6732       }
6733     }
6734   }
6735
6736   // play sound of magic wall / mill
6737   if (!last_line &&
6738       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6739        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6740        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6741   {
6742     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6743       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6744     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6745       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6746     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6747       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6748
6749     return;
6750   }
6751
6752   // play sound of object that hits the ground
6753   if (last_line || object_hit)
6754     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6755 }
6756
6757 static void TurnRoundExt(int x, int y)
6758 {
6759   static struct
6760   {
6761     int dx, dy;
6762   } move_xy[] =
6763   {
6764     {  0,  0 },
6765     { -1,  0 },
6766     { +1,  0 },
6767     {  0,  0 },
6768     {  0, -1 },
6769     {  0,  0 }, { 0, 0 }, { 0, 0 },
6770     {  0, +1 }
6771   };
6772   static struct
6773   {
6774     int left, right, back;
6775   } turn[] =
6776   {
6777     { 0,        0,              0        },
6778     { MV_DOWN,  MV_UP,          MV_RIGHT },
6779     { MV_UP,    MV_DOWN,        MV_LEFT  },
6780     { 0,        0,              0        },
6781     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6782     { 0,        0,              0        },
6783     { 0,        0,              0        },
6784     { 0,        0,              0        },
6785     { MV_RIGHT, MV_LEFT,        MV_UP    }
6786   };
6787
6788   int element = Tile[x][y];
6789   int move_pattern = element_info[element].move_pattern;
6790
6791   int old_move_dir = MovDir[x][y];
6792   int left_dir  = turn[old_move_dir].left;
6793   int right_dir = turn[old_move_dir].right;
6794   int back_dir  = turn[old_move_dir].back;
6795
6796   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6797   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6798   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6799   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6800
6801   int left_x  = x + left_dx,  left_y  = y + left_dy;
6802   int right_x = x + right_dx, right_y = y + right_dy;
6803   int move_x  = x + move_dx,  move_y  = y + move_dy;
6804
6805   int xx, yy;
6806
6807   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6808   {
6809     TestIfBadThingTouchesOtherBadThing(x, y);
6810
6811     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6812       MovDir[x][y] = right_dir;
6813     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6814       MovDir[x][y] = left_dir;
6815
6816     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6817       MovDelay[x][y] = 9;
6818     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6819       MovDelay[x][y] = 1;
6820   }
6821   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6822   {
6823     TestIfBadThingTouchesOtherBadThing(x, y);
6824
6825     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6826       MovDir[x][y] = left_dir;
6827     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6828       MovDir[x][y] = right_dir;
6829
6830     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6831       MovDelay[x][y] = 9;
6832     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6833       MovDelay[x][y] = 1;
6834   }
6835   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6836   {
6837     TestIfBadThingTouchesOtherBadThing(x, y);
6838
6839     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6840       MovDir[x][y] = left_dir;
6841     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6842       MovDir[x][y] = right_dir;
6843
6844     if (MovDir[x][y] != old_move_dir)
6845       MovDelay[x][y] = 9;
6846   }
6847   else if (element == EL_YAMYAM)
6848   {
6849     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6850     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6851
6852     if (can_turn_left && can_turn_right)
6853       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6854     else if (can_turn_left)
6855       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6856     else if (can_turn_right)
6857       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6858     else
6859       MovDir[x][y] = back_dir;
6860
6861     MovDelay[x][y] = 16 + 16 * RND(3);
6862   }
6863   else if (element == EL_DARK_YAMYAM)
6864   {
6865     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6866                                                          left_x, left_y);
6867     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6868                                                          right_x, right_y);
6869
6870     if (can_turn_left && can_turn_right)
6871       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6872     else if (can_turn_left)
6873       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6874     else if (can_turn_right)
6875       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6876     else
6877       MovDir[x][y] = back_dir;
6878
6879     MovDelay[x][y] = 16 + 16 * RND(3);
6880   }
6881   else if (element == EL_PACMAN)
6882   {
6883     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6884     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6885
6886     if (can_turn_left && can_turn_right)
6887       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6888     else if (can_turn_left)
6889       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6890     else if (can_turn_right)
6891       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6892     else
6893       MovDir[x][y] = back_dir;
6894
6895     MovDelay[x][y] = 6 + RND(40);
6896   }
6897   else if (element == EL_PIG)
6898   {
6899     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6900     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6901     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6902     boolean should_turn_left, should_turn_right, should_move_on;
6903     int rnd_value = 24;
6904     int rnd = RND(rnd_value);
6905
6906     should_turn_left = (can_turn_left &&
6907                         (!can_move_on ||
6908                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6909                                                    y + back_dy + left_dy)));
6910     should_turn_right = (can_turn_right &&
6911                          (!can_move_on ||
6912                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6913                                                     y + back_dy + right_dy)));
6914     should_move_on = (can_move_on &&
6915                       (!can_turn_left ||
6916                        !can_turn_right ||
6917                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6918                                                  y + move_dy + left_dy) ||
6919                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6920                                                  y + move_dy + right_dy)));
6921
6922     if (should_turn_left || should_turn_right || should_move_on)
6923     {
6924       if (should_turn_left && should_turn_right && should_move_on)
6925         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6926                         rnd < 2 * rnd_value / 3 ? right_dir :
6927                         old_move_dir);
6928       else if (should_turn_left && should_turn_right)
6929         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6930       else if (should_turn_left && should_move_on)
6931         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6932       else if (should_turn_right && should_move_on)
6933         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6934       else if (should_turn_left)
6935         MovDir[x][y] = left_dir;
6936       else if (should_turn_right)
6937         MovDir[x][y] = right_dir;
6938       else if (should_move_on)
6939         MovDir[x][y] = old_move_dir;
6940     }
6941     else if (can_move_on && rnd > rnd_value / 8)
6942       MovDir[x][y] = old_move_dir;
6943     else if (can_turn_left && can_turn_right)
6944       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6945     else if (can_turn_left && rnd > rnd_value / 8)
6946       MovDir[x][y] = left_dir;
6947     else if (can_turn_right && rnd > rnd_value/8)
6948       MovDir[x][y] = right_dir;
6949     else
6950       MovDir[x][y] = back_dir;
6951
6952     xx = x + move_xy[MovDir[x][y]].dx;
6953     yy = y + move_xy[MovDir[x][y]].dy;
6954
6955     if (!IN_LEV_FIELD(xx, yy) ||
6956         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6957       MovDir[x][y] = old_move_dir;
6958
6959     MovDelay[x][y] = 0;
6960   }
6961   else if (element == EL_DRAGON)
6962   {
6963     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6964     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6965     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6966     int rnd_value = 24;
6967     int rnd = RND(rnd_value);
6968
6969     if (can_move_on && rnd > rnd_value / 8)
6970       MovDir[x][y] = old_move_dir;
6971     else if (can_turn_left && can_turn_right)
6972       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6973     else if (can_turn_left && rnd > rnd_value / 8)
6974       MovDir[x][y] = left_dir;
6975     else if (can_turn_right && rnd > rnd_value / 8)
6976       MovDir[x][y] = right_dir;
6977     else
6978       MovDir[x][y] = back_dir;
6979
6980     xx = x + move_xy[MovDir[x][y]].dx;
6981     yy = y + move_xy[MovDir[x][y]].dy;
6982
6983     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6984       MovDir[x][y] = old_move_dir;
6985
6986     MovDelay[x][y] = 0;
6987   }
6988   else if (element == EL_MOLE)
6989   {
6990     boolean can_move_on =
6991       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6992                             IS_AMOEBOID(Tile[move_x][move_y]) ||
6993                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
6994     if (!can_move_on)
6995     {
6996       boolean can_turn_left =
6997         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6998                               IS_AMOEBOID(Tile[left_x][left_y])));
6999
7000       boolean can_turn_right =
7001         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7002                               IS_AMOEBOID(Tile[right_x][right_y])));
7003
7004       if (can_turn_left && can_turn_right)
7005         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7006       else if (can_turn_left)
7007         MovDir[x][y] = left_dir;
7008       else
7009         MovDir[x][y] = right_dir;
7010     }
7011
7012     if (MovDir[x][y] != old_move_dir)
7013       MovDelay[x][y] = 9;
7014   }
7015   else if (element == EL_BALLOON)
7016   {
7017     MovDir[x][y] = game.wind_direction;
7018     MovDelay[x][y] = 0;
7019   }
7020   else if (element == EL_SPRING)
7021   {
7022     if (MovDir[x][y] & MV_HORIZONTAL)
7023     {
7024       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7025           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7026       {
7027         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7028         ResetGfxAnimation(move_x, move_y);
7029         TEST_DrawLevelField(move_x, move_y);
7030
7031         MovDir[x][y] = back_dir;
7032       }
7033       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7034                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7035         MovDir[x][y] = MV_NONE;
7036     }
7037
7038     MovDelay[x][y] = 0;
7039   }
7040   else if (element == EL_ROBOT ||
7041            element == EL_SATELLITE ||
7042            element == EL_PENGUIN ||
7043            element == EL_EMC_ANDROID)
7044   {
7045     int attr_x = -1, attr_y = -1;
7046
7047     if (game.all_players_gone)
7048     {
7049       attr_x = game.exit_x;
7050       attr_y = game.exit_y;
7051     }
7052     else
7053     {
7054       int i;
7055
7056       for (i = 0; i < MAX_PLAYERS; i++)
7057       {
7058         struct PlayerInfo *player = &stored_player[i];
7059         int jx = player->jx, jy = player->jy;
7060
7061         if (!player->active)
7062           continue;
7063
7064         if (attr_x == -1 ||
7065             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7066         {
7067           attr_x = jx;
7068           attr_y = jy;
7069         }
7070       }
7071     }
7072
7073     if (element == EL_ROBOT &&
7074         game.robot_wheel_x >= 0 &&
7075         game.robot_wheel_y >= 0 &&
7076         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7077          game.engine_version < VERSION_IDENT(3,1,0,0)))
7078     {
7079       attr_x = game.robot_wheel_x;
7080       attr_y = game.robot_wheel_y;
7081     }
7082
7083     if (element == EL_PENGUIN)
7084     {
7085       int i;
7086       static int xy[4][2] =
7087       {
7088         { 0, -1 },
7089         { -1, 0 },
7090         { +1, 0 },
7091         { 0, +1 }
7092       };
7093
7094       for (i = 0; i < NUM_DIRECTIONS; i++)
7095       {
7096         int ex = x + xy[i][0];
7097         int ey = y + xy[i][1];
7098
7099         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7100                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7101                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7102                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7103         {
7104           attr_x = ex;
7105           attr_y = ey;
7106           break;
7107         }
7108       }
7109     }
7110
7111     MovDir[x][y] = MV_NONE;
7112     if (attr_x < x)
7113       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7114     else if (attr_x > x)
7115       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7116     if (attr_y < y)
7117       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7118     else if (attr_y > y)
7119       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7120
7121     if (element == EL_ROBOT)
7122     {
7123       int newx, newy;
7124
7125       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7126         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7127       Moving2Blocked(x, y, &newx, &newy);
7128
7129       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7130         MovDelay[x][y] = 8 + 8 * !RND(3);
7131       else
7132         MovDelay[x][y] = 16;
7133     }
7134     else if (element == EL_PENGUIN)
7135     {
7136       int newx, newy;
7137
7138       MovDelay[x][y] = 1;
7139
7140       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7141       {
7142         boolean first_horiz = RND(2);
7143         int new_move_dir = MovDir[x][y];
7144
7145         MovDir[x][y] =
7146           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7147         Moving2Blocked(x, y, &newx, &newy);
7148
7149         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7150           return;
7151
7152         MovDir[x][y] =
7153           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7154         Moving2Blocked(x, y, &newx, &newy);
7155
7156         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7157           return;
7158
7159         MovDir[x][y] = old_move_dir;
7160         return;
7161       }
7162     }
7163     else if (element == EL_SATELLITE)
7164     {
7165       int newx, newy;
7166
7167       MovDelay[x][y] = 1;
7168
7169       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7170       {
7171         boolean first_horiz = RND(2);
7172         int new_move_dir = MovDir[x][y];
7173
7174         MovDir[x][y] =
7175           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7176         Moving2Blocked(x, y, &newx, &newy);
7177
7178         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7179           return;
7180
7181         MovDir[x][y] =
7182           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7183         Moving2Blocked(x, y, &newx, &newy);
7184
7185         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7186           return;
7187
7188         MovDir[x][y] = old_move_dir;
7189         return;
7190       }
7191     }
7192     else if (element == EL_EMC_ANDROID)
7193     {
7194       static int check_pos[16] =
7195       {
7196         -1,             //  0 => (invalid)
7197         7,              //  1 => MV_LEFT
7198         3,              //  2 => MV_RIGHT
7199         -1,             //  3 => (invalid)
7200         1,              //  4 =>            MV_UP
7201         0,              //  5 => MV_LEFT  | MV_UP
7202         2,              //  6 => MV_RIGHT | MV_UP
7203         -1,             //  7 => (invalid)
7204         5,              //  8 =>            MV_DOWN
7205         6,              //  9 => MV_LEFT  | MV_DOWN
7206         4,              // 10 => MV_RIGHT | MV_DOWN
7207         -1,             // 11 => (invalid)
7208         -1,             // 12 => (invalid)
7209         -1,             // 13 => (invalid)
7210         -1,             // 14 => (invalid)
7211         -1,             // 15 => (invalid)
7212       };
7213       static struct
7214       {
7215         int dx, dy;
7216         int dir;
7217       } check_xy[8] =
7218       {
7219         { -1, -1,       MV_LEFT  | MV_UP   },
7220         {  0, -1,                  MV_UP   },
7221         { +1, -1,       MV_RIGHT | MV_UP   },
7222         { +1,  0,       MV_RIGHT           },
7223         { +1, +1,       MV_RIGHT | MV_DOWN },
7224         {  0, +1,                  MV_DOWN },
7225         { -1, +1,       MV_LEFT  | MV_DOWN },
7226         { -1,  0,       MV_LEFT            },
7227       };
7228       int start_pos, check_order;
7229       boolean can_clone = FALSE;
7230       int i;
7231
7232       // check if there is any free field around current position
7233       for (i = 0; i < 8; i++)
7234       {
7235         int newx = x + check_xy[i].dx;
7236         int newy = y + check_xy[i].dy;
7237
7238         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7239         {
7240           can_clone = TRUE;
7241
7242           break;
7243         }
7244       }
7245
7246       if (can_clone)            // randomly find an element to clone
7247       {
7248         can_clone = FALSE;
7249
7250         start_pos = check_pos[RND(8)];
7251         check_order = (RND(2) ? -1 : +1);
7252
7253         for (i = 0; i < 8; i++)
7254         {
7255           int pos_raw = start_pos + i * check_order;
7256           int pos = (pos_raw + 8) % 8;
7257           int newx = x + check_xy[pos].dx;
7258           int newy = y + check_xy[pos].dy;
7259
7260           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7261           {
7262             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7263             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7264
7265             Store[x][y] = Tile[newx][newy];
7266
7267             can_clone = TRUE;
7268
7269             break;
7270           }
7271         }
7272       }
7273
7274       if (can_clone)            // randomly find a direction to move
7275       {
7276         can_clone = FALSE;
7277
7278         start_pos = check_pos[RND(8)];
7279         check_order = (RND(2) ? -1 : +1);
7280
7281         for (i = 0; i < 8; i++)
7282         {
7283           int pos_raw = start_pos + i * check_order;
7284           int pos = (pos_raw + 8) % 8;
7285           int newx = x + check_xy[pos].dx;
7286           int newy = y + check_xy[pos].dy;
7287           int new_move_dir = check_xy[pos].dir;
7288
7289           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7290           {
7291             MovDir[x][y] = new_move_dir;
7292             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7293
7294             can_clone = TRUE;
7295
7296             break;
7297           }
7298         }
7299       }
7300
7301       if (can_clone)            // cloning and moving successful
7302         return;
7303
7304       // cannot clone -- try to move towards player
7305
7306       start_pos = check_pos[MovDir[x][y] & 0x0f];
7307       check_order = (RND(2) ? -1 : +1);
7308
7309       for (i = 0; i < 3; i++)
7310       {
7311         // first check start_pos, then previous/next or (next/previous) pos
7312         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7313         int pos = (pos_raw + 8) % 8;
7314         int newx = x + check_xy[pos].dx;
7315         int newy = y + check_xy[pos].dy;
7316         int new_move_dir = check_xy[pos].dir;
7317
7318         if (IS_PLAYER(newx, newy))
7319           break;
7320
7321         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7322         {
7323           MovDir[x][y] = new_move_dir;
7324           MovDelay[x][y] = level.android_move_time * 8 + 1;
7325
7326           break;
7327         }
7328       }
7329     }
7330   }
7331   else if (move_pattern == MV_TURNING_LEFT ||
7332            move_pattern == MV_TURNING_RIGHT ||
7333            move_pattern == MV_TURNING_LEFT_RIGHT ||
7334            move_pattern == MV_TURNING_RIGHT_LEFT ||
7335            move_pattern == MV_TURNING_RANDOM ||
7336            move_pattern == MV_ALL_DIRECTIONS)
7337   {
7338     boolean can_turn_left =
7339       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7340     boolean can_turn_right =
7341       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7342
7343     if (element_info[element].move_stepsize == 0)       // "not moving"
7344       return;
7345
7346     if (move_pattern == MV_TURNING_LEFT)
7347       MovDir[x][y] = left_dir;
7348     else if (move_pattern == MV_TURNING_RIGHT)
7349       MovDir[x][y] = right_dir;
7350     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7351       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7352     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7353       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7354     else if (move_pattern == MV_TURNING_RANDOM)
7355       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7356                       can_turn_right && !can_turn_left ? right_dir :
7357                       RND(2) ? left_dir : right_dir);
7358     else if (can_turn_left && can_turn_right)
7359       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7360     else if (can_turn_left)
7361       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7362     else if (can_turn_right)
7363       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7364     else
7365       MovDir[x][y] = back_dir;
7366
7367     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7368   }
7369   else if (move_pattern == MV_HORIZONTAL ||
7370            move_pattern == MV_VERTICAL)
7371   {
7372     if (move_pattern & old_move_dir)
7373       MovDir[x][y] = back_dir;
7374     else if (move_pattern == MV_HORIZONTAL)
7375       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7376     else if (move_pattern == MV_VERTICAL)
7377       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7378
7379     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7380   }
7381   else if (move_pattern & MV_ANY_DIRECTION)
7382   {
7383     MovDir[x][y] = move_pattern;
7384     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7385   }
7386   else if (move_pattern & MV_WIND_DIRECTION)
7387   {
7388     MovDir[x][y] = game.wind_direction;
7389     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7390   }
7391   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7392   {
7393     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7394       MovDir[x][y] = left_dir;
7395     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7396       MovDir[x][y] = right_dir;
7397
7398     if (MovDir[x][y] != old_move_dir)
7399       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7400   }
7401   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7402   {
7403     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7404       MovDir[x][y] = right_dir;
7405     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7406       MovDir[x][y] = left_dir;
7407
7408     if (MovDir[x][y] != old_move_dir)
7409       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7410   }
7411   else if (move_pattern == MV_TOWARDS_PLAYER ||
7412            move_pattern == MV_AWAY_FROM_PLAYER)
7413   {
7414     int attr_x = -1, attr_y = -1;
7415     int newx, newy;
7416     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7417
7418     if (game.all_players_gone)
7419     {
7420       attr_x = game.exit_x;
7421       attr_y = game.exit_y;
7422     }
7423     else
7424     {
7425       int i;
7426
7427       for (i = 0; i < MAX_PLAYERS; i++)
7428       {
7429         struct PlayerInfo *player = &stored_player[i];
7430         int jx = player->jx, jy = player->jy;
7431
7432         if (!player->active)
7433           continue;
7434
7435         if (attr_x == -1 ||
7436             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7437         {
7438           attr_x = jx;
7439           attr_y = jy;
7440         }
7441       }
7442     }
7443
7444     MovDir[x][y] = MV_NONE;
7445     if (attr_x < x)
7446       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7447     else if (attr_x > x)
7448       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7449     if (attr_y < y)
7450       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7451     else if (attr_y > y)
7452       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7453
7454     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7455
7456     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7457     {
7458       boolean first_horiz = RND(2);
7459       int new_move_dir = MovDir[x][y];
7460
7461       if (element_info[element].move_stepsize == 0)     // "not moving"
7462       {
7463         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7464         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7465
7466         return;
7467       }
7468
7469       MovDir[x][y] =
7470         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7471       Moving2Blocked(x, y, &newx, &newy);
7472
7473       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7474         return;
7475
7476       MovDir[x][y] =
7477         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7478       Moving2Blocked(x, y, &newx, &newy);
7479
7480       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7481         return;
7482
7483       MovDir[x][y] = old_move_dir;
7484     }
7485   }
7486   else if (move_pattern == MV_WHEN_PUSHED ||
7487            move_pattern == MV_WHEN_DROPPED)
7488   {
7489     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7490       MovDir[x][y] = MV_NONE;
7491
7492     MovDelay[x][y] = 0;
7493   }
7494   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7495   {
7496     static int test_xy[7][2] =
7497     {
7498       { 0, -1 },
7499       { -1, 0 },
7500       { +1, 0 },
7501       { 0, +1 },
7502       { 0, -1 },
7503       { -1, 0 },
7504       { +1, 0 },
7505     };
7506     static int test_dir[7] =
7507     {
7508       MV_UP,
7509       MV_LEFT,
7510       MV_RIGHT,
7511       MV_DOWN,
7512       MV_UP,
7513       MV_LEFT,
7514       MV_RIGHT,
7515     };
7516     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7517     int move_preference = -1000000;     // start with very low preference
7518     int new_move_dir = MV_NONE;
7519     int start_test = RND(4);
7520     int i;
7521
7522     for (i = 0; i < NUM_DIRECTIONS; i++)
7523     {
7524       int move_dir = test_dir[start_test + i];
7525       int move_dir_preference;
7526
7527       xx = x + test_xy[start_test + i][0];
7528       yy = y + test_xy[start_test + i][1];
7529
7530       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7531           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7532       {
7533         new_move_dir = move_dir;
7534
7535         break;
7536       }
7537
7538       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7539         continue;
7540
7541       move_dir_preference = -1 * RunnerVisit[xx][yy];
7542       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7543         move_dir_preference = PlayerVisit[xx][yy];
7544
7545       if (move_dir_preference > move_preference)
7546       {
7547         // prefer field that has not been visited for the longest time
7548         move_preference = move_dir_preference;
7549         new_move_dir = move_dir;
7550       }
7551       else if (move_dir_preference == move_preference &&
7552                move_dir == old_move_dir)
7553       {
7554         // prefer last direction when all directions are preferred equally
7555         move_preference = move_dir_preference;
7556         new_move_dir = move_dir;
7557       }
7558     }
7559
7560     MovDir[x][y] = new_move_dir;
7561     if (old_move_dir != new_move_dir)
7562       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7563   }
7564 }
7565
7566 static void TurnRound(int x, int y)
7567 {
7568   int direction = MovDir[x][y];
7569
7570   TurnRoundExt(x, y);
7571
7572   GfxDir[x][y] = MovDir[x][y];
7573
7574   if (direction != MovDir[x][y])
7575     GfxFrame[x][y] = 0;
7576
7577   if (MovDelay[x][y])
7578     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7579
7580   ResetGfxFrame(x, y);
7581 }
7582
7583 static boolean JustBeingPushed(int x, int y)
7584 {
7585   int i;
7586
7587   for (i = 0; i < MAX_PLAYERS; i++)
7588   {
7589     struct PlayerInfo *player = &stored_player[i];
7590
7591     if (player->active && player->is_pushing && player->MovPos)
7592     {
7593       int next_jx = player->jx + (player->jx - player->last_jx);
7594       int next_jy = player->jy + (player->jy - player->last_jy);
7595
7596       if (x == next_jx && y == next_jy)
7597         return TRUE;
7598     }
7599   }
7600
7601   return FALSE;
7602 }
7603
7604 static void StartMoving(int x, int y)
7605 {
7606   boolean started_moving = FALSE;       // some elements can fall _and_ move
7607   int element = Tile[x][y];
7608
7609   if (Stop[x][y])
7610     return;
7611
7612   if (MovDelay[x][y] == 0)
7613     GfxAction[x][y] = ACTION_DEFAULT;
7614
7615   if (CAN_FALL(element) && y < lev_fieldy - 1)
7616   {
7617     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7618         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7619       if (JustBeingPushed(x, y))
7620         return;
7621
7622     if (element == EL_QUICKSAND_FULL)
7623     {
7624       if (IS_FREE(x, y + 1))
7625       {
7626         InitMovingField(x, y, MV_DOWN);
7627         started_moving = TRUE;
7628
7629         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7630 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7631         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7632           Store[x][y] = EL_ROCK;
7633 #else
7634         Store[x][y] = EL_ROCK;
7635 #endif
7636
7637         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7638       }
7639       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7640       {
7641         if (!MovDelay[x][y])
7642         {
7643           MovDelay[x][y] = TILEY + 1;
7644
7645           ResetGfxAnimation(x, y);
7646           ResetGfxAnimation(x, y + 1);
7647         }
7648
7649         if (MovDelay[x][y])
7650         {
7651           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7652           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7653
7654           MovDelay[x][y]--;
7655           if (MovDelay[x][y])
7656             return;
7657         }
7658
7659         Tile[x][y] = EL_QUICKSAND_EMPTY;
7660         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7661         Store[x][y + 1] = Store[x][y];
7662         Store[x][y] = 0;
7663
7664         PlayLevelSoundAction(x, y, ACTION_FILLING);
7665       }
7666       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7667       {
7668         if (!MovDelay[x][y])
7669         {
7670           MovDelay[x][y] = TILEY + 1;
7671
7672           ResetGfxAnimation(x, y);
7673           ResetGfxAnimation(x, y + 1);
7674         }
7675
7676         if (MovDelay[x][y])
7677         {
7678           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7679           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7680
7681           MovDelay[x][y]--;
7682           if (MovDelay[x][y])
7683             return;
7684         }
7685
7686         Tile[x][y] = EL_QUICKSAND_EMPTY;
7687         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7688         Store[x][y + 1] = Store[x][y];
7689         Store[x][y] = 0;
7690
7691         PlayLevelSoundAction(x, y, ACTION_FILLING);
7692       }
7693     }
7694     else if (element == EL_QUICKSAND_FAST_FULL)
7695     {
7696       if (IS_FREE(x, y + 1))
7697       {
7698         InitMovingField(x, y, MV_DOWN);
7699         started_moving = TRUE;
7700
7701         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7702 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7703         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7704           Store[x][y] = EL_ROCK;
7705 #else
7706         Store[x][y] = EL_ROCK;
7707 #endif
7708
7709         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7710       }
7711       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7712       {
7713         if (!MovDelay[x][y])
7714         {
7715           MovDelay[x][y] = TILEY + 1;
7716
7717           ResetGfxAnimation(x, y);
7718           ResetGfxAnimation(x, y + 1);
7719         }
7720
7721         if (MovDelay[x][y])
7722         {
7723           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7724           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7725
7726           MovDelay[x][y]--;
7727           if (MovDelay[x][y])
7728             return;
7729         }
7730
7731         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7732         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7733         Store[x][y + 1] = Store[x][y];
7734         Store[x][y] = 0;
7735
7736         PlayLevelSoundAction(x, y, ACTION_FILLING);
7737       }
7738       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7739       {
7740         if (!MovDelay[x][y])
7741         {
7742           MovDelay[x][y] = TILEY + 1;
7743
7744           ResetGfxAnimation(x, y);
7745           ResetGfxAnimation(x, y + 1);
7746         }
7747
7748         if (MovDelay[x][y])
7749         {
7750           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7751           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7752
7753           MovDelay[x][y]--;
7754           if (MovDelay[x][y])
7755             return;
7756         }
7757
7758         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7759         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7760         Store[x][y + 1] = Store[x][y];
7761         Store[x][y] = 0;
7762
7763         PlayLevelSoundAction(x, y, ACTION_FILLING);
7764       }
7765     }
7766     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7767              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7768     {
7769       InitMovingField(x, y, MV_DOWN);
7770       started_moving = TRUE;
7771
7772       Tile[x][y] = EL_QUICKSAND_FILLING;
7773       Store[x][y] = element;
7774
7775       PlayLevelSoundAction(x, y, ACTION_FILLING);
7776     }
7777     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7778              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7779     {
7780       InitMovingField(x, y, MV_DOWN);
7781       started_moving = TRUE;
7782
7783       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7784       Store[x][y] = element;
7785
7786       PlayLevelSoundAction(x, y, ACTION_FILLING);
7787     }
7788     else if (element == EL_MAGIC_WALL_FULL)
7789     {
7790       if (IS_FREE(x, y + 1))
7791       {
7792         InitMovingField(x, y, MV_DOWN);
7793         started_moving = TRUE;
7794
7795         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7796         Store[x][y] = EL_CHANGED(Store[x][y]);
7797       }
7798       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7799       {
7800         if (!MovDelay[x][y])
7801           MovDelay[x][y] = TILEY / 4 + 1;
7802
7803         if (MovDelay[x][y])
7804         {
7805           MovDelay[x][y]--;
7806           if (MovDelay[x][y])
7807             return;
7808         }
7809
7810         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7811         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7812         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7813         Store[x][y] = 0;
7814       }
7815     }
7816     else if (element == EL_BD_MAGIC_WALL_FULL)
7817     {
7818       if (IS_FREE(x, y + 1))
7819       {
7820         InitMovingField(x, y, MV_DOWN);
7821         started_moving = TRUE;
7822
7823         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7824         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7825       }
7826       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7827       {
7828         if (!MovDelay[x][y])
7829           MovDelay[x][y] = TILEY / 4 + 1;
7830
7831         if (MovDelay[x][y])
7832         {
7833           MovDelay[x][y]--;
7834           if (MovDelay[x][y])
7835             return;
7836         }
7837
7838         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7839         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7840         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7841         Store[x][y] = 0;
7842       }
7843     }
7844     else if (element == EL_DC_MAGIC_WALL_FULL)
7845     {
7846       if (IS_FREE(x, y + 1))
7847       {
7848         InitMovingField(x, y, MV_DOWN);
7849         started_moving = TRUE;
7850
7851         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7852         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7853       }
7854       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7855       {
7856         if (!MovDelay[x][y])
7857           MovDelay[x][y] = TILEY / 4 + 1;
7858
7859         if (MovDelay[x][y])
7860         {
7861           MovDelay[x][y]--;
7862           if (MovDelay[x][y])
7863             return;
7864         }
7865
7866         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7867         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7868         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7869         Store[x][y] = 0;
7870       }
7871     }
7872     else if ((CAN_PASS_MAGIC_WALL(element) &&
7873               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7874                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7875              (CAN_PASS_DC_MAGIC_WALL(element) &&
7876               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7877
7878     {
7879       InitMovingField(x, y, MV_DOWN);
7880       started_moving = TRUE;
7881
7882       Tile[x][y] =
7883         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7884          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7885          EL_DC_MAGIC_WALL_FILLING);
7886       Store[x][y] = element;
7887     }
7888     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7889     {
7890       SplashAcid(x, y + 1);
7891
7892       InitMovingField(x, y, MV_DOWN);
7893       started_moving = TRUE;
7894
7895       Store[x][y] = EL_ACID;
7896     }
7897     else if (
7898              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7899               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7900              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7901               CAN_FALL(element) && WasJustFalling[x][y] &&
7902               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7903
7904              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7905               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7906               (Tile[x][y + 1] == EL_BLOCKED)))
7907     {
7908       /* this is needed for a special case not covered by calling "Impact()"
7909          from "ContinueMoving()": if an element moves to a tile directly below
7910          another element which was just falling on that tile (which was empty
7911          in the previous frame), the falling element above would just stop
7912          instead of smashing the element below (in previous version, the above
7913          element was just checked for "moving" instead of "falling", resulting
7914          in incorrect smashes caused by horizontal movement of the above
7915          element; also, the case of the player being the element to smash was
7916          simply not covered here... :-/ ) */
7917
7918       CheckCollision[x][y] = 0;
7919       CheckImpact[x][y] = 0;
7920
7921       Impact(x, y);
7922     }
7923     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7924     {
7925       if (MovDir[x][y] == MV_NONE)
7926       {
7927         InitMovingField(x, y, MV_DOWN);
7928         started_moving = TRUE;
7929       }
7930     }
7931     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7932     {
7933       if (WasJustFalling[x][y]) // prevent animation from being restarted
7934         MovDir[x][y] = MV_DOWN;
7935
7936       InitMovingField(x, y, MV_DOWN);
7937       started_moving = TRUE;
7938     }
7939     else if (element == EL_AMOEBA_DROP)
7940     {
7941       Tile[x][y] = EL_AMOEBA_GROWING;
7942       Store[x][y] = EL_AMOEBA_WET;
7943     }
7944     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7945               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7946              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7947              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7948     {
7949       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7950                                 (IS_FREE(x - 1, y + 1) ||
7951                                  Tile[x - 1][y + 1] == EL_ACID));
7952       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7953                                 (IS_FREE(x + 1, y + 1) ||
7954                                  Tile[x + 1][y + 1] == EL_ACID));
7955       boolean can_fall_any  = (can_fall_left || can_fall_right);
7956       boolean can_fall_both = (can_fall_left && can_fall_right);
7957       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7958
7959       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7960       {
7961         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7962           can_fall_right = FALSE;
7963         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7964           can_fall_left = FALSE;
7965         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7966           can_fall_right = FALSE;
7967         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7968           can_fall_left = FALSE;
7969
7970         can_fall_any  = (can_fall_left || can_fall_right);
7971         can_fall_both = FALSE;
7972       }
7973
7974       if (can_fall_both)
7975       {
7976         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7977           can_fall_right = FALSE;       // slip down on left side
7978         else
7979           can_fall_left = !(can_fall_right = RND(2));
7980
7981         can_fall_both = FALSE;
7982       }
7983
7984       if (can_fall_any)
7985       {
7986         // if not determined otherwise, prefer left side for slipping down
7987         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7988         started_moving = TRUE;
7989       }
7990     }
7991     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
7992     {
7993       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7994       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7995       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
7996       int belt_dir = game.belt_dir[belt_nr];
7997
7998       if ((belt_dir == MV_LEFT  && left_is_free) ||
7999           (belt_dir == MV_RIGHT && right_is_free))
8000       {
8001         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8002
8003         InitMovingField(x, y, belt_dir);
8004         started_moving = TRUE;
8005
8006         Pushed[x][y] = TRUE;
8007         Pushed[nextx][y] = TRUE;
8008
8009         GfxAction[x][y] = ACTION_DEFAULT;
8010       }
8011       else
8012       {
8013         MovDir[x][y] = 0;       // if element was moving, stop it
8014       }
8015     }
8016   }
8017
8018   // not "else if" because of elements that can fall and move (EL_SPRING)
8019   if (CAN_MOVE(element) && !started_moving)
8020   {
8021     int move_pattern = element_info[element].move_pattern;
8022     int newx, newy;
8023
8024     Moving2Blocked(x, y, &newx, &newy);
8025
8026     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8027       return;
8028
8029     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8030         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8031     {
8032       WasJustMoving[x][y] = 0;
8033       CheckCollision[x][y] = 0;
8034
8035       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8036
8037       if (Tile[x][y] != element)        // element has changed
8038         return;
8039     }
8040
8041     if (!MovDelay[x][y])        // start new movement phase
8042     {
8043       // all objects that can change their move direction after each step
8044       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8045
8046       if (element != EL_YAMYAM &&
8047           element != EL_DARK_YAMYAM &&
8048           element != EL_PACMAN &&
8049           !(move_pattern & MV_ANY_DIRECTION) &&
8050           move_pattern != MV_TURNING_LEFT &&
8051           move_pattern != MV_TURNING_RIGHT &&
8052           move_pattern != MV_TURNING_LEFT_RIGHT &&
8053           move_pattern != MV_TURNING_RIGHT_LEFT &&
8054           move_pattern != MV_TURNING_RANDOM)
8055       {
8056         TurnRound(x, y);
8057
8058         if (MovDelay[x][y] && (element == EL_BUG ||
8059                                element == EL_SPACESHIP ||
8060                                element == EL_SP_SNIKSNAK ||
8061                                element == EL_SP_ELECTRON ||
8062                                element == EL_MOLE))
8063           TEST_DrawLevelField(x, y);
8064       }
8065     }
8066
8067     if (MovDelay[x][y])         // wait some time before next movement
8068     {
8069       MovDelay[x][y]--;
8070
8071       if (element == EL_ROBOT ||
8072           element == EL_YAMYAM ||
8073           element == EL_DARK_YAMYAM)
8074       {
8075         DrawLevelElementAnimationIfNeeded(x, y, element);
8076         PlayLevelSoundAction(x, y, ACTION_WAITING);
8077       }
8078       else if (element == EL_SP_ELECTRON)
8079         DrawLevelElementAnimationIfNeeded(x, y, element);
8080       else if (element == EL_DRAGON)
8081       {
8082         int i;
8083         int dir = MovDir[x][y];
8084         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8085         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8086         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8087                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8088                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8089                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8090         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8091
8092         GfxAction[x][y] = ACTION_ATTACKING;
8093
8094         if (IS_PLAYER(x, y))
8095           DrawPlayerField(x, y);
8096         else
8097           TEST_DrawLevelField(x, y);
8098
8099         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8100
8101         for (i = 1; i <= 3; i++)
8102         {
8103           int xx = x + i * dx;
8104           int yy = y + i * dy;
8105           int sx = SCREENX(xx);
8106           int sy = SCREENY(yy);
8107           int flame_graphic = graphic + (i - 1);
8108
8109           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8110             break;
8111
8112           if (MovDelay[x][y])
8113           {
8114             int flamed = MovingOrBlocked2Element(xx, yy);
8115
8116             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8117               Bang(xx, yy);
8118             else
8119               RemoveMovingField(xx, yy);
8120
8121             ChangeDelay[xx][yy] = 0;
8122
8123             Tile[xx][yy] = EL_FLAMES;
8124
8125             if (IN_SCR_FIELD(sx, sy))
8126             {
8127               TEST_DrawLevelFieldCrumbled(xx, yy);
8128               DrawGraphic(sx, sy, flame_graphic, frame);
8129             }
8130           }
8131           else
8132           {
8133             if (Tile[xx][yy] == EL_FLAMES)
8134               Tile[xx][yy] = EL_EMPTY;
8135             TEST_DrawLevelField(xx, yy);
8136           }
8137         }
8138       }
8139
8140       if (MovDelay[x][y])       // element still has to wait some time
8141       {
8142         PlayLevelSoundAction(x, y, ACTION_WAITING);
8143
8144         return;
8145       }
8146     }
8147
8148     // now make next step
8149
8150     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8151
8152     if (DONT_COLLIDE_WITH(element) &&
8153         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8154         !PLAYER_ENEMY_PROTECTED(newx, newy))
8155     {
8156       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8157
8158       return;
8159     }
8160
8161     else if (CAN_MOVE_INTO_ACID(element) &&
8162              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8163              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8164              (MovDir[x][y] == MV_DOWN ||
8165               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8166     {
8167       SplashAcid(newx, newy);
8168       Store[x][y] = EL_ACID;
8169     }
8170     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8171     {
8172       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8173           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8174           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8175           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8176       {
8177         RemoveField(x, y);
8178         TEST_DrawLevelField(x, y);
8179
8180         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8181         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8182           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8183
8184         game.friends_still_needed--;
8185         if (!game.friends_still_needed &&
8186             !game.GameOver &&
8187             game.all_players_gone)
8188           LevelSolved();
8189
8190         return;
8191       }
8192       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8193       {
8194         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8195           TEST_DrawLevelField(newx, newy);
8196         else
8197           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8198       }
8199       else if (!IS_FREE(newx, newy))
8200       {
8201         GfxAction[x][y] = ACTION_WAITING;
8202
8203         if (IS_PLAYER(x, y))
8204           DrawPlayerField(x, y);
8205         else
8206           TEST_DrawLevelField(x, y);
8207
8208         return;
8209       }
8210     }
8211     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8212     {
8213       if (IS_FOOD_PIG(Tile[newx][newy]))
8214       {
8215         if (IS_MOVING(newx, newy))
8216           RemoveMovingField(newx, newy);
8217         else
8218         {
8219           Tile[newx][newy] = EL_EMPTY;
8220           TEST_DrawLevelField(newx, newy);
8221         }
8222
8223         PlayLevelSound(x, y, SND_PIG_DIGGING);
8224       }
8225       else if (!IS_FREE(newx, newy))
8226       {
8227         if (IS_PLAYER(x, y))
8228           DrawPlayerField(x, y);
8229         else
8230           TEST_DrawLevelField(x, y);
8231
8232         return;
8233       }
8234     }
8235     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8236     {
8237       if (Store[x][y] != EL_EMPTY)
8238       {
8239         boolean can_clone = FALSE;
8240         int xx, yy;
8241
8242         // check if element to clone is still there
8243         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8244         {
8245           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8246           {
8247             can_clone = TRUE;
8248
8249             break;
8250           }
8251         }
8252
8253         // cannot clone or target field not free anymore -- do not clone
8254         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8255           Store[x][y] = EL_EMPTY;
8256       }
8257
8258       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8259       {
8260         if (IS_MV_DIAGONAL(MovDir[x][y]))
8261         {
8262           int diagonal_move_dir = MovDir[x][y];
8263           int stored = Store[x][y];
8264           int change_delay = 8;
8265           int graphic;
8266
8267           // android is moving diagonally
8268
8269           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8270
8271           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8272           GfxElement[x][y] = EL_EMC_ANDROID;
8273           GfxAction[x][y] = ACTION_SHRINKING;
8274           GfxDir[x][y] = diagonal_move_dir;
8275           ChangeDelay[x][y] = change_delay;
8276
8277           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8278                                    GfxDir[x][y]);
8279
8280           DrawLevelGraphicAnimation(x, y, graphic);
8281           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8282
8283           if (Tile[newx][newy] == EL_ACID)
8284           {
8285             SplashAcid(newx, newy);
8286
8287             return;
8288           }
8289
8290           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8291
8292           Store[newx][newy] = EL_EMC_ANDROID;
8293           GfxElement[newx][newy] = EL_EMC_ANDROID;
8294           GfxAction[newx][newy] = ACTION_GROWING;
8295           GfxDir[newx][newy] = diagonal_move_dir;
8296           ChangeDelay[newx][newy] = change_delay;
8297
8298           graphic = el_act_dir2img(GfxElement[newx][newy],
8299                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8300
8301           DrawLevelGraphicAnimation(newx, newy, graphic);
8302           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8303
8304           return;
8305         }
8306         else
8307         {
8308           Tile[newx][newy] = EL_EMPTY;
8309           TEST_DrawLevelField(newx, newy);
8310
8311           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8312         }
8313       }
8314       else if (!IS_FREE(newx, newy))
8315       {
8316         return;
8317       }
8318     }
8319     else if (IS_CUSTOM_ELEMENT(element) &&
8320              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8321     {
8322       if (!DigFieldByCE(newx, newy, element))
8323         return;
8324
8325       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8326       {
8327         RunnerVisit[x][y] = FrameCounter;
8328         PlayerVisit[x][y] /= 8;         // expire player visit path
8329       }
8330     }
8331     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8332     {
8333       if (!IS_FREE(newx, newy))
8334       {
8335         if (IS_PLAYER(x, y))
8336           DrawPlayerField(x, y);
8337         else
8338           TEST_DrawLevelField(x, y);
8339
8340         return;
8341       }
8342       else
8343       {
8344         boolean wanna_flame = !RND(10);
8345         int dx = newx - x, dy = newy - y;
8346         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8347         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8348         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8349                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8350         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8351                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8352
8353         if ((wanna_flame ||
8354              IS_CLASSIC_ENEMY(element1) ||
8355              IS_CLASSIC_ENEMY(element2)) &&
8356             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8357             element1 != EL_FLAMES && element2 != EL_FLAMES)
8358         {
8359           ResetGfxAnimation(x, y);
8360           GfxAction[x][y] = ACTION_ATTACKING;
8361
8362           if (IS_PLAYER(x, y))
8363             DrawPlayerField(x, y);
8364           else
8365             TEST_DrawLevelField(x, y);
8366
8367           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8368
8369           MovDelay[x][y] = 50;
8370
8371           Tile[newx][newy] = EL_FLAMES;
8372           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8373             Tile[newx1][newy1] = EL_FLAMES;
8374           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8375             Tile[newx2][newy2] = EL_FLAMES;
8376
8377           return;
8378         }
8379       }
8380     }
8381     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8382              Tile[newx][newy] == EL_DIAMOND)
8383     {
8384       if (IS_MOVING(newx, newy))
8385         RemoveMovingField(newx, newy);
8386       else
8387       {
8388         Tile[newx][newy] = EL_EMPTY;
8389         TEST_DrawLevelField(newx, newy);
8390       }
8391
8392       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8393     }
8394     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8395              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8396     {
8397       if (AmoebaNr[newx][newy])
8398       {
8399         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8400         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8401             Tile[newx][newy] == EL_BD_AMOEBA)
8402           AmoebaCnt[AmoebaNr[newx][newy]]--;
8403       }
8404
8405       if (IS_MOVING(newx, newy))
8406       {
8407         RemoveMovingField(newx, newy);
8408       }
8409       else
8410       {
8411         Tile[newx][newy] = EL_EMPTY;
8412         TEST_DrawLevelField(newx, newy);
8413       }
8414
8415       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8416     }
8417     else if ((element == EL_PACMAN || element == EL_MOLE)
8418              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8419     {
8420       if (AmoebaNr[newx][newy])
8421       {
8422         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8423         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8424             Tile[newx][newy] == EL_BD_AMOEBA)
8425           AmoebaCnt[AmoebaNr[newx][newy]]--;
8426       }
8427
8428       if (element == EL_MOLE)
8429       {
8430         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8431         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8432
8433         ResetGfxAnimation(x, y);
8434         GfxAction[x][y] = ACTION_DIGGING;
8435         TEST_DrawLevelField(x, y);
8436
8437         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8438
8439         return;                         // wait for shrinking amoeba
8440       }
8441       else      // element == EL_PACMAN
8442       {
8443         Tile[newx][newy] = EL_EMPTY;
8444         TEST_DrawLevelField(newx, newy);
8445         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8446       }
8447     }
8448     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8449              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8450               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8451     {
8452       // wait for shrinking amoeba to completely disappear
8453       return;
8454     }
8455     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8456     {
8457       // object was running against a wall
8458
8459       TurnRound(x, y);
8460
8461       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8462         DrawLevelElementAnimation(x, y, element);
8463
8464       if (DONT_TOUCH(element))
8465         TestIfBadThingTouchesPlayer(x, y);
8466
8467       return;
8468     }
8469
8470     InitMovingField(x, y, MovDir[x][y]);
8471
8472     PlayLevelSoundAction(x, y, ACTION_MOVING);
8473   }
8474
8475   if (MovDir[x][y])
8476     ContinueMoving(x, y);
8477 }
8478
8479 void ContinueMoving(int x, int y)
8480 {
8481   int element = Tile[x][y];
8482   struct ElementInfo *ei = &element_info[element];
8483   int direction = MovDir[x][y];
8484   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8485   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8486   int newx = x + dx, newy = y + dy;
8487   int stored = Store[x][y];
8488   int stored_new = Store[newx][newy];
8489   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8490   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8491   boolean last_line = (newy == lev_fieldy - 1);
8492
8493   MovPos[x][y] += getElementMoveStepsize(x, y);
8494
8495   if (pushed_by_player) // special case: moving object pushed by player
8496     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8497
8498   if (ABS(MovPos[x][y]) < TILEX)
8499   {
8500     TEST_DrawLevelField(x, y);
8501
8502     return;     // element is still moving
8503   }
8504
8505   // element reached destination field
8506
8507   Tile[x][y] = EL_EMPTY;
8508   Tile[newx][newy] = element;
8509   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8510
8511   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8512   {
8513     element = Tile[newx][newy] = EL_ACID;
8514   }
8515   else if (element == EL_MOLE)
8516   {
8517     Tile[x][y] = EL_SAND;
8518
8519     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8520   }
8521   else if (element == EL_QUICKSAND_FILLING)
8522   {
8523     element = Tile[newx][newy] = get_next_element(element);
8524     Store[newx][newy] = Store[x][y];
8525   }
8526   else if (element == EL_QUICKSAND_EMPTYING)
8527   {
8528     Tile[x][y] = get_next_element(element);
8529     element = Tile[newx][newy] = Store[x][y];
8530   }
8531   else if (element == EL_QUICKSAND_FAST_FILLING)
8532   {
8533     element = Tile[newx][newy] = get_next_element(element);
8534     Store[newx][newy] = Store[x][y];
8535   }
8536   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8537   {
8538     Tile[x][y] = get_next_element(element);
8539     element = Tile[newx][newy] = Store[x][y];
8540   }
8541   else if (element == EL_MAGIC_WALL_FILLING)
8542   {
8543     element = Tile[newx][newy] = get_next_element(element);
8544     if (!game.magic_wall_active)
8545       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8546     Store[newx][newy] = Store[x][y];
8547   }
8548   else if (element == EL_MAGIC_WALL_EMPTYING)
8549   {
8550     Tile[x][y] = get_next_element(element);
8551     if (!game.magic_wall_active)
8552       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8553     element = Tile[newx][newy] = Store[x][y];
8554
8555     InitField(newx, newy, FALSE);
8556   }
8557   else if (element == EL_BD_MAGIC_WALL_FILLING)
8558   {
8559     element = Tile[newx][newy] = get_next_element(element);
8560     if (!game.magic_wall_active)
8561       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8562     Store[newx][newy] = Store[x][y];
8563   }
8564   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8565   {
8566     Tile[x][y] = get_next_element(element);
8567     if (!game.magic_wall_active)
8568       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8569     element = Tile[newx][newy] = Store[x][y];
8570
8571     InitField(newx, newy, FALSE);
8572   }
8573   else if (element == EL_DC_MAGIC_WALL_FILLING)
8574   {
8575     element = Tile[newx][newy] = get_next_element(element);
8576     if (!game.magic_wall_active)
8577       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8578     Store[newx][newy] = Store[x][y];
8579   }
8580   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8581   {
8582     Tile[x][y] = get_next_element(element);
8583     if (!game.magic_wall_active)
8584       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8585     element = Tile[newx][newy] = Store[x][y];
8586
8587     InitField(newx, newy, FALSE);
8588   }
8589   else if (element == EL_AMOEBA_DROPPING)
8590   {
8591     Tile[x][y] = get_next_element(element);
8592     element = Tile[newx][newy] = Store[x][y];
8593   }
8594   else if (element == EL_SOKOBAN_OBJECT)
8595   {
8596     if (Back[x][y])
8597       Tile[x][y] = Back[x][y];
8598
8599     if (Back[newx][newy])
8600       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8601
8602     Back[x][y] = Back[newx][newy] = 0;
8603   }
8604
8605   Store[x][y] = EL_EMPTY;
8606   MovPos[x][y] = 0;
8607   MovDir[x][y] = 0;
8608   MovDelay[x][y] = 0;
8609
8610   MovDelay[newx][newy] = 0;
8611
8612   if (CAN_CHANGE_OR_HAS_ACTION(element))
8613   {
8614     // copy element change control values to new field
8615     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8616     ChangePage[newx][newy]  = ChangePage[x][y];
8617     ChangeCount[newx][newy] = ChangeCount[x][y];
8618     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8619   }
8620
8621   CustomValue[newx][newy] = CustomValue[x][y];
8622
8623   ChangeDelay[x][y] = 0;
8624   ChangePage[x][y] = -1;
8625   ChangeCount[x][y] = 0;
8626   ChangeEvent[x][y] = -1;
8627
8628   CustomValue[x][y] = 0;
8629
8630   // copy animation control values to new field
8631   GfxFrame[newx][newy]  = GfxFrame[x][y];
8632   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8633   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8634   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8635
8636   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8637
8638   // some elements can leave other elements behind after moving
8639   if (ei->move_leave_element != EL_EMPTY &&
8640       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8641       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8642   {
8643     int move_leave_element = ei->move_leave_element;
8644
8645     // this makes it possible to leave the removed element again
8646     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8647       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8648
8649     Tile[x][y] = move_leave_element;
8650
8651     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8652       MovDir[x][y] = direction;
8653
8654     InitField(x, y, FALSE);
8655
8656     if (GFX_CRUMBLED(Tile[x][y]))
8657       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8658
8659     if (ELEM_IS_PLAYER(move_leave_element))
8660       RelocatePlayer(x, y, move_leave_element);
8661   }
8662
8663   // do this after checking for left-behind element
8664   ResetGfxAnimation(x, y);      // reset animation values for old field
8665
8666   if (!CAN_MOVE(element) ||
8667       (CAN_FALL(element) && direction == MV_DOWN &&
8668        (element == EL_SPRING ||
8669         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8670         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8671     GfxDir[x][y] = MovDir[newx][newy] = 0;
8672
8673   TEST_DrawLevelField(x, y);
8674   TEST_DrawLevelField(newx, newy);
8675
8676   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8677
8678   // prevent pushed element from moving on in pushed direction
8679   if (pushed_by_player && CAN_MOVE(element) &&
8680       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8681       !(element_info[element].move_pattern & direction))
8682     TurnRound(newx, newy);
8683
8684   // prevent elements on conveyor belt from moving on in last direction
8685   if (pushed_by_conveyor && CAN_FALL(element) &&
8686       direction & MV_HORIZONTAL)
8687     MovDir[newx][newy] = 0;
8688
8689   if (!pushed_by_player)
8690   {
8691     int nextx = newx + dx, nexty = newy + dy;
8692     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8693
8694     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8695
8696     if (CAN_FALL(element) && direction == MV_DOWN)
8697       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8698
8699     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8700       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8701
8702     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8703       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8704   }
8705
8706   if (DONT_TOUCH(element))      // object may be nasty to player or others
8707   {
8708     TestIfBadThingTouchesPlayer(newx, newy);
8709     TestIfBadThingTouchesFriend(newx, newy);
8710
8711     if (!IS_CUSTOM_ELEMENT(element))
8712       TestIfBadThingTouchesOtherBadThing(newx, newy);
8713   }
8714   else if (element == EL_PENGUIN)
8715     TestIfFriendTouchesBadThing(newx, newy);
8716
8717   if (DONT_GET_HIT_BY(element))
8718   {
8719     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8720   }
8721
8722   // give the player one last chance (one more frame) to move away
8723   if (CAN_FALL(element) && direction == MV_DOWN &&
8724       (last_line || (!IS_FREE(x, newy + 1) &&
8725                      (!IS_PLAYER(x, newy + 1) ||
8726                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8727     Impact(x, newy);
8728
8729   if (pushed_by_player && !game.use_change_when_pushing_bug)
8730   {
8731     int push_side = MV_DIR_OPPOSITE(direction);
8732     struct PlayerInfo *player = PLAYERINFO(x, y);
8733
8734     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8735                                player->index_bit, push_side);
8736     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8737                                         player->index_bit, push_side);
8738   }
8739
8740   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8741     MovDelay[newx][newy] = 1;
8742
8743   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8744
8745   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8746   TestIfElementHitsCustomElement(newx, newy, direction);
8747   TestIfPlayerTouchesCustomElement(newx, newy);
8748   TestIfElementTouchesCustomElement(newx, newy);
8749
8750   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8751       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8752     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8753                              MV_DIR_OPPOSITE(direction));
8754 }
8755
8756 int AmoebaNeighbourNr(int ax, int ay)
8757 {
8758   int i;
8759   int element = Tile[ax][ay];
8760   int group_nr = 0;
8761   static int xy[4][2] =
8762   {
8763     { 0, -1 },
8764     { -1, 0 },
8765     { +1, 0 },
8766     { 0, +1 }
8767   };
8768
8769   for (i = 0; i < NUM_DIRECTIONS; i++)
8770   {
8771     int x = ax + xy[i][0];
8772     int y = ay + xy[i][1];
8773
8774     if (!IN_LEV_FIELD(x, y))
8775       continue;
8776
8777     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8778       group_nr = AmoebaNr[x][y];
8779   }
8780
8781   return group_nr;
8782 }
8783
8784 static void AmoebaMerge(int ax, int ay)
8785 {
8786   int i, x, y, xx, yy;
8787   int new_group_nr = AmoebaNr[ax][ay];
8788   static int xy[4][2] =
8789   {
8790     { 0, -1 },
8791     { -1, 0 },
8792     { +1, 0 },
8793     { 0, +1 }
8794   };
8795
8796   if (new_group_nr == 0)
8797     return;
8798
8799   for (i = 0; i < NUM_DIRECTIONS; i++)
8800   {
8801     x = ax + xy[i][0];
8802     y = ay + xy[i][1];
8803
8804     if (!IN_LEV_FIELD(x, y))
8805       continue;
8806
8807     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8808          Tile[x][y] == EL_BD_AMOEBA ||
8809          Tile[x][y] == EL_AMOEBA_DEAD) &&
8810         AmoebaNr[x][y] != new_group_nr)
8811     {
8812       int old_group_nr = AmoebaNr[x][y];
8813
8814       if (old_group_nr == 0)
8815         return;
8816
8817       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8818       AmoebaCnt[old_group_nr] = 0;
8819       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8820       AmoebaCnt2[old_group_nr] = 0;
8821
8822       SCAN_PLAYFIELD(xx, yy)
8823       {
8824         if (AmoebaNr[xx][yy] == old_group_nr)
8825           AmoebaNr[xx][yy] = new_group_nr;
8826       }
8827     }
8828   }
8829 }
8830
8831 void AmoebaToDiamond(int ax, int ay)
8832 {
8833   int i, x, y;
8834
8835   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8836   {
8837     int group_nr = AmoebaNr[ax][ay];
8838
8839 #ifdef DEBUG
8840     if (group_nr == 0)
8841     {
8842       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8843       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8844
8845       return;
8846     }
8847 #endif
8848
8849     SCAN_PLAYFIELD(x, y)
8850     {
8851       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8852       {
8853         AmoebaNr[x][y] = 0;
8854         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8855       }
8856     }
8857
8858     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8859                             SND_AMOEBA_TURNING_TO_GEM :
8860                             SND_AMOEBA_TURNING_TO_ROCK));
8861     Bang(ax, ay);
8862   }
8863   else
8864   {
8865     static int xy[4][2] =
8866     {
8867       { 0, -1 },
8868       { -1, 0 },
8869       { +1, 0 },
8870       { 0, +1 }
8871     };
8872
8873     for (i = 0; i < NUM_DIRECTIONS; i++)
8874     {
8875       x = ax + xy[i][0];
8876       y = ay + xy[i][1];
8877
8878       if (!IN_LEV_FIELD(x, y))
8879         continue;
8880
8881       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8882       {
8883         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8884                               SND_AMOEBA_TURNING_TO_GEM :
8885                               SND_AMOEBA_TURNING_TO_ROCK));
8886         Bang(x, y);
8887       }
8888     }
8889   }
8890 }
8891
8892 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8893 {
8894   int x, y;
8895   int group_nr = AmoebaNr[ax][ay];
8896   boolean done = FALSE;
8897
8898 #ifdef DEBUG
8899   if (group_nr == 0)
8900   {
8901     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8902     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8903
8904     return;
8905   }
8906 #endif
8907
8908   SCAN_PLAYFIELD(x, y)
8909   {
8910     if (AmoebaNr[x][y] == group_nr &&
8911         (Tile[x][y] == EL_AMOEBA_DEAD ||
8912          Tile[x][y] == EL_BD_AMOEBA ||
8913          Tile[x][y] == EL_AMOEBA_GROWING))
8914     {
8915       AmoebaNr[x][y] = 0;
8916       Tile[x][y] = new_element;
8917       InitField(x, y, FALSE);
8918       TEST_DrawLevelField(x, y);
8919       done = TRUE;
8920     }
8921   }
8922
8923   if (done)
8924     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8925                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8926                             SND_BD_AMOEBA_TURNING_TO_GEM));
8927 }
8928
8929 static void AmoebaGrowing(int x, int y)
8930 {
8931   static unsigned int sound_delay = 0;
8932   static unsigned int sound_delay_value = 0;
8933
8934   if (!MovDelay[x][y])          // start new growing cycle
8935   {
8936     MovDelay[x][y] = 7;
8937
8938     if (DelayReached(&sound_delay, sound_delay_value))
8939     {
8940       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8941       sound_delay_value = 30;
8942     }
8943   }
8944
8945   if (MovDelay[x][y])           // wait some time before growing bigger
8946   {
8947     MovDelay[x][y]--;
8948     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8949     {
8950       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8951                                            6 - MovDelay[x][y]);
8952
8953       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8954     }
8955
8956     if (!MovDelay[x][y])
8957     {
8958       Tile[x][y] = Store[x][y];
8959       Store[x][y] = 0;
8960       TEST_DrawLevelField(x, y);
8961     }
8962   }
8963 }
8964
8965 static void AmoebaShrinking(int x, int y)
8966 {
8967   static unsigned int sound_delay = 0;
8968   static unsigned int sound_delay_value = 0;
8969
8970   if (!MovDelay[x][y])          // start new shrinking cycle
8971   {
8972     MovDelay[x][y] = 7;
8973
8974     if (DelayReached(&sound_delay, sound_delay_value))
8975       sound_delay_value = 30;
8976   }
8977
8978   if (MovDelay[x][y])           // wait some time before shrinking
8979   {
8980     MovDelay[x][y]--;
8981     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8982     {
8983       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8984                                            6 - MovDelay[x][y]);
8985
8986       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8987     }
8988
8989     if (!MovDelay[x][y])
8990     {
8991       Tile[x][y] = EL_EMPTY;
8992       TEST_DrawLevelField(x, y);
8993
8994       // don't let mole enter this field in this cycle;
8995       // (give priority to objects falling to this field from above)
8996       Stop[x][y] = TRUE;
8997     }
8998   }
8999 }
9000
9001 static void AmoebaReproduce(int ax, int ay)
9002 {
9003   int i;
9004   int element = Tile[ax][ay];
9005   int graphic = el2img(element);
9006   int newax = ax, neway = ay;
9007   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9008   static int xy[4][2] =
9009   {
9010     { 0, -1 },
9011     { -1, 0 },
9012     { +1, 0 },
9013     { 0, +1 }
9014   };
9015
9016   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9017   {
9018     Tile[ax][ay] = EL_AMOEBA_DEAD;
9019     TEST_DrawLevelField(ax, ay);
9020     return;
9021   }
9022
9023   if (IS_ANIMATED(graphic))
9024     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9025
9026   if (!MovDelay[ax][ay])        // start making new amoeba field
9027     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9028
9029   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9030   {
9031     MovDelay[ax][ay]--;
9032     if (MovDelay[ax][ay])
9033       return;
9034   }
9035
9036   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9037   {
9038     int start = RND(4);
9039     int x = ax + xy[start][0];
9040     int y = ay + xy[start][1];
9041
9042     if (!IN_LEV_FIELD(x, y))
9043       return;
9044
9045     if (IS_FREE(x, y) ||
9046         CAN_GROW_INTO(Tile[x][y]) ||
9047         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9048         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9049     {
9050       newax = x;
9051       neway = y;
9052     }
9053
9054     if (newax == ax && neway == ay)
9055       return;
9056   }
9057   else                          // normal or "filled" (BD style) amoeba
9058   {
9059     int start = RND(4);
9060     boolean waiting_for_player = FALSE;
9061
9062     for (i = 0; i < NUM_DIRECTIONS; i++)
9063     {
9064       int j = (start + i) % 4;
9065       int x = ax + xy[j][0];
9066       int y = ay + xy[j][1];
9067
9068       if (!IN_LEV_FIELD(x, y))
9069         continue;
9070
9071       if (IS_FREE(x, y) ||
9072           CAN_GROW_INTO(Tile[x][y]) ||
9073           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9074           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9075       {
9076         newax = x;
9077         neway = y;
9078         break;
9079       }
9080       else if (IS_PLAYER(x, y))
9081         waiting_for_player = TRUE;
9082     }
9083
9084     if (newax == ax && neway == ay)             // amoeba cannot grow
9085     {
9086       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9087       {
9088         Tile[ax][ay] = EL_AMOEBA_DEAD;
9089         TEST_DrawLevelField(ax, ay);
9090         AmoebaCnt[AmoebaNr[ax][ay]]--;
9091
9092         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9093         {
9094           if (element == EL_AMOEBA_FULL)
9095             AmoebaToDiamond(ax, ay);
9096           else if (element == EL_BD_AMOEBA)
9097             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9098         }
9099       }
9100       return;
9101     }
9102     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9103     {
9104       // amoeba gets larger by growing in some direction
9105
9106       int new_group_nr = AmoebaNr[ax][ay];
9107
9108 #ifdef DEBUG
9109   if (new_group_nr == 0)
9110   {
9111     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9112           newax, neway);
9113     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9114
9115     return;
9116   }
9117 #endif
9118
9119       AmoebaNr[newax][neway] = new_group_nr;
9120       AmoebaCnt[new_group_nr]++;
9121       AmoebaCnt2[new_group_nr]++;
9122
9123       // if amoeba touches other amoeba(s) after growing, unify them
9124       AmoebaMerge(newax, neway);
9125
9126       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9127       {
9128         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9129         return;
9130       }
9131     }
9132   }
9133
9134   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9135       (neway == lev_fieldy - 1 && newax != ax))
9136   {
9137     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9138     Store[newax][neway] = element;
9139   }
9140   else if (neway == ay || element == EL_EMC_DRIPPER)
9141   {
9142     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9143
9144     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9145   }
9146   else
9147   {
9148     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9149     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9150     Store[ax][ay] = EL_AMOEBA_DROP;
9151     ContinueMoving(ax, ay);
9152     return;
9153   }
9154
9155   TEST_DrawLevelField(newax, neway);
9156 }
9157
9158 static void Life(int ax, int ay)
9159 {
9160   int x1, y1, x2, y2;
9161   int life_time = 40;
9162   int element = Tile[ax][ay];
9163   int graphic = el2img(element);
9164   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9165                          level.biomaze);
9166   boolean changed = FALSE;
9167
9168   if (IS_ANIMATED(graphic))
9169     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9170
9171   if (Stop[ax][ay])
9172     return;
9173
9174   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9175     MovDelay[ax][ay] = life_time;
9176
9177   if (MovDelay[ax][ay])         // wait some time before next cycle
9178   {
9179     MovDelay[ax][ay]--;
9180     if (MovDelay[ax][ay])
9181       return;
9182   }
9183
9184   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9185   {
9186     int xx = ax+x1, yy = ay+y1;
9187     int old_element = Tile[xx][yy];
9188     int num_neighbours = 0;
9189
9190     if (!IN_LEV_FIELD(xx, yy))
9191       continue;
9192
9193     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9194     {
9195       int x = xx+x2, y = yy+y2;
9196
9197       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9198         continue;
9199
9200       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9201       boolean is_neighbour = FALSE;
9202
9203       if (level.use_life_bugs)
9204         is_neighbour =
9205           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9206            (IS_FREE(x, y)                             &&  Stop[x][y]));
9207       else
9208         is_neighbour =
9209           (Last[x][y] == element || is_player_cell);
9210
9211       if (is_neighbour)
9212         num_neighbours++;
9213     }
9214
9215     boolean is_free = FALSE;
9216
9217     if (level.use_life_bugs)
9218       is_free = (IS_FREE(xx, yy));
9219     else
9220       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9221
9222     if (xx == ax && yy == ay)           // field in the middle
9223     {
9224       if (num_neighbours < life_parameter[0] ||
9225           num_neighbours > life_parameter[1])
9226       {
9227         Tile[xx][yy] = EL_EMPTY;
9228         if (Tile[xx][yy] != old_element)
9229           TEST_DrawLevelField(xx, yy);
9230         Stop[xx][yy] = TRUE;
9231         changed = TRUE;
9232       }
9233     }
9234     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9235     {                                   // free border field
9236       if (num_neighbours >= life_parameter[2] &&
9237           num_neighbours <= life_parameter[3])
9238       {
9239         Tile[xx][yy] = element;
9240         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9241         if (Tile[xx][yy] != old_element)
9242           TEST_DrawLevelField(xx, yy);
9243         Stop[xx][yy] = TRUE;
9244         changed = TRUE;
9245       }
9246     }
9247   }
9248
9249   if (changed)
9250     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9251                    SND_GAME_OF_LIFE_GROWING);
9252 }
9253
9254 static void InitRobotWheel(int x, int y)
9255 {
9256   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9257 }
9258
9259 static void RunRobotWheel(int x, int y)
9260 {
9261   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9262 }
9263
9264 static void StopRobotWheel(int x, int y)
9265 {
9266   if (game.robot_wheel_x == x &&
9267       game.robot_wheel_y == y)
9268   {
9269     game.robot_wheel_x = -1;
9270     game.robot_wheel_y = -1;
9271     game.robot_wheel_active = FALSE;
9272   }
9273 }
9274
9275 static void InitTimegateWheel(int x, int y)
9276 {
9277   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9278 }
9279
9280 static void RunTimegateWheel(int x, int y)
9281 {
9282   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9283 }
9284
9285 static void InitMagicBallDelay(int x, int y)
9286 {
9287   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9288 }
9289
9290 static void ActivateMagicBall(int bx, int by)
9291 {
9292   int x, y;
9293
9294   if (level.ball_random)
9295   {
9296     int pos_border = RND(8);    // select one of the eight border elements
9297     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9298     int xx = pos_content % 3;
9299     int yy = pos_content / 3;
9300
9301     x = bx - 1 + xx;
9302     y = by - 1 + yy;
9303
9304     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9305       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9306   }
9307   else
9308   {
9309     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9310     {
9311       int xx = x - bx + 1;
9312       int yy = y - by + 1;
9313
9314       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9315         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9316     }
9317   }
9318
9319   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9320 }
9321
9322 static void CheckExit(int x, int y)
9323 {
9324   if (game.gems_still_needed > 0 ||
9325       game.sokoban_fields_still_needed > 0 ||
9326       game.sokoban_objects_still_needed > 0 ||
9327       game.lights_still_needed > 0)
9328   {
9329     int element = Tile[x][y];
9330     int graphic = el2img(element);
9331
9332     if (IS_ANIMATED(graphic))
9333       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9334
9335     return;
9336   }
9337
9338   // do not re-open exit door closed after last player
9339   if (game.all_players_gone)
9340     return;
9341
9342   Tile[x][y] = EL_EXIT_OPENING;
9343
9344   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9345 }
9346
9347 static void CheckExitEM(int x, int y)
9348 {
9349   if (game.gems_still_needed > 0 ||
9350       game.sokoban_fields_still_needed > 0 ||
9351       game.sokoban_objects_still_needed > 0 ||
9352       game.lights_still_needed > 0)
9353   {
9354     int element = Tile[x][y];
9355     int graphic = el2img(element);
9356
9357     if (IS_ANIMATED(graphic))
9358       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9359
9360     return;
9361   }
9362
9363   // do not re-open exit door closed after last player
9364   if (game.all_players_gone)
9365     return;
9366
9367   Tile[x][y] = EL_EM_EXIT_OPENING;
9368
9369   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9370 }
9371
9372 static void CheckExitSteel(int x, int y)
9373 {
9374   if (game.gems_still_needed > 0 ||
9375       game.sokoban_fields_still_needed > 0 ||
9376       game.sokoban_objects_still_needed > 0 ||
9377       game.lights_still_needed > 0)
9378   {
9379     int element = Tile[x][y];
9380     int graphic = el2img(element);
9381
9382     if (IS_ANIMATED(graphic))
9383       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9384
9385     return;
9386   }
9387
9388   // do not re-open exit door closed after last player
9389   if (game.all_players_gone)
9390     return;
9391
9392   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9393
9394   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9395 }
9396
9397 static void CheckExitSteelEM(int x, int y)
9398 {
9399   if (game.gems_still_needed > 0 ||
9400       game.sokoban_fields_still_needed > 0 ||
9401       game.sokoban_objects_still_needed > 0 ||
9402       game.lights_still_needed > 0)
9403   {
9404     int element = Tile[x][y];
9405     int graphic = el2img(element);
9406
9407     if (IS_ANIMATED(graphic))
9408       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9409
9410     return;
9411   }
9412
9413   // do not re-open exit door closed after last player
9414   if (game.all_players_gone)
9415     return;
9416
9417   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9418
9419   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9420 }
9421
9422 static void CheckExitSP(int x, int y)
9423 {
9424   if (game.gems_still_needed > 0)
9425   {
9426     int element = Tile[x][y];
9427     int graphic = el2img(element);
9428
9429     if (IS_ANIMATED(graphic))
9430       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9431
9432     return;
9433   }
9434
9435   // do not re-open exit door closed after last player
9436   if (game.all_players_gone)
9437     return;
9438
9439   Tile[x][y] = EL_SP_EXIT_OPENING;
9440
9441   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9442 }
9443
9444 static void CloseAllOpenTimegates(void)
9445 {
9446   int x, y;
9447
9448   SCAN_PLAYFIELD(x, y)
9449   {
9450     int element = Tile[x][y];
9451
9452     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9453     {
9454       Tile[x][y] = EL_TIMEGATE_CLOSING;
9455
9456       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9457     }
9458   }
9459 }
9460
9461 static void DrawTwinkleOnField(int x, int y)
9462 {
9463   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9464     return;
9465
9466   if (Tile[x][y] == EL_BD_DIAMOND)
9467     return;
9468
9469   if (MovDelay[x][y] == 0)      // next animation frame
9470     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9471
9472   if (MovDelay[x][y] != 0)      // wait some time before next frame
9473   {
9474     MovDelay[x][y]--;
9475
9476     DrawLevelElementAnimation(x, y, Tile[x][y]);
9477
9478     if (MovDelay[x][y] != 0)
9479     {
9480       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9481                                            10 - MovDelay[x][y]);
9482
9483       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9484     }
9485   }
9486 }
9487
9488 static void MauerWaechst(int x, int y)
9489 {
9490   int delay = 6;
9491
9492   if (!MovDelay[x][y])          // next animation frame
9493     MovDelay[x][y] = 3 * delay;
9494
9495   if (MovDelay[x][y])           // wait some time before next frame
9496   {
9497     MovDelay[x][y]--;
9498
9499     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9500     {
9501       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9502       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9503
9504       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9505     }
9506
9507     if (!MovDelay[x][y])
9508     {
9509       if (MovDir[x][y] == MV_LEFT)
9510       {
9511         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9512           TEST_DrawLevelField(x - 1, y);
9513       }
9514       else if (MovDir[x][y] == MV_RIGHT)
9515       {
9516         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9517           TEST_DrawLevelField(x + 1, y);
9518       }
9519       else if (MovDir[x][y] == MV_UP)
9520       {
9521         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9522           TEST_DrawLevelField(x, y - 1);
9523       }
9524       else
9525       {
9526         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9527           TEST_DrawLevelField(x, y + 1);
9528       }
9529
9530       Tile[x][y] = Store[x][y];
9531       Store[x][y] = 0;
9532       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9533       TEST_DrawLevelField(x, y);
9534     }
9535   }
9536 }
9537
9538 static void MauerAbleger(int ax, int ay)
9539 {
9540   int element = Tile[ax][ay];
9541   int graphic = el2img(element);
9542   boolean oben_frei = FALSE, unten_frei = FALSE;
9543   boolean links_frei = FALSE, rechts_frei = FALSE;
9544   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9545   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9546   boolean new_wall = FALSE;
9547
9548   if (IS_ANIMATED(graphic))
9549     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9550
9551   if (!MovDelay[ax][ay])        // start building new wall
9552     MovDelay[ax][ay] = 6;
9553
9554   if (MovDelay[ax][ay])         // wait some time before building new wall
9555   {
9556     MovDelay[ax][ay]--;
9557     if (MovDelay[ax][ay])
9558       return;
9559   }
9560
9561   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9562     oben_frei = TRUE;
9563   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9564     unten_frei = TRUE;
9565   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9566     links_frei = TRUE;
9567   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9568     rechts_frei = TRUE;
9569
9570   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9571       element == EL_EXPANDABLE_WALL_ANY)
9572   {
9573     if (oben_frei)
9574     {
9575       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9576       Store[ax][ay-1] = element;
9577       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9578       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9579         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9580                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9581       new_wall = TRUE;
9582     }
9583     if (unten_frei)
9584     {
9585       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9586       Store[ax][ay+1] = element;
9587       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9588       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9589         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9590                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9591       new_wall = TRUE;
9592     }
9593   }
9594
9595   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9596       element == EL_EXPANDABLE_WALL_ANY ||
9597       element == EL_EXPANDABLE_WALL ||
9598       element == EL_BD_EXPANDABLE_WALL)
9599   {
9600     if (links_frei)
9601     {
9602       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9603       Store[ax-1][ay] = element;
9604       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9605       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9606         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9607                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9608       new_wall = TRUE;
9609     }
9610
9611     if (rechts_frei)
9612     {
9613       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9614       Store[ax+1][ay] = element;
9615       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9616       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9617         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9618                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9619       new_wall = TRUE;
9620     }
9621   }
9622
9623   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9624     TEST_DrawLevelField(ax, ay);
9625
9626   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9627     oben_massiv = TRUE;
9628   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9629     unten_massiv = TRUE;
9630   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9631     links_massiv = TRUE;
9632   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9633     rechts_massiv = TRUE;
9634
9635   if (((oben_massiv && unten_massiv) ||
9636        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9637        element == EL_EXPANDABLE_WALL) &&
9638       ((links_massiv && rechts_massiv) ||
9639        element == EL_EXPANDABLE_WALL_VERTICAL))
9640     Tile[ax][ay] = EL_WALL;
9641
9642   if (new_wall)
9643     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9644 }
9645
9646 static void MauerAblegerStahl(int ax, int ay)
9647 {
9648   int element = Tile[ax][ay];
9649   int graphic = el2img(element);
9650   boolean oben_frei = FALSE, unten_frei = FALSE;
9651   boolean links_frei = FALSE, rechts_frei = FALSE;
9652   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9653   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9654   boolean new_wall = FALSE;
9655
9656   if (IS_ANIMATED(graphic))
9657     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9658
9659   if (!MovDelay[ax][ay])        // start building new wall
9660     MovDelay[ax][ay] = 6;
9661
9662   if (MovDelay[ax][ay])         // wait some time before building new wall
9663   {
9664     MovDelay[ax][ay]--;
9665     if (MovDelay[ax][ay])
9666       return;
9667   }
9668
9669   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9670     oben_frei = TRUE;
9671   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9672     unten_frei = TRUE;
9673   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9674     links_frei = TRUE;
9675   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9676     rechts_frei = TRUE;
9677
9678   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9679       element == EL_EXPANDABLE_STEELWALL_ANY)
9680   {
9681     if (oben_frei)
9682     {
9683       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9684       Store[ax][ay-1] = element;
9685       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9686       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9687         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9688                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9689       new_wall = TRUE;
9690     }
9691     if (unten_frei)
9692     {
9693       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9694       Store[ax][ay+1] = element;
9695       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9696       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9697         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9698                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9699       new_wall = TRUE;
9700     }
9701   }
9702
9703   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9704       element == EL_EXPANDABLE_STEELWALL_ANY)
9705   {
9706     if (links_frei)
9707     {
9708       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9709       Store[ax-1][ay] = element;
9710       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9711       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9712         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9713                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9714       new_wall = TRUE;
9715     }
9716
9717     if (rechts_frei)
9718     {
9719       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9720       Store[ax+1][ay] = element;
9721       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9722       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9723         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9724                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9725       new_wall = TRUE;
9726     }
9727   }
9728
9729   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9730     oben_massiv = TRUE;
9731   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9732     unten_massiv = TRUE;
9733   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9734     links_massiv = TRUE;
9735   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9736     rechts_massiv = TRUE;
9737
9738   if (((oben_massiv && unten_massiv) ||
9739        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9740       ((links_massiv && rechts_massiv) ||
9741        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9742     Tile[ax][ay] = EL_STEELWALL;
9743
9744   if (new_wall)
9745     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9746 }
9747
9748 static void CheckForDragon(int x, int y)
9749 {
9750   int i, j;
9751   boolean dragon_found = FALSE;
9752   static int xy[4][2] =
9753   {
9754     { 0, -1 },
9755     { -1, 0 },
9756     { +1, 0 },
9757     { 0, +1 }
9758   };
9759
9760   for (i = 0; i < NUM_DIRECTIONS; i++)
9761   {
9762     for (j = 0; j < 4; j++)
9763     {
9764       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9765
9766       if (IN_LEV_FIELD(xx, yy) &&
9767           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9768       {
9769         if (Tile[xx][yy] == EL_DRAGON)
9770           dragon_found = TRUE;
9771       }
9772       else
9773         break;
9774     }
9775   }
9776
9777   if (!dragon_found)
9778   {
9779     for (i = 0; i < NUM_DIRECTIONS; i++)
9780     {
9781       for (j = 0; j < 3; j++)
9782       {
9783         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9784   
9785         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9786         {
9787           Tile[xx][yy] = EL_EMPTY;
9788           TEST_DrawLevelField(xx, yy);
9789         }
9790         else
9791           break;
9792       }
9793     }
9794   }
9795 }
9796
9797 static void InitBuggyBase(int x, int y)
9798 {
9799   int element = Tile[x][y];
9800   int activating_delay = FRAMES_PER_SECOND / 4;
9801
9802   ChangeDelay[x][y] =
9803     (element == EL_SP_BUGGY_BASE ?
9804      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9805      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9806      activating_delay :
9807      element == EL_SP_BUGGY_BASE_ACTIVE ?
9808      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9809 }
9810
9811 static void WarnBuggyBase(int x, int y)
9812 {
9813   int i;
9814   static int xy[4][2] =
9815   {
9816     { 0, -1 },
9817     { -1, 0 },
9818     { +1, 0 },
9819     { 0, +1 }
9820   };
9821
9822   for (i = 0; i < NUM_DIRECTIONS; i++)
9823   {
9824     int xx = x + xy[i][0];
9825     int yy = y + xy[i][1];
9826
9827     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9828     {
9829       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9830
9831       break;
9832     }
9833   }
9834 }
9835
9836 static void InitTrap(int x, int y)
9837 {
9838   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9839 }
9840
9841 static void ActivateTrap(int x, int y)
9842 {
9843   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9844 }
9845
9846 static void ChangeActiveTrap(int x, int y)
9847 {
9848   int graphic = IMG_TRAP_ACTIVE;
9849
9850   // if new animation frame was drawn, correct crumbled sand border
9851   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9852     TEST_DrawLevelFieldCrumbled(x, y);
9853 }
9854
9855 static int getSpecialActionElement(int element, int number, int base_element)
9856 {
9857   return (element != EL_EMPTY ? element :
9858           number != -1 ? base_element + number - 1 :
9859           EL_EMPTY);
9860 }
9861
9862 static int getModifiedActionNumber(int value_old, int operator, int operand,
9863                                    int value_min, int value_max)
9864 {
9865   int value_new = (operator == CA_MODE_SET      ? operand :
9866                    operator == CA_MODE_ADD      ? value_old + operand :
9867                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9868                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9869                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9870                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9871                    value_old);
9872
9873   return (value_new < value_min ? value_min :
9874           value_new > value_max ? value_max :
9875           value_new);
9876 }
9877
9878 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9879 {
9880   struct ElementInfo *ei = &element_info[element];
9881   struct ElementChangeInfo *change = &ei->change_page[page];
9882   int target_element = change->target_element;
9883   int action_type = change->action_type;
9884   int action_mode = change->action_mode;
9885   int action_arg = change->action_arg;
9886   int action_element = change->action_element;
9887   int i;
9888
9889   if (!change->has_action)
9890     return;
9891
9892   // ---------- determine action paramater values -----------------------------
9893
9894   int level_time_value =
9895     (level.time > 0 ? TimeLeft :
9896      TimePlayed);
9897
9898   int action_arg_element_raw =
9899     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9900      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9901      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9902      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9903      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9904      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9905      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9906      EL_EMPTY);
9907   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9908
9909   int action_arg_direction =
9910     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9911      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9912      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9913      change->actual_trigger_side :
9914      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9915      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9916      MV_NONE);
9917
9918   int action_arg_number_min =
9919     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9920      CA_ARG_MIN);
9921
9922   int action_arg_number_max =
9923     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9924      action_type == CA_SET_LEVEL_GEMS ? 999 :
9925      action_type == CA_SET_LEVEL_TIME ? 9999 :
9926      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9927      action_type == CA_SET_CE_VALUE ? 9999 :
9928      action_type == CA_SET_CE_SCORE ? 9999 :
9929      CA_ARG_MAX);
9930
9931   int action_arg_number_reset =
9932     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9933      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9934      action_type == CA_SET_LEVEL_TIME ? level.time :
9935      action_type == CA_SET_LEVEL_SCORE ? 0 :
9936      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9937      action_type == CA_SET_CE_SCORE ? 0 :
9938      0);
9939
9940   int action_arg_number =
9941     (action_arg <= CA_ARG_MAX ? action_arg :
9942      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9943      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9944      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9945      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9946      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9947      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9948      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9949      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9950      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9951      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9952      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9953      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9954      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9955      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9956      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9957      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9958      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9959      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9960      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9961      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9962      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9963      -1);
9964
9965   int action_arg_number_old =
9966     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9967      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9968      action_type == CA_SET_LEVEL_SCORE ? game.score :
9969      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9970      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9971      0);
9972
9973   int action_arg_number_new =
9974     getModifiedActionNumber(action_arg_number_old,
9975                             action_mode, action_arg_number,
9976                             action_arg_number_min, action_arg_number_max);
9977
9978   int trigger_player_bits =
9979     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9980      change->actual_trigger_player_bits : change->trigger_player);
9981
9982   int action_arg_player_bits =
9983     (action_arg >= CA_ARG_PLAYER_1 &&
9984      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9985      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9986      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9987      PLAYER_BITS_ANY);
9988
9989   // ---------- execute action  -----------------------------------------------
9990
9991   switch (action_type)
9992   {
9993     case CA_NO_ACTION:
9994     {
9995       return;
9996     }
9997
9998     // ---------- level actions  ----------------------------------------------
9999
10000     case CA_RESTART_LEVEL:
10001     {
10002       game.restart_level = TRUE;
10003
10004       break;
10005     }
10006
10007     case CA_SHOW_ENVELOPE:
10008     {
10009       int element = getSpecialActionElement(action_arg_element,
10010                                             action_arg_number, EL_ENVELOPE_1);
10011
10012       if (IS_ENVELOPE(element))
10013         local_player->show_envelope = element;
10014
10015       break;
10016     }
10017
10018     case CA_SET_LEVEL_TIME:
10019     {
10020       if (level.time > 0)       // only modify limited time value
10021       {
10022         TimeLeft = action_arg_number_new;
10023
10024         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10025
10026         DisplayGameControlValues();
10027
10028         if (!TimeLeft && setup.time_limit)
10029           for (i = 0; i < MAX_PLAYERS; i++)
10030             KillPlayer(&stored_player[i]);
10031       }
10032
10033       break;
10034     }
10035
10036     case CA_SET_LEVEL_SCORE:
10037     {
10038       game.score = action_arg_number_new;
10039
10040       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10041
10042       DisplayGameControlValues();
10043
10044       break;
10045     }
10046
10047     case CA_SET_LEVEL_GEMS:
10048     {
10049       game.gems_still_needed = action_arg_number_new;
10050
10051       game.snapshot.collected_item = TRUE;
10052
10053       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10054
10055       DisplayGameControlValues();
10056
10057       break;
10058     }
10059
10060     case CA_SET_LEVEL_WIND:
10061     {
10062       game.wind_direction = action_arg_direction;
10063
10064       break;
10065     }
10066
10067     case CA_SET_LEVEL_RANDOM_SEED:
10068     {
10069       // ensure that setting a new random seed while playing is predictable
10070       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10071
10072       break;
10073     }
10074
10075     // ---------- player actions  ---------------------------------------------
10076
10077     case CA_MOVE_PLAYER:
10078     case CA_MOVE_PLAYER_NEW:
10079     {
10080       // automatically move to the next field in specified direction
10081       for (i = 0; i < MAX_PLAYERS; i++)
10082         if (trigger_player_bits & (1 << i))
10083           if (action_type == CA_MOVE_PLAYER ||
10084               stored_player[i].MovPos == 0)
10085             stored_player[i].programmed_action = action_arg_direction;
10086
10087       break;
10088     }
10089
10090     case CA_EXIT_PLAYER:
10091     {
10092       for (i = 0; i < MAX_PLAYERS; i++)
10093         if (action_arg_player_bits & (1 << i))
10094           ExitPlayer(&stored_player[i]);
10095
10096       if (game.players_still_needed == 0)
10097         LevelSolved();
10098
10099       break;
10100     }
10101
10102     case CA_KILL_PLAYER:
10103     {
10104       for (i = 0; i < MAX_PLAYERS; i++)
10105         if (action_arg_player_bits & (1 << i))
10106           KillPlayer(&stored_player[i]);
10107
10108       break;
10109     }
10110
10111     case CA_SET_PLAYER_KEYS:
10112     {
10113       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10114       int element = getSpecialActionElement(action_arg_element,
10115                                             action_arg_number, EL_KEY_1);
10116
10117       if (IS_KEY(element))
10118       {
10119         for (i = 0; i < MAX_PLAYERS; i++)
10120         {
10121           if (trigger_player_bits & (1 << i))
10122           {
10123             stored_player[i].key[KEY_NR(element)] = key_state;
10124
10125             DrawGameDoorValues();
10126           }
10127         }
10128       }
10129
10130       break;
10131     }
10132
10133     case CA_SET_PLAYER_SPEED:
10134     {
10135       for (i = 0; i < MAX_PLAYERS; i++)
10136       {
10137         if (trigger_player_bits & (1 << i))
10138         {
10139           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10140
10141           if (action_arg == CA_ARG_SPEED_FASTER &&
10142               stored_player[i].cannot_move)
10143           {
10144             action_arg_number = STEPSIZE_VERY_SLOW;
10145           }
10146           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10147                    action_arg == CA_ARG_SPEED_FASTER)
10148           {
10149             action_arg_number = 2;
10150             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10151                            CA_MODE_MULTIPLY);
10152           }
10153           else if (action_arg == CA_ARG_NUMBER_RESET)
10154           {
10155             action_arg_number = level.initial_player_stepsize[i];
10156           }
10157
10158           move_stepsize =
10159             getModifiedActionNumber(move_stepsize,
10160                                     action_mode,
10161                                     action_arg_number,
10162                                     action_arg_number_min,
10163                                     action_arg_number_max);
10164
10165           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10166         }
10167       }
10168
10169       break;
10170     }
10171
10172     case CA_SET_PLAYER_SHIELD:
10173     {
10174       for (i = 0; i < MAX_PLAYERS; i++)
10175       {
10176         if (trigger_player_bits & (1 << i))
10177         {
10178           if (action_arg == CA_ARG_SHIELD_OFF)
10179           {
10180             stored_player[i].shield_normal_time_left = 0;
10181             stored_player[i].shield_deadly_time_left = 0;
10182           }
10183           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10184           {
10185             stored_player[i].shield_normal_time_left = 999999;
10186           }
10187           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10188           {
10189             stored_player[i].shield_normal_time_left = 999999;
10190             stored_player[i].shield_deadly_time_left = 999999;
10191           }
10192         }
10193       }
10194
10195       break;
10196     }
10197
10198     case CA_SET_PLAYER_GRAVITY:
10199     {
10200       for (i = 0; i < MAX_PLAYERS; i++)
10201       {
10202         if (trigger_player_bits & (1 << i))
10203         {
10204           stored_player[i].gravity =
10205             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10206              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10207              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10208              stored_player[i].gravity);
10209         }
10210       }
10211
10212       break;
10213     }
10214
10215     case CA_SET_PLAYER_ARTWORK:
10216     {
10217       for (i = 0; i < MAX_PLAYERS; i++)
10218       {
10219         if (trigger_player_bits & (1 << i))
10220         {
10221           int artwork_element = action_arg_element;
10222
10223           if (action_arg == CA_ARG_ELEMENT_RESET)
10224             artwork_element =
10225               (level.use_artwork_element[i] ? level.artwork_element[i] :
10226                stored_player[i].element_nr);
10227
10228           if (stored_player[i].artwork_element != artwork_element)
10229             stored_player[i].Frame = 0;
10230
10231           stored_player[i].artwork_element = artwork_element;
10232
10233           SetPlayerWaiting(&stored_player[i], FALSE);
10234
10235           // set number of special actions for bored and sleeping animation
10236           stored_player[i].num_special_action_bored =
10237             get_num_special_action(artwork_element,
10238                                    ACTION_BORING_1, ACTION_BORING_LAST);
10239           stored_player[i].num_special_action_sleeping =
10240             get_num_special_action(artwork_element,
10241                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10242         }
10243       }
10244
10245       break;
10246     }
10247
10248     case CA_SET_PLAYER_INVENTORY:
10249     {
10250       for (i = 0; i < MAX_PLAYERS; i++)
10251       {
10252         struct PlayerInfo *player = &stored_player[i];
10253         int j, k;
10254
10255         if (trigger_player_bits & (1 << i))
10256         {
10257           int inventory_element = action_arg_element;
10258
10259           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10260               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10261               action_arg == CA_ARG_ELEMENT_ACTION)
10262           {
10263             int element = inventory_element;
10264             int collect_count = element_info[element].collect_count_initial;
10265
10266             if (!IS_CUSTOM_ELEMENT(element))
10267               collect_count = 1;
10268
10269             if (collect_count == 0)
10270               player->inventory_infinite_element = element;
10271             else
10272               for (k = 0; k < collect_count; k++)
10273                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10274                   player->inventory_element[player->inventory_size++] =
10275                     element;
10276           }
10277           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10278                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10279                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10280           {
10281             if (player->inventory_infinite_element != EL_UNDEFINED &&
10282                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10283                                      action_arg_element_raw))
10284               player->inventory_infinite_element = EL_UNDEFINED;
10285
10286             for (k = 0, j = 0; j < player->inventory_size; j++)
10287             {
10288               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10289                                         action_arg_element_raw))
10290                 player->inventory_element[k++] = player->inventory_element[j];
10291             }
10292
10293             player->inventory_size = k;
10294           }
10295           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10296           {
10297             if (player->inventory_size > 0)
10298             {
10299               for (j = 0; j < player->inventory_size - 1; j++)
10300                 player->inventory_element[j] = player->inventory_element[j + 1];
10301
10302               player->inventory_size--;
10303             }
10304           }
10305           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10306           {
10307             if (player->inventory_size > 0)
10308               player->inventory_size--;
10309           }
10310           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10311           {
10312             player->inventory_infinite_element = EL_UNDEFINED;
10313             player->inventory_size = 0;
10314           }
10315           else if (action_arg == CA_ARG_INVENTORY_RESET)
10316           {
10317             player->inventory_infinite_element = EL_UNDEFINED;
10318             player->inventory_size = 0;
10319
10320             if (level.use_initial_inventory[i])
10321             {
10322               for (j = 0; j < level.initial_inventory_size[i]; j++)
10323               {
10324                 int element = level.initial_inventory_content[i][j];
10325                 int collect_count = element_info[element].collect_count_initial;
10326
10327                 if (!IS_CUSTOM_ELEMENT(element))
10328                   collect_count = 1;
10329
10330                 if (collect_count == 0)
10331                   player->inventory_infinite_element = element;
10332                 else
10333                   for (k = 0; k < collect_count; k++)
10334                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10335                       player->inventory_element[player->inventory_size++] =
10336                         element;
10337               }
10338             }
10339           }
10340         }
10341       }
10342
10343       break;
10344     }
10345
10346     // ---------- CE actions  -------------------------------------------------
10347
10348     case CA_SET_CE_VALUE:
10349     {
10350       int last_ce_value = CustomValue[x][y];
10351
10352       CustomValue[x][y] = action_arg_number_new;
10353
10354       if (CustomValue[x][y] != last_ce_value)
10355       {
10356         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10357         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10358
10359         if (CustomValue[x][y] == 0)
10360         {
10361           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10362           ChangeCount[x][y] = 0;        // allow at least one more change
10363
10364           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10365           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10366         }
10367       }
10368
10369       break;
10370     }
10371
10372     case CA_SET_CE_SCORE:
10373     {
10374       int last_ce_score = ei->collect_score;
10375
10376       ei->collect_score = action_arg_number_new;
10377
10378       if (ei->collect_score != last_ce_score)
10379       {
10380         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10381         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10382
10383         if (ei->collect_score == 0)
10384         {
10385           int xx, yy;
10386
10387           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10388           ChangeCount[x][y] = 0;        // allow at least one more change
10389
10390           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10391           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10392
10393           /*
10394             This is a very special case that seems to be a mixture between
10395             CheckElementChange() and CheckTriggeredElementChange(): while
10396             the first one only affects single elements that are triggered
10397             directly, the second one affects multiple elements in the playfield
10398             that are triggered indirectly by another element. This is a third
10399             case: Changing the CE score always affects multiple identical CEs,
10400             so every affected CE must be checked, not only the single CE for
10401             which the CE score was changed in the first place (as every instance
10402             of that CE shares the same CE score, and therefore also can change)!
10403           */
10404           SCAN_PLAYFIELD(xx, yy)
10405           {
10406             if (Tile[xx][yy] == element)
10407               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10408                                  CE_SCORE_GETS_ZERO);
10409           }
10410         }
10411       }
10412
10413       break;
10414     }
10415
10416     case CA_SET_CE_ARTWORK:
10417     {
10418       int artwork_element = action_arg_element;
10419       boolean reset_frame = FALSE;
10420       int xx, yy;
10421
10422       if (action_arg == CA_ARG_ELEMENT_RESET)
10423         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10424                            element);
10425
10426       if (ei->gfx_element != artwork_element)
10427         reset_frame = TRUE;
10428
10429       ei->gfx_element = artwork_element;
10430
10431       SCAN_PLAYFIELD(xx, yy)
10432       {
10433         if (Tile[xx][yy] == element)
10434         {
10435           if (reset_frame)
10436           {
10437             ResetGfxAnimation(xx, yy);
10438             ResetRandomAnimationValue(xx, yy);
10439           }
10440
10441           TEST_DrawLevelField(xx, yy);
10442         }
10443       }
10444
10445       break;
10446     }
10447
10448     // ---------- engine actions  ---------------------------------------------
10449
10450     case CA_SET_ENGINE_SCAN_MODE:
10451     {
10452       InitPlayfieldScanMode(action_arg);
10453
10454       break;
10455     }
10456
10457     default:
10458       break;
10459   }
10460 }
10461
10462 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10463 {
10464   int old_element = Tile[x][y];
10465   int new_element = GetElementFromGroupElement(element);
10466   int previous_move_direction = MovDir[x][y];
10467   int last_ce_value = CustomValue[x][y];
10468   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10469   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10470   boolean add_player_onto_element = (new_element_is_player &&
10471                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10472                                      IS_WALKABLE(old_element));
10473
10474   if (!add_player_onto_element)
10475   {
10476     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10477       RemoveMovingField(x, y);
10478     else
10479       RemoveField(x, y);
10480
10481     Tile[x][y] = new_element;
10482
10483     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10484       MovDir[x][y] = previous_move_direction;
10485
10486     if (element_info[new_element].use_last_ce_value)
10487       CustomValue[x][y] = last_ce_value;
10488
10489     InitField_WithBug1(x, y, FALSE);
10490
10491     new_element = Tile[x][y];   // element may have changed
10492
10493     ResetGfxAnimation(x, y);
10494     ResetRandomAnimationValue(x, y);
10495
10496     TEST_DrawLevelField(x, y);
10497
10498     if (GFX_CRUMBLED(new_element))
10499       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10500   }
10501
10502   // check if element under the player changes from accessible to unaccessible
10503   // (needed for special case of dropping element which then changes)
10504   // (must be checked after creating new element for walkable group elements)
10505   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10506       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10507   {
10508     Bang(x, y);
10509
10510     return;
10511   }
10512
10513   // "ChangeCount" not set yet to allow "entered by player" change one time
10514   if (new_element_is_player)
10515     RelocatePlayer(x, y, new_element);
10516
10517   if (is_change)
10518     ChangeCount[x][y]++;        // count number of changes in the same frame
10519
10520   TestIfBadThingTouchesPlayer(x, y);
10521   TestIfPlayerTouchesCustomElement(x, y);
10522   TestIfElementTouchesCustomElement(x, y);
10523 }
10524
10525 static void CreateField(int x, int y, int element)
10526 {
10527   CreateFieldExt(x, y, element, FALSE);
10528 }
10529
10530 static void CreateElementFromChange(int x, int y, int element)
10531 {
10532   element = GET_VALID_RUNTIME_ELEMENT(element);
10533
10534   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10535   {
10536     int old_element = Tile[x][y];
10537
10538     // prevent changed element from moving in same engine frame
10539     // unless both old and new element can either fall or move
10540     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10541         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10542       Stop[x][y] = TRUE;
10543   }
10544
10545   CreateFieldExt(x, y, element, TRUE);
10546 }
10547
10548 static boolean ChangeElement(int x, int y, int element, int page)
10549 {
10550   struct ElementInfo *ei = &element_info[element];
10551   struct ElementChangeInfo *change = &ei->change_page[page];
10552   int ce_value = CustomValue[x][y];
10553   int ce_score = ei->collect_score;
10554   int target_element;
10555   int old_element = Tile[x][y];
10556
10557   // always use default change event to prevent running into a loop
10558   if (ChangeEvent[x][y] == -1)
10559     ChangeEvent[x][y] = CE_DELAY;
10560
10561   if (ChangeEvent[x][y] == CE_DELAY)
10562   {
10563     // reset actual trigger element, trigger player and action element
10564     change->actual_trigger_element = EL_EMPTY;
10565     change->actual_trigger_player = EL_EMPTY;
10566     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10567     change->actual_trigger_side = CH_SIDE_NONE;
10568     change->actual_trigger_ce_value = 0;
10569     change->actual_trigger_ce_score = 0;
10570   }
10571
10572   // do not change elements more than a specified maximum number of changes
10573   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10574     return FALSE;
10575
10576   ChangeCount[x][y]++;          // count number of changes in the same frame
10577
10578   if (change->explode)
10579   {
10580     Bang(x, y);
10581
10582     return TRUE;
10583   }
10584
10585   if (change->use_target_content)
10586   {
10587     boolean complete_replace = TRUE;
10588     boolean can_replace[3][3];
10589     int xx, yy;
10590
10591     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10592     {
10593       boolean is_empty;
10594       boolean is_walkable;
10595       boolean is_diggable;
10596       boolean is_collectible;
10597       boolean is_removable;
10598       boolean is_destructible;
10599       int ex = x + xx - 1;
10600       int ey = y + yy - 1;
10601       int content_element = change->target_content.e[xx][yy];
10602       int e;
10603
10604       can_replace[xx][yy] = TRUE;
10605
10606       if (ex == x && ey == y)   // do not check changing element itself
10607         continue;
10608
10609       if (content_element == EL_EMPTY_SPACE)
10610       {
10611         can_replace[xx][yy] = FALSE;    // do not replace border with space
10612
10613         continue;
10614       }
10615
10616       if (!IN_LEV_FIELD(ex, ey))
10617       {
10618         can_replace[xx][yy] = FALSE;
10619         complete_replace = FALSE;
10620
10621         continue;
10622       }
10623
10624       e = Tile[ex][ey];
10625
10626       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10627         e = MovingOrBlocked2Element(ex, ey);
10628
10629       is_empty = (IS_FREE(ex, ey) ||
10630                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10631
10632       is_walkable     = (is_empty || IS_WALKABLE(e));
10633       is_diggable     = (is_empty || IS_DIGGABLE(e));
10634       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10635       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10636       is_removable    = (is_diggable || is_collectible);
10637
10638       can_replace[xx][yy] =
10639         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10640           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10641           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10642           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10643           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10644           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10645          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10646
10647       if (!can_replace[xx][yy])
10648         complete_replace = FALSE;
10649     }
10650
10651     if (!change->only_if_complete || complete_replace)
10652     {
10653       boolean something_has_changed = FALSE;
10654
10655       if (change->only_if_complete && change->use_random_replace &&
10656           RND(100) < change->random_percentage)
10657         return FALSE;
10658
10659       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10660       {
10661         int ex = x + xx - 1;
10662         int ey = y + yy - 1;
10663         int content_element;
10664
10665         if (can_replace[xx][yy] && (!change->use_random_replace ||
10666                                     RND(100) < change->random_percentage))
10667         {
10668           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10669             RemoveMovingField(ex, ey);
10670
10671           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10672
10673           content_element = change->target_content.e[xx][yy];
10674           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10675                                               ce_value, ce_score);
10676
10677           CreateElementFromChange(ex, ey, target_element);
10678
10679           something_has_changed = TRUE;
10680
10681           // for symmetry reasons, freeze newly created border elements
10682           if (ex != x || ey != y)
10683             Stop[ex][ey] = TRUE;        // no more moving in this frame
10684         }
10685       }
10686
10687       if (something_has_changed)
10688       {
10689         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10690         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10691       }
10692     }
10693   }
10694   else
10695   {
10696     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10697                                         ce_value, ce_score);
10698
10699     if (element == EL_DIAGONAL_GROWING ||
10700         element == EL_DIAGONAL_SHRINKING)
10701     {
10702       target_element = Store[x][y];
10703
10704       Store[x][y] = EL_EMPTY;
10705     }
10706
10707     CreateElementFromChange(x, y, target_element);
10708
10709     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10710     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10711   }
10712
10713   // this uses direct change before indirect change
10714   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10715
10716   return TRUE;
10717 }
10718
10719 static void HandleElementChange(int x, int y, int page)
10720 {
10721   int element = MovingOrBlocked2Element(x, y);
10722   struct ElementInfo *ei = &element_info[element];
10723   struct ElementChangeInfo *change = &ei->change_page[page];
10724   boolean handle_action_before_change = FALSE;
10725
10726 #ifdef DEBUG
10727   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10728       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10729   {
10730     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10731           x, y, element, element_info[element].token_name);
10732     Debug("game:playing:HandleElementChange", "This should never happen!");
10733   }
10734 #endif
10735
10736   // this can happen with classic bombs on walkable, changing elements
10737   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10738   {
10739     return;
10740   }
10741
10742   if (ChangeDelay[x][y] == 0)           // initialize element change
10743   {
10744     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10745
10746     if (change->can_change)
10747     {
10748       // !!! not clear why graphic animation should be reset at all here !!!
10749       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10750       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10751
10752       /*
10753         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10754
10755         When using an animation frame delay of 1 (this only happens with
10756         "sp_zonk.moving.left/right" in the classic graphics), the default
10757         (non-moving) animation shows wrong animation frames (while the
10758         moving animation, like "sp_zonk.moving.left/right", is correct,
10759         so this graphical bug never shows up with the classic graphics).
10760         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10761         be drawn instead of the correct frames 0,1,2,3. This is caused by
10762         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10763         an element change: First when the change delay ("ChangeDelay[][]")
10764         counter has reached zero after decrementing, then a second time in
10765         the next frame (after "GfxFrame[][]" was already incremented) when
10766         "ChangeDelay[][]" is reset to the initial delay value again.
10767
10768         This causes frame 0 to be drawn twice, while the last frame won't
10769         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10770
10771         As some animations may already be cleverly designed around this bug
10772         (at least the "Snake Bite" snake tail animation does this), it cannot
10773         simply be fixed here without breaking such existing animations.
10774         Unfortunately, it cannot easily be detected if a graphics set was
10775         designed "before" or "after" the bug was fixed. As a workaround,
10776         a new graphics set option "game.graphics_engine_version" was added
10777         to be able to specify the game's major release version for which the
10778         graphics set was designed, which can then be used to decide if the
10779         bugfix should be used (version 4 and above) or not (version 3 or
10780         below, or if no version was specified at all, as with old sets).
10781
10782         (The wrong/fixed animation frames can be tested with the test level set
10783         "test_gfxframe" and level "000", which contains a specially prepared
10784         custom element at level position (x/y) == (11/9) which uses the zonk
10785         animation mentioned above. Using "game.graphics_engine_version: 4"
10786         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10787         This can also be seen from the debug output for this test element.)
10788       */
10789
10790       // when a custom element is about to change (for example by change delay),
10791       // do not reset graphic animation when the custom element is moving
10792       if (game.graphics_engine_version < 4 &&
10793           !IS_MOVING(x, y))
10794       {
10795         ResetGfxAnimation(x, y);
10796         ResetRandomAnimationValue(x, y);
10797       }
10798
10799       if (change->pre_change_function)
10800         change->pre_change_function(x, y);
10801     }
10802   }
10803
10804   ChangeDelay[x][y]--;
10805
10806   if (ChangeDelay[x][y] != 0)           // continue element change
10807   {
10808     if (change->can_change)
10809     {
10810       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10811
10812       if (IS_ANIMATED(graphic))
10813         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10814
10815       if (change->change_function)
10816         change->change_function(x, y);
10817     }
10818   }
10819   else                                  // finish element change
10820   {
10821     if (ChangePage[x][y] != -1)         // remember page from delayed change
10822     {
10823       page = ChangePage[x][y];
10824       ChangePage[x][y] = -1;
10825
10826       change = &ei->change_page[page];
10827     }
10828
10829     if (IS_MOVING(x, y))                // never change a running system ;-)
10830     {
10831       ChangeDelay[x][y] = 1;            // try change after next move step
10832       ChangePage[x][y] = page;          // remember page to use for change
10833
10834       return;
10835     }
10836
10837     // special case: set new level random seed before changing element
10838     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10839       handle_action_before_change = TRUE;
10840
10841     if (change->has_action && handle_action_before_change)
10842       ExecuteCustomElementAction(x, y, element, page);
10843
10844     if (change->can_change)
10845     {
10846       if (ChangeElement(x, y, element, page))
10847       {
10848         if (change->post_change_function)
10849           change->post_change_function(x, y);
10850       }
10851     }
10852
10853     if (change->has_action && !handle_action_before_change)
10854       ExecuteCustomElementAction(x, y, element, page);
10855   }
10856 }
10857
10858 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10859                                               int trigger_element,
10860                                               int trigger_event,
10861                                               int trigger_player,
10862                                               int trigger_side,
10863                                               int trigger_page)
10864 {
10865   boolean change_done_any = FALSE;
10866   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10867   int i;
10868
10869   if (!(trigger_events[trigger_element][trigger_event]))
10870     return FALSE;
10871
10872   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10873
10874   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10875   {
10876     int element = EL_CUSTOM_START + i;
10877     boolean change_done = FALSE;
10878     int p;
10879
10880     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10881         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10882       continue;
10883
10884     for (p = 0; p < element_info[element].num_change_pages; p++)
10885     {
10886       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10887
10888       if (change->can_change_or_has_action &&
10889           change->has_event[trigger_event] &&
10890           change->trigger_side & trigger_side &&
10891           change->trigger_player & trigger_player &&
10892           change->trigger_page & trigger_page_bits &&
10893           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10894       {
10895         change->actual_trigger_element = trigger_element;
10896         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10897         change->actual_trigger_player_bits = trigger_player;
10898         change->actual_trigger_side = trigger_side;
10899         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10900         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10901
10902         if ((change->can_change && !change_done) || change->has_action)
10903         {
10904           int x, y;
10905
10906           SCAN_PLAYFIELD(x, y)
10907           {
10908             if (Tile[x][y] == element)
10909             {
10910               if (change->can_change && !change_done)
10911               {
10912                 // if element already changed in this frame, not only prevent
10913                 // another element change (checked in ChangeElement()), but
10914                 // also prevent additional element actions for this element
10915
10916                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10917                     !level.use_action_after_change_bug)
10918                   continue;
10919
10920                 ChangeDelay[x][y] = 1;
10921                 ChangeEvent[x][y] = trigger_event;
10922
10923                 HandleElementChange(x, y, p);
10924               }
10925               else if (change->has_action)
10926               {
10927                 // if element already changed in this frame, not only prevent
10928                 // another element change (checked in ChangeElement()), but
10929                 // also prevent additional element actions for this element
10930
10931                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10932                     !level.use_action_after_change_bug)
10933                   continue;
10934
10935                 ExecuteCustomElementAction(x, y, element, p);
10936                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10937               }
10938             }
10939           }
10940
10941           if (change->can_change)
10942           {
10943             change_done = TRUE;
10944             change_done_any = TRUE;
10945           }
10946         }
10947       }
10948     }
10949   }
10950
10951   RECURSION_LOOP_DETECTION_END();
10952
10953   return change_done_any;
10954 }
10955
10956 static boolean CheckElementChangeExt(int x, int y,
10957                                      int element,
10958                                      int trigger_element,
10959                                      int trigger_event,
10960                                      int trigger_player,
10961                                      int trigger_side)
10962 {
10963   boolean change_done = FALSE;
10964   int p;
10965
10966   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10967       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10968     return FALSE;
10969
10970   if (Tile[x][y] == EL_BLOCKED)
10971   {
10972     Blocked2Moving(x, y, &x, &y);
10973     element = Tile[x][y];
10974   }
10975
10976   // check if element has already changed or is about to change after moving
10977   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10978        Tile[x][y] != element) ||
10979
10980       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10981        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10982         ChangePage[x][y] != -1)))
10983     return FALSE;
10984
10985   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10986
10987   for (p = 0; p < element_info[element].num_change_pages; p++)
10988   {
10989     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10990
10991     /* check trigger element for all events where the element that is checked
10992        for changing interacts with a directly adjacent element -- this is
10993        different to element changes that affect other elements to change on the
10994        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10995     boolean check_trigger_element =
10996       (trigger_event == CE_TOUCHING_X ||
10997        trigger_event == CE_HITTING_X ||
10998        trigger_event == CE_HIT_BY_X ||
10999        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11000
11001     if (change->can_change_or_has_action &&
11002         change->has_event[trigger_event] &&
11003         change->trigger_side & trigger_side &&
11004         change->trigger_player & trigger_player &&
11005         (!check_trigger_element ||
11006          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11007     {
11008       change->actual_trigger_element = trigger_element;
11009       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11010       change->actual_trigger_player_bits = trigger_player;
11011       change->actual_trigger_side = trigger_side;
11012       change->actual_trigger_ce_value = CustomValue[x][y];
11013       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11014
11015       // special case: trigger element not at (x,y) position for some events
11016       if (check_trigger_element)
11017       {
11018         static struct
11019         {
11020           int dx, dy;
11021         } move_xy[] =
11022           {
11023             {  0,  0 },
11024             { -1,  0 },
11025             { +1,  0 },
11026             {  0,  0 },
11027             {  0, -1 },
11028             {  0,  0 }, { 0, 0 }, { 0, 0 },
11029             {  0, +1 }
11030           };
11031
11032         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11033         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11034
11035         change->actual_trigger_ce_value = CustomValue[xx][yy];
11036         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11037       }
11038
11039       if (change->can_change && !change_done)
11040       {
11041         ChangeDelay[x][y] = 1;
11042         ChangeEvent[x][y] = trigger_event;
11043
11044         HandleElementChange(x, y, p);
11045
11046         change_done = TRUE;
11047       }
11048       else if (change->has_action)
11049       {
11050         ExecuteCustomElementAction(x, y, element, p);
11051         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11052       }
11053     }
11054   }
11055
11056   RECURSION_LOOP_DETECTION_END();
11057
11058   return change_done;
11059 }
11060
11061 static void PlayPlayerSound(struct PlayerInfo *player)
11062 {
11063   int jx = player->jx, jy = player->jy;
11064   int sound_element = player->artwork_element;
11065   int last_action = player->last_action_waiting;
11066   int action = player->action_waiting;
11067
11068   if (player->is_waiting)
11069   {
11070     if (action != last_action)
11071       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11072     else
11073       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11074   }
11075   else
11076   {
11077     if (action != last_action)
11078       StopSound(element_info[sound_element].sound[last_action]);
11079
11080     if (last_action == ACTION_SLEEPING)
11081       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11082   }
11083 }
11084
11085 static void PlayAllPlayersSound(void)
11086 {
11087   int i;
11088
11089   for (i = 0; i < MAX_PLAYERS; i++)
11090     if (stored_player[i].active)
11091       PlayPlayerSound(&stored_player[i]);
11092 }
11093
11094 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11095 {
11096   boolean last_waiting = player->is_waiting;
11097   int move_dir = player->MovDir;
11098
11099   player->dir_waiting = move_dir;
11100   player->last_action_waiting = player->action_waiting;
11101
11102   if (is_waiting)
11103   {
11104     if (!last_waiting)          // not waiting -> waiting
11105     {
11106       player->is_waiting = TRUE;
11107
11108       player->frame_counter_bored =
11109         FrameCounter +
11110         game.player_boring_delay_fixed +
11111         GetSimpleRandom(game.player_boring_delay_random);
11112       player->frame_counter_sleeping =
11113         FrameCounter +
11114         game.player_sleeping_delay_fixed +
11115         GetSimpleRandom(game.player_sleeping_delay_random);
11116
11117       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11118     }
11119
11120     if (game.player_sleeping_delay_fixed +
11121         game.player_sleeping_delay_random > 0 &&
11122         player->anim_delay_counter == 0 &&
11123         player->post_delay_counter == 0 &&
11124         FrameCounter >= player->frame_counter_sleeping)
11125       player->is_sleeping = TRUE;
11126     else if (game.player_boring_delay_fixed +
11127              game.player_boring_delay_random > 0 &&
11128              FrameCounter >= player->frame_counter_bored)
11129       player->is_bored = TRUE;
11130
11131     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11132                               player->is_bored ? ACTION_BORING :
11133                               ACTION_WAITING);
11134
11135     if (player->is_sleeping && player->use_murphy)
11136     {
11137       // special case for sleeping Murphy when leaning against non-free tile
11138
11139       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11140           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11141            !IS_MOVING(player->jx - 1, player->jy)))
11142         move_dir = MV_LEFT;
11143       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11144                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11145                 !IS_MOVING(player->jx + 1, player->jy)))
11146         move_dir = MV_RIGHT;
11147       else
11148         player->is_sleeping = FALSE;
11149
11150       player->dir_waiting = move_dir;
11151     }
11152
11153     if (player->is_sleeping)
11154     {
11155       if (player->num_special_action_sleeping > 0)
11156       {
11157         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11158         {
11159           int last_special_action = player->special_action_sleeping;
11160           int num_special_action = player->num_special_action_sleeping;
11161           int special_action =
11162             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11163              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11164              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11165              last_special_action + 1 : ACTION_SLEEPING);
11166           int special_graphic =
11167             el_act_dir2img(player->artwork_element, special_action, move_dir);
11168
11169           player->anim_delay_counter =
11170             graphic_info[special_graphic].anim_delay_fixed +
11171             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11172           player->post_delay_counter =
11173             graphic_info[special_graphic].post_delay_fixed +
11174             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11175
11176           player->special_action_sleeping = special_action;
11177         }
11178
11179         if (player->anim_delay_counter > 0)
11180         {
11181           player->action_waiting = player->special_action_sleeping;
11182           player->anim_delay_counter--;
11183         }
11184         else if (player->post_delay_counter > 0)
11185         {
11186           player->post_delay_counter--;
11187         }
11188       }
11189     }
11190     else if (player->is_bored)
11191     {
11192       if (player->num_special_action_bored > 0)
11193       {
11194         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11195         {
11196           int special_action =
11197             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11198           int special_graphic =
11199             el_act_dir2img(player->artwork_element, special_action, move_dir);
11200
11201           player->anim_delay_counter =
11202             graphic_info[special_graphic].anim_delay_fixed +
11203             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11204           player->post_delay_counter =
11205             graphic_info[special_graphic].post_delay_fixed +
11206             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11207
11208           player->special_action_bored = special_action;
11209         }
11210
11211         if (player->anim_delay_counter > 0)
11212         {
11213           player->action_waiting = player->special_action_bored;
11214           player->anim_delay_counter--;
11215         }
11216         else if (player->post_delay_counter > 0)
11217         {
11218           player->post_delay_counter--;
11219         }
11220       }
11221     }
11222   }
11223   else if (last_waiting)        // waiting -> not waiting
11224   {
11225     player->is_waiting = FALSE;
11226     player->is_bored = FALSE;
11227     player->is_sleeping = FALSE;
11228
11229     player->frame_counter_bored = -1;
11230     player->frame_counter_sleeping = -1;
11231
11232     player->anim_delay_counter = 0;
11233     player->post_delay_counter = 0;
11234
11235     player->dir_waiting = player->MovDir;
11236     player->action_waiting = ACTION_DEFAULT;
11237
11238     player->special_action_bored = ACTION_DEFAULT;
11239     player->special_action_sleeping = ACTION_DEFAULT;
11240   }
11241 }
11242
11243 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11244 {
11245   if ((!player->is_moving  && player->was_moving) ||
11246       (player->MovPos == 0 && player->was_moving) ||
11247       (player->is_snapping && !player->was_snapping) ||
11248       (player->is_dropping && !player->was_dropping))
11249   {
11250     if (!CheckSaveEngineSnapshotToList())
11251       return;
11252
11253     player->was_moving = FALSE;
11254     player->was_snapping = TRUE;
11255     player->was_dropping = TRUE;
11256   }
11257   else
11258   {
11259     if (player->is_moving)
11260       player->was_moving = TRUE;
11261
11262     if (!player->is_snapping)
11263       player->was_snapping = FALSE;
11264
11265     if (!player->is_dropping)
11266       player->was_dropping = FALSE;
11267   }
11268
11269   static struct MouseActionInfo mouse_action_last = { 0 };
11270   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11271   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11272
11273   if (new_released)
11274     CheckSaveEngineSnapshotToList();
11275
11276   mouse_action_last = mouse_action;
11277 }
11278
11279 static void CheckSingleStepMode(struct PlayerInfo *player)
11280 {
11281   if (tape.single_step && tape.recording && !tape.pausing)
11282   {
11283     /* as it is called "single step mode", just return to pause mode when the
11284        player stopped moving after one tile (or never starts moving at all) */
11285     if (!player->is_moving &&
11286         !player->is_pushing &&
11287         !player->is_dropping_pressed &&
11288         !player->effective_mouse_action.button)
11289       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11290   }
11291
11292   CheckSaveEngineSnapshot(player);
11293 }
11294
11295 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11296 {
11297   int left      = player_action & JOY_LEFT;
11298   int right     = player_action & JOY_RIGHT;
11299   int up        = player_action & JOY_UP;
11300   int down      = player_action & JOY_DOWN;
11301   int button1   = player_action & JOY_BUTTON_1;
11302   int button2   = player_action & JOY_BUTTON_2;
11303   int dx        = (left ? -1 : right ? 1 : 0);
11304   int dy        = (up   ? -1 : down  ? 1 : 0);
11305
11306   if (!player->active || tape.pausing)
11307     return 0;
11308
11309   if (player_action)
11310   {
11311     if (button1)
11312       SnapField(player, dx, dy);
11313     else
11314     {
11315       if (button2)
11316         DropElement(player);
11317
11318       MovePlayer(player, dx, dy);
11319     }
11320
11321     CheckSingleStepMode(player);
11322
11323     SetPlayerWaiting(player, FALSE);
11324
11325     return player_action;
11326   }
11327   else
11328   {
11329     // no actions for this player (no input at player's configured device)
11330
11331     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11332     SnapField(player, 0, 0);
11333     CheckGravityMovementWhenNotMoving(player);
11334
11335     if (player->MovPos == 0)
11336       SetPlayerWaiting(player, TRUE);
11337
11338     if (player->MovPos == 0)    // needed for tape.playing
11339       player->is_moving = FALSE;
11340
11341     player->is_dropping = FALSE;
11342     player->is_dropping_pressed = FALSE;
11343     player->drop_pressed_delay = 0;
11344
11345     CheckSingleStepMode(player);
11346
11347     return 0;
11348   }
11349 }
11350
11351 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11352                                          byte *tape_action)
11353 {
11354   if (!tape.use_mouse_actions)
11355     return;
11356
11357   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11358   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11359   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11360 }
11361
11362 static void SetTapeActionFromMouseAction(byte *tape_action,
11363                                          struct MouseActionInfo *mouse_action)
11364 {
11365   if (!tape.use_mouse_actions)
11366     return;
11367
11368   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11369   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11370   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11371 }
11372
11373 static void CheckLevelSolved(void)
11374 {
11375   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11376   {
11377     if (game_em.level_solved &&
11378         !game_em.game_over)                             // game won
11379     {
11380       LevelSolved();
11381
11382       game_em.game_over = TRUE;
11383
11384       game.all_players_gone = TRUE;
11385     }
11386
11387     if (game_em.game_over)                              // game lost
11388       game.all_players_gone = TRUE;
11389   }
11390   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11391   {
11392     if (game_sp.level_solved &&
11393         !game_sp.game_over)                             // game won
11394     {
11395       LevelSolved();
11396
11397       game_sp.game_over = TRUE;
11398
11399       game.all_players_gone = TRUE;
11400     }
11401
11402     if (game_sp.game_over)                              // game lost
11403       game.all_players_gone = TRUE;
11404   }
11405   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11406   {
11407     if (game_mm.level_solved &&
11408         !game_mm.game_over)                             // game won
11409     {
11410       LevelSolved();
11411
11412       game_mm.game_over = TRUE;
11413
11414       game.all_players_gone = TRUE;
11415     }
11416
11417     if (game_mm.game_over)                              // game lost
11418       game.all_players_gone = TRUE;
11419   }
11420 }
11421
11422 static void CheckLevelTime(void)
11423 {
11424   int i;
11425
11426   if (TimeFrames >= FRAMES_PER_SECOND)
11427   {
11428     TimeFrames = 0;
11429     TapeTime++;
11430
11431     for (i = 0; i < MAX_PLAYERS; i++)
11432     {
11433       struct PlayerInfo *player = &stored_player[i];
11434
11435       if (SHIELD_ON(player))
11436       {
11437         player->shield_normal_time_left--;
11438
11439         if (player->shield_deadly_time_left > 0)
11440           player->shield_deadly_time_left--;
11441       }
11442     }
11443
11444     if (!game.LevelSolved && !level.use_step_counter)
11445     {
11446       TimePlayed++;
11447
11448       if (TimeLeft > 0)
11449       {
11450         TimeLeft--;
11451
11452         if (TimeLeft <= 10 && setup.time_limit)
11453           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11454
11455         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11456            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11457
11458         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11459
11460         if (!TimeLeft && setup.time_limit)
11461         {
11462           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11463             game_em.lev->killed_out_of_time = TRUE;
11464           else
11465             for (i = 0; i < MAX_PLAYERS; i++)
11466               KillPlayer(&stored_player[i]);
11467         }
11468       }
11469       else if (game.no_time_limit && !game.all_players_gone)
11470       {
11471         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11472       }
11473
11474       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11475     }
11476
11477     if (tape.recording || tape.playing)
11478       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11479   }
11480
11481   if (tape.recording || tape.playing)
11482     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11483
11484   UpdateAndDisplayGameControlValues();
11485 }
11486
11487 void AdvanceFrameAndPlayerCounters(int player_nr)
11488 {
11489   int i;
11490
11491   // advance frame counters (global frame counter and time frame counter)
11492   FrameCounter++;
11493   TimeFrames++;
11494
11495   // advance player counters (counters for move delay, move animation etc.)
11496   for (i = 0; i < MAX_PLAYERS; i++)
11497   {
11498     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11499     int move_delay_value = stored_player[i].move_delay_value;
11500     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11501
11502     if (!advance_player_counters)       // not all players may be affected
11503       continue;
11504
11505     if (move_frames == 0)       // less than one move per game frame
11506     {
11507       int stepsize = TILEX / move_delay_value;
11508       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11509       int count = (stored_player[i].is_moving ?
11510                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11511
11512       if (count % delay == 0)
11513         move_frames = 1;
11514     }
11515
11516     stored_player[i].Frame += move_frames;
11517
11518     if (stored_player[i].MovPos != 0)
11519       stored_player[i].StepFrame += move_frames;
11520
11521     if (stored_player[i].move_delay > 0)
11522       stored_player[i].move_delay--;
11523
11524     // due to bugs in previous versions, counter must count up, not down
11525     if (stored_player[i].push_delay != -1)
11526       stored_player[i].push_delay++;
11527
11528     if (stored_player[i].drop_delay > 0)
11529       stored_player[i].drop_delay--;
11530
11531     if (stored_player[i].is_dropping_pressed)
11532       stored_player[i].drop_pressed_delay++;
11533   }
11534 }
11535
11536 void StartGameActions(boolean init_network_game, boolean record_tape,
11537                       int random_seed)
11538 {
11539   unsigned int new_random_seed = InitRND(random_seed);
11540
11541   if (record_tape)
11542     TapeStartRecording(new_random_seed);
11543
11544   if (init_network_game)
11545   {
11546     SendToServer_LevelFile();
11547     SendToServer_StartPlaying();
11548
11549     return;
11550   }
11551
11552   InitGame();
11553 }
11554
11555 static void GameActionsExt(void)
11556 {
11557 #if 0
11558   static unsigned int game_frame_delay = 0;
11559 #endif
11560   unsigned int game_frame_delay_value;
11561   byte *recorded_player_action;
11562   byte summarized_player_action = 0;
11563   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11564   int i;
11565
11566   // detect endless loops, caused by custom element programming
11567   if (recursion_loop_detected && recursion_loop_depth == 0)
11568   {
11569     char *message = getStringCat3("Internal Error! Element ",
11570                                   EL_NAME(recursion_loop_element),
11571                                   " caused endless loop! Quit the game?");
11572
11573     Warn("element '%s' caused endless loop in game engine",
11574          EL_NAME(recursion_loop_element));
11575
11576     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11577
11578     recursion_loop_detected = FALSE;    // if game should be continued
11579
11580     free(message);
11581
11582     return;
11583   }
11584
11585   if (game.restart_level)
11586     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11587
11588   CheckLevelSolved();
11589
11590   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11591     GameWon();
11592
11593   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11594     TapeStop();
11595
11596   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11597     return;
11598
11599   game_frame_delay_value =
11600     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11601
11602   if (tape.playing && tape.warp_forward && !tape.pausing)
11603     game_frame_delay_value = 0;
11604
11605   SetVideoFrameDelay(game_frame_delay_value);
11606
11607   // (de)activate virtual buttons depending on current game status
11608   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11609   {
11610     if (game.all_players_gone)  // if no players there to be controlled anymore
11611       SetOverlayActive(FALSE);
11612     else if (!tape.playing)     // if game continues after tape stopped playing
11613       SetOverlayActive(TRUE);
11614   }
11615
11616 #if 0
11617 #if 0
11618   // ---------- main game synchronization point ----------
11619
11620   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11621
11622   Debug("game:playing:skip", "skip == %d", skip);
11623
11624 #else
11625   // ---------- main game synchronization point ----------
11626
11627   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11628 #endif
11629 #endif
11630
11631   if (network_playing && !network_player_action_received)
11632   {
11633     // try to get network player actions in time
11634
11635     // last chance to get network player actions without main loop delay
11636     HandleNetworking();
11637
11638     // game was quit by network peer
11639     if (game_status != GAME_MODE_PLAYING)
11640       return;
11641
11642     // check if network player actions still missing and game still running
11643     if (!network_player_action_received && !checkGameEnded())
11644       return;           // failed to get network player actions in time
11645
11646     // do not yet reset "network_player_action_received" (for tape.pausing)
11647   }
11648
11649   if (tape.pausing)
11650     return;
11651
11652   // at this point we know that we really continue executing the game
11653
11654   network_player_action_received = FALSE;
11655
11656   // when playing tape, read previously recorded player input from tape data
11657   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11658
11659   local_player->effective_mouse_action = local_player->mouse_action;
11660
11661   if (recorded_player_action != NULL)
11662     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11663                                  recorded_player_action);
11664
11665   // TapePlayAction() may return NULL when toggling to "pause before death"
11666   if (tape.pausing)
11667     return;
11668
11669   if (tape.set_centered_player)
11670   {
11671     game.centered_player_nr_next = tape.centered_player_nr_next;
11672     game.set_centered_player = TRUE;
11673   }
11674
11675   for (i = 0; i < MAX_PLAYERS; i++)
11676   {
11677     summarized_player_action |= stored_player[i].action;
11678
11679     if (!network_playing && (game.team_mode || tape.playing))
11680       stored_player[i].effective_action = stored_player[i].action;
11681   }
11682
11683   if (network_playing && !checkGameEnded())
11684     SendToServer_MovePlayer(summarized_player_action);
11685
11686   // summarize all actions at local players mapped input device position
11687   // (this allows using different input devices in single player mode)
11688   if (!network.enabled && !game.team_mode)
11689     stored_player[map_player_action[local_player->index_nr]].effective_action =
11690       summarized_player_action;
11691
11692   // summarize all actions at centered player in local team mode
11693   if (tape.recording &&
11694       setup.team_mode && !network.enabled &&
11695       setup.input_on_focus &&
11696       game.centered_player_nr != -1)
11697   {
11698     for (i = 0; i < MAX_PLAYERS; i++)
11699       stored_player[map_player_action[i]].effective_action =
11700         (i == game.centered_player_nr ? summarized_player_action : 0);
11701   }
11702
11703   if (recorded_player_action != NULL)
11704     for (i = 0; i < MAX_PLAYERS; i++)
11705       stored_player[i].effective_action = recorded_player_action[i];
11706
11707   for (i = 0; i < MAX_PLAYERS; i++)
11708   {
11709     tape_action[i] = stored_player[i].effective_action;
11710
11711     /* (this may happen in the RND game engine if a player was not present on
11712        the playfield on level start, but appeared later from a custom element */
11713     if (setup.team_mode &&
11714         tape.recording &&
11715         tape_action[i] &&
11716         !tape.player_participates[i])
11717       tape.player_participates[i] = TRUE;
11718   }
11719
11720   SetTapeActionFromMouseAction(tape_action,
11721                                &local_player->effective_mouse_action);
11722
11723   // only record actions from input devices, but not programmed actions
11724   if (tape.recording)
11725     TapeRecordAction(tape_action);
11726
11727   // remember if game was played (especially after tape stopped playing)
11728   if (!tape.playing && summarized_player_action)
11729     game.GamePlayed = TRUE;
11730
11731 #if USE_NEW_PLAYER_ASSIGNMENTS
11732   // !!! also map player actions in single player mode !!!
11733   // if (game.team_mode)
11734   if (1)
11735   {
11736     byte mapped_action[MAX_PLAYERS];
11737
11738 #if DEBUG_PLAYER_ACTIONS
11739     for (i = 0; i < MAX_PLAYERS; i++)
11740       DebugContinued("", "%d, ", stored_player[i].effective_action);
11741 #endif
11742
11743     for (i = 0; i < MAX_PLAYERS; i++)
11744       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11745
11746     for (i = 0; i < MAX_PLAYERS; i++)
11747       stored_player[i].effective_action = mapped_action[i];
11748
11749 #if DEBUG_PLAYER_ACTIONS
11750     DebugContinued("", "=> ");
11751     for (i = 0; i < MAX_PLAYERS; i++)
11752       DebugContinued("", "%d, ", stored_player[i].effective_action);
11753     DebugContinued("game:playing:player", "\n");
11754 #endif
11755   }
11756 #if DEBUG_PLAYER_ACTIONS
11757   else
11758   {
11759     for (i = 0; i < MAX_PLAYERS; i++)
11760       DebugContinued("", "%d, ", stored_player[i].effective_action);
11761     DebugContinued("game:playing:player", "\n");
11762   }
11763 #endif
11764 #endif
11765
11766   for (i = 0; i < MAX_PLAYERS; i++)
11767   {
11768     // allow engine snapshot in case of changed movement attempt
11769     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11770         (stored_player[i].effective_action & KEY_MOTION))
11771       game.snapshot.changed_action = TRUE;
11772
11773     // allow engine snapshot in case of snapping/dropping attempt
11774     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11775         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11776       game.snapshot.changed_action = TRUE;
11777
11778     game.snapshot.last_action[i] = stored_player[i].effective_action;
11779   }
11780
11781   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11782   {
11783     GameActions_EM_Main();
11784   }
11785   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11786   {
11787     GameActions_SP_Main();
11788   }
11789   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11790   {
11791     GameActions_MM_Main();
11792   }
11793   else
11794   {
11795     GameActions_RND_Main();
11796   }
11797
11798   BlitScreenToBitmap(backbuffer);
11799
11800   CheckLevelSolved();
11801   CheckLevelTime();
11802
11803   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11804
11805   if (global.show_frames_per_second)
11806   {
11807     static unsigned int fps_counter = 0;
11808     static int fps_frames = 0;
11809     unsigned int fps_delay_ms = Counter() - fps_counter;
11810
11811     fps_frames++;
11812
11813     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11814     {
11815       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11816
11817       fps_frames = 0;
11818       fps_counter = Counter();
11819
11820       // always draw FPS to screen after FPS value was updated
11821       redraw_mask |= REDRAW_FPS;
11822     }
11823
11824     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11825     if (GetDrawDeactivationMask() == REDRAW_NONE)
11826       redraw_mask |= REDRAW_FPS;
11827   }
11828 }
11829
11830 static void GameActions_CheckSaveEngineSnapshot(void)
11831 {
11832   if (!game.snapshot.save_snapshot)
11833     return;
11834
11835   // clear flag for saving snapshot _before_ saving snapshot
11836   game.snapshot.save_snapshot = FALSE;
11837
11838   SaveEngineSnapshotToList();
11839 }
11840
11841 void GameActions(void)
11842 {
11843   GameActionsExt();
11844
11845   GameActions_CheckSaveEngineSnapshot();
11846 }
11847
11848 void GameActions_EM_Main(void)
11849 {
11850   byte effective_action[MAX_PLAYERS];
11851   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11852   int i;
11853
11854   for (i = 0; i < MAX_PLAYERS; i++)
11855     effective_action[i] = stored_player[i].effective_action;
11856
11857   GameActions_EM(effective_action, warp_mode);
11858 }
11859
11860 void GameActions_SP_Main(void)
11861 {
11862   byte effective_action[MAX_PLAYERS];
11863   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11864   int i;
11865
11866   for (i = 0; i < MAX_PLAYERS; i++)
11867     effective_action[i] = stored_player[i].effective_action;
11868
11869   GameActions_SP(effective_action, warp_mode);
11870
11871   for (i = 0; i < MAX_PLAYERS; i++)
11872   {
11873     if (stored_player[i].force_dropping)
11874       stored_player[i].action |= KEY_BUTTON_DROP;
11875
11876     stored_player[i].force_dropping = FALSE;
11877   }
11878 }
11879
11880 void GameActions_MM_Main(void)
11881 {
11882   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11883
11884   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11885 }
11886
11887 void GameActions_RND_Main(void)
11888 {
11889   GameActions_RND();
11890 }
11891
11892 void GameActions_RND(void)
11893 {
11894   static struct MouseActionInfo mouse_action_last = { 0 };
11895   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11896   int magic_wall_x = 0, magic_wall_y = 0;
11897   int i, x, y, element, graphic, last_gfx_frame;
11898
11899   InitPlayfieldScanModeVars();
11900
11901   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11902   {
11903     SCAN_PLAYFIELD(x, y)
11904     {
11905       ChangeCount[x][y] = 0;
11906       ChangeEvent[x][y] = -1;
11907     }
11908   }
11909
11910   if (game.set_centered_player)
11911   {
11912     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11913
11914     // switching to "all players" only possible if all players fit to screen
11915     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11916     {
11917       game.centered_player_nr_next = game.centered_player_nr;
11918       game.set_centered_player = FALSE;
11919     }
11920
11921     // do not switch focus to non-existing (or non-active) player
11922     if (game.centered_player_nr_next >= 0 &&
11923         !stored_player[game.centered_player_nr_next].active)
11924     {
11925       game.centered_player_nr_next = game.centered_player_nr;
11926       game.set_centered_player = FALSE;
11927     }
11928   }
11929
11930   if (game.set_centered_player &&
11931       ScreenMovPos == 0)        // screen currently aligned at tile position
11932   {
11933     int sx, sy;
11934
11935     if (game.centered_player_nr_next == -1)
11936     {
11937       setScreenCenteredToAllPlayers(&sx, &sy);
11938     }
11939     else
11940     {
11941       sx = stored_player[game.centered_player_nr_next].jx;
11942       sy = stored_player[game.centered_player_nr_next].jy;
11943     }
11944
11945     game.centered_player_nr = game.centered_player_nr_next;
11946     game.set_centered_player = FALSE;
11947
11948     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11949     DrawGameDoorValues();
11950   }
11951
11952   for (i = 0; i < MAX_PLAYERS; i++)
11953   {
11954     int actual_player_action = stored_player[i].effective_action;
11955
11956 #if 1
11957     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11958        - rnd_equinox_tetrachloride 048
11959        - rnd_equinox_tetrachloride_ii 096
11960        - rnd_emanuel_schmieg 002
11961        - doctor_sloan_ww 001, 020
11962     */
11963     if (stored_player[i].MovPos == 0)
11964       CheckGravityMovement(&stored_player[i]);
11965 #endif
11966
11967     // overwrite programmed action with tape action
11968     if (stored_player[i].programmed_action)
11969       actual_player_action = stored_player[i].programmed_action;
11970
11971     PlayerActions(&stored_player[i], actual_player_action);
11972
11973     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11974   }
11975
11976   ScrollScreen(NULL, SCROLL_GO_ON);
11977
11978   /* for backwards compatibility, the following code emulates a fixed bug that
11979      occured when pushing elements (causing elements that just made their last
11980      pushing step to already (if possible) make their first falling step in the
11981      same game frame, which is bad); this code is also needed to use the famous
11982      "spring push bug" which is used in older levels and might be wanted to be
11983      used also in newer levels, but in this case the buggy pushing code is only
11984      affecting the "spring" element and no other elements */
11985
11986   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11987   {
11988     for (i = 0; i < MAX_PLAYERS; i++)
11989     {
11990       struct PlayerInfo *player = &stored_player[i];
11991       int x = player->jx;
11992       int y = player->jy;
11993
11994       if (player->active && player->is_pushing && player->is_moving &&
11995           IS_MOVING(x, y) &&
11996           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11997            Tile[x][y] == EL_SPRING))
11998       {
11999         ContinueMoving(x, y);
12000
12001         // continue moving after pushing (this is actually a bug)
12002         if (!IS_MOVING(x, y))
12003           Stop[x][y] = FALSE;
12004       }
12005     }
12006   }
12007
12008   SCAN_PLAYFIELD(x, y)
12009   {
12010     Last[x][y] = Tile[x][y];
12011
12012     ChangeCount[x][y] = 0;
12013     ChangeEvent[x][y] = -1;
12014
12015     // this must be handled before main playfield loop
12016     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12017     {
12018       MovDelay[x][y]--;
12019       if (MovDelay[x][y] <= 0)
12020         RemoveField(x, y);
12021     }
12022
12023     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12024     {
12025       MovDelay[x][y]--;
12026       if (MovDelay[x][y] <= 0)
12027       {
12028         RemoveField(x, y);
12029         TEST_DrawLevelField(x, y);
12030
12031         TestIfElementTouchesCustomElement(x, y);        // for empty space
12032       }
12033     }
12034
12035 #if DEBUG
12036     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12037     {
12038       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12039             x, y);
12040       Debug("game:playing:GameActions_RND", "This should never happen!");
12041
12042       ChangePage[x][y] = -1;
12043     }
12044 #endif
12045
12046     Stop[x][y] = FALSE;
12047     if (WasJustMoving[x][y] > 0)
12048       WasJustMoving[x][y]--;
12049     if (WasJustFalling[x][y] > 0)
12050       WasJustFalling[x][y]--;
12051     if (CheckCollision[x][y] > 0)
12052       CheckCollision[x][y]--;
12053     if (CheckImpact[x][y] > 0)
12054       CheckImpact[x][y]--;
12055
12056     GfxFrame[x][y]++;
12057
12058     /* reset finished pushing action (not done in ContinueMoving() to allow
12059        continuous pushing animation for elements with zero push delay) */
12060     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12061     {
12062       ResetGfxAnimation(x, y);
12063       TEST_DrawLevelField(x, y);
12064     }
12065
12066 #if DEBUG
12067     if (IS_BLOCKED(x, y))
12068     {
12069       int oldx, oldy;
12070
12071       Blocked2Moving(x, y, &oldx, &oldy);
12072       if (!IS_MOVING(oldx, oldy))
12073       {
12074         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12075         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12076         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12077         Debug("game:playing:GameActions_RND", "This should never happen!");
12078       }
12079     }
12080 #endif
12081   }
12082
12083   if (mouse_action.button)
12084   {
12085     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12086
12087     x = mouse_action.lx;
12088     y = mouse_action.ly;
12089     element = Tile[x][y];
12090
12091     if (new_button)
12092     {
12093       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12094       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12095     }
12096
12097     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12098     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12099   }
12100
12101   SCAN_PLAYFIELD(x, y)
12102   {
12103     element = Tile[x][y];
12104     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12105     last_gfx_frame = GfxFrame[x][y];
12106
12107     ResetGfxFrame(x, y);
12108
12109     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12110       DrawLevelGraphicAnimation(x, y, graphic);
12111
12112     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12113         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12114       ResetRandomAnimationValue(x, y);
12115
12116     SetRandomAnimationValue(x, y);
12117
12118     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12119
12120     if (IS_INACTIVE(element))
12121     {
12122       if (IS_ANIMATED(graphic))
12123         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12124
12125       continue;
12126     }
12127
12128     // this may take place after moving, so 'element' may have changed
12129     if (IS_CHANGING(x, y) &&
12130         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12131     {
12132       int page = element_info[element].event_page_nr[CE_DELAY];
12133
12134       HandleElementChange(x, y, page);
12135
12136       element = Tile[x][y];
12137       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12138     }
12139
12140     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12141     {
12142       StartMoving(x, y);
12143
12144       element = Tile[x][y];
12145       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12146
12147       if (IS_ANIMATED(graphic) &&
12148           !IS_MOVING(x, y) &&
12149           !Stop[x][y])
12150         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12151
12152       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12153         TEST_DrawTwinkleOnField(x, y);
12154     }
12155     else if (element == EL_ACID)
12156     {
12157       if (!Stop[x][y])
12158         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12159     }
12160     else if ((element == EL_EXIT_OPEN ||
12161               element == EL_EM_EXIT_OPEN ||
12162               element == EL_SP_EXIT_OPEN ||
12163               element == EL_STEEL_EXIT_OPEN ||
12164               element == EL_EM_STEEL_EXIT_OPEN ||
12165               element == EL_SP_TERMINAL ||
12166               element == EL_SP_TERMINAL_ACTIVE ||
12167               element == EL_EXTRA_TIME ||
12168               element == EL_SHIELD_NORMAL ||
12169               element == EL_SHIELD_DEADLY) &&
12170              IS_ANIMATED(graphic))
12171       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12172     else if (IS_MOVING(x, y))
12173       ContinueMoving(x, y);
12174     else if (IS_ACTIVE_BOMB(element))
12175       CheckDynamite(x, y);
12176     else if (element == EL_AMOEBA_GROWING)
12177       AmoebaGrowing(x, y);
12178     else if (element == EL_AMOEBA_SHRINKING)
12179       AmoebaShrinking(x, y);
12180
12181 #if !USE_NEW_AMOEBA_CODE
12182     else if (IS_AMOEBALIVE(element))
12183       AmoebaReproduce(x, y);
12184 #endif
12185
12186     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12187       Life(x, y);
12188     else if (element == EL_EXIT_CLOSED)
12189       CheckExit(x, y);
12190     else if (element == EL_EM_EXIT_CLOSED)
12191       CheckExitEM(x, y);
12192     else if (element == EL_STEEL_EXIT_CLOSED)
12193       CheckExitSteel(x, y);
12194     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12195       CheckExitSteelEM(x, y);
12196     else if (element == EL_SP_EXIT_CLOSED)
12197       CheckExitSP(x, y);
12198     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12199              element == EL_EXPANDABLE_STEELWALL_GROWING)
12200       MauerWaechst(x, y);
12201     else if (element == EL_EXPANDABLE_WALL ||
12202              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12203              element == EL_EXPANDABLE_WALL_VERTICAL ||
12204              element == EL_EXPANDABLE_WALL_ANY ||
12205              element == EL_BD_EXPANDABLE_WALL)
12206       MauerAbleger(x, y);
12207     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12208              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12209              element == EL_EXPANDABLE_STEELWALL_ANY)
12210       MauerAblegerStahl(x, y);
12211     else if (element == EL_FLAMES)
12212       CheckForDragon(x, y);
12213     else if (element == EL_EXPLOSION)
12214       ; // drawing of correct explosion animation is handled separately
12215     else if (element == EL_ELEMENT_SNAPPING ||
12216              element == EL_DIAGONAL_SHRINKING ||
12217              element == EL_DIAGONAL_GROWING)
12218     {
12219       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12220
12221       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12222     }
12223     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12224       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12225
12226     if (IS_BELT_ACTIVE(element))
12227       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12228
12229     if (game.magic_wall_active)
12230     {
12231       int jx = local_player->jx, jy = local_player->jy;
12232
12233       // play the element sound at the position nearest to the player
12234       if ((element == EL_MAGIC_WALL_FULL ||
12235            element == EL_MAGIC_WALL_ACTIVE ||
12236            element == EL_MAGIC_WALL_EMPTYING ||
12237            element == EL_BD_MAGIC_WALL_FULL ||
12238            element == EL_BD_MAGIC_WALL_ACTIVE ||
12239            element == EL_BD_MAGIC_WALL_EMPTYING ||
12240            element == EL_DC_MAGIC_WALL_FULL ||
12241            element == EL_DC_MAGIC_WALL_ACTIVE ||
12242            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12243           ABS(x - jx) + ABS(y - jy) <
12244           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12245       {
12246         magic_wall_x = x;
12247         magic_wall_y = y;
12248       }
12249     }
12250   }
12251
12252 #if USE_NEW_AMOEBA_CODE
12253   // new experimental amoeba growth stuff
12254   if (!(FrameCounter % 8))
12255   {
12256     static unsigned int random = 1684108901;
12257
12258     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12259     {
12260       x = RND(lev_fieldx);
12261       y = RND(lev_fieldy);
12262       element = Tile[x][y];
12263
12264       if (!IS_PLAYER(x,y) &&
12265           (element == EL_EMPTY ||
12266            CAN_GROW_INTO(element) ||
12267            element == EL_QUICKSAND_EMPTY ||
12268            element == EL_QUICKSAND_FAST_EMPTY ||
12269            element == EL_ACID_SPLASH_LEFT ||
12270            element == EL_ACID_SPLASH_RIGHT))
12271       {
12272         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12273             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12274             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12275             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12276           Tile[x][y] = EL_AMOEBA_DROP;
12277       }
12278
12279       random = random * 129 + 1;
12280     }
12281   }
12282 #endif
12283
12284   game.explosions_delayed = FALSE;
12285
12286   SCAN_PLAYFIELD(x, y)
12287   {
12288     element = Tile[x][y];
12289
12290     if (ExplodeField[x][y])
12291       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12292     else if (element == EL_EXPLOSION)
12293       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12294
12295     ExplodeField[x][y] = EX_TYPE_NONE;
12296   }
12297
12298   game.explosions_delayed = TRUE;
12299
12300   if (game.magic_wall_active)
12301   {
12302     if (!(game.magic_wall_time_left % 4))
12303     {
12304       int element = Tile[magic_wall_x][magic_wall_y];
12305
12306       if (element == EL_BD_MAGIC_WALL_FULL ||
12307           element == EL_BD_MAGIC_WALL_ACTIVE ||
12308           element == EL_BD_MAGIC_WALL_EMPTYING)
12309         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12310       else if (element == EL_DC_MAGIC_WALL_FULL ||
12311                element == EL_DC_MAGIC_WALL_ACTIVE ||
12312                element == EL_DC_MAGIC_WALL_EMPTYING)
12313         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12314       else
12315         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12316     }
12317
12318     if (game.magic_wall_time_left > 0)
12319     {
12320       game.magic_wall_time_left--;
12321
12322       if (!game.magic_wall_time_left)
12323       {
12324         SCAN_PLAYFIELD(x, y)
12325         {
12326           element = Tile[x][y];
12327
12328           if (element == EL_MAGIC_WALL_ACTIVE ||
12329               element == EL_MAGIC_WALL_FULL)
12330           {
12331             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12332             TEST_DrawLevelField(x, y);
12333           }
12334           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12335                    element == EL_BD_MAGIC_WALL_FULL)
12336           {
12337             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12338             TEST_DrawLevelField(x, y);
12339           }
12340           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12341                    element == EL_DC_MAGIC_WALL_FULL)
12342           {
12343             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12344             TEST_DrawLevelField(x, y);
12345           }
12346         }
12347
12348         game.magic_wall_active = FALSE;
12349       }
12350     }
12351   }
12352
12353   if (game.light_time_left > 0)
12354   {
12355     game.light_time_left--;
12356
12357     if (game.light_time_left == 0)
12358       RedrawAllLightSwitchesAndInvisibleElements();
12359   }
12360
12361   if (game.timegate_time_left > 0)
12362   {
12363     game.timegate_time_left--;
12364
12365     if (game.timegate_time_left == 0)
12366       CloseAllOpenTimegates();
12367   }
12368
12369   if (game.lenses_time_left > 0)
12370   {
12371     game.lenses_time_left--;
12372
12373     if (game.lenses_time_left == 0)
12374       RedrawAllInvisibleElementsForLenses();
12375   }
12376
12377   if (game.magnify_time_left > 0)
12378   {
12379     game.magnify_time_left--;
12380
12381     if (game.magnify_time_left == 0)
12382       RedrawAllInvisibleElementsForMagnifier();
12383   }
12384
12385   for (i = 0; i < MAX_PLAYERS; i++)
12386   {
12387     struct PlayerInfo *player = &stored_player[i];
12388
12389     if (SHIELD_ON(player))
12390     {
12391       if (player->shield_deadly_time_left)
12392         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12393       else if (player->shield_normal_time_left)
12394         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12395     }
12396   }
12397
12398 #if USE_DELAYED_GFX_REDRAW
12399   SCAN_PLAYFIELD(x, y)
12400   {
12401     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12402     {
12403       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12404          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12405
12406       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12407         DrawLevelField(x, y);
12408
12409       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12410         DrawLevelFieldCrumbled(x, y);
12411
12412       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12413         DrawLevelFieldCrumbledNeighbours(x, y);
12414
12415       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12416         DrawTwinkleOnField(x, y);
12417     }
12418
12419     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12420   }
12421 #endif
12422
12423   DrawAllPlayers();
12424   PlayAllPlayersSound();
12425
12426   for (i = 0; i < MAX_PLAYERS; i++)
12427   {
12428     struct PlayerInfo *player = &stored_player[i];
12429
12430     if (player->show_envelope != 0 && (!player->active ||
12431                                        player->MovPos == 0))
12432     {
12433       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12434
12435       player->show_envelope = 0;
12436     }
12437   }
12438
12439   // use random number generator in every frame to make it less predictable
12440   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12441     RND(1);
12442
12443   mouse_action_last = mouse_action;
12444 }
12445
12446 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12447 {
12448   int min_x = x, min_y = y, max_x = x, max_y = y;
12449   int i;
12450
12451   for (i = 0; i < MAX_PLAYERS; i++)
12452   {
12453     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12454
12455     if (!stored_player[i].active || &stored_player[i] == player)
12456       continue;
12457
12458     min_x = MIN(min_x, jx);
12459     min_y = MIN(min_y, jy);
12460     max_x = MAX(max_x, jx);
12461     max_y = MAX(max_y, jy);
12462   }
12463
12464   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12465 }
12466
12467 static boolean AllPlayersInVisibleScreen(void)
12468 {
12469   int i;
12470
12471   for (i = 0; i < MAX_PLAYERS; i++)
12472   {
12473     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12474
12475     if (!stored_player[i].active)
12476       continue;
12477
12478     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12479       return FALSE;
12480   }
12481
12482   return TRUE;
12483 }
12484
12485 void ScrollLevel(int dx, int dy)
12486 {
12487   int scroll_offset = 2 * TILEX_VAR;
12488   int x, y;
12489
12490   BlitBitmap(drawto_field, drawto_field,
12491              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12492              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12493              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12494              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12495              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12496              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12497
12498   if (dx != 0)
12499   {
12500     x = (dx == 1 ? BX1 : BX2);
12501     for (y = BY1; y <= BY2; y++)
12502       DrawScreenField(x, y);
12503   }
12504
12505   if (dy != 0)
12506   {
12507     y = (dy == 1 ? BY1 : BY2);
12508     for (x = BX1; x <= BX2; x++)
12509       DrawScreenField(x, y);
12510   }
12511
12512   redraw_mask |= REDRAW_FIELD;
12513 }
12514
12515 static boolean canFallDown(struct PlayerInfo *player)
12516 {
12517   int jx = player->jx, jy = player->jy;
12518
12519   return (IN_LEV_FIELD(jx, jy + 1) &&
12520           (IS_FREE(jx, jy + 1) ||
12521            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12522           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12523           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12524 }
12525
12526 static boolean canPassField(int x, int y, int move_dir)
12527 {
12528   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12529   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12530   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12531   int nextx = x + dx;
12532   int nexty = y + dy;
12533   int element = Tile[x][y];
12534
12535   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12536           !CAN_MOVE(element) &&
12537           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12538           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12539           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12540 }
12541
12542 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12543 {
12544   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12545   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12546   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12547   int newx = x + dx;
12548   int newy = y + dy;
12549
12550   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12551           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12552           (IS_DIGGABLE(Tile[newx][newy]) ||
12553            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12554            canPassField(newx, newy, move_dir)));
12555 }
12556
12557 static void CheckGravityMovement(struct PlayerInfo *player)
12558 {
12559   if (player->gravity && !player->programmed_action)
12560   {
12561     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12562     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12563     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12564     int jx = player->jx, jy = player->jy;
12565     boolean player_is_moving_to_valid_field =
12566       (!player_is_snapping &&
12567        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12568         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12569     boolean player_can_fall_down = canFallDown(player);
12570
12571     if (player_can_fall_down &&
12572         !player_is_moving_to_valid_field)
12573       player->programmed_action = MV_DOWN;
12574   }
12575 }
12576
12577 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12578 {
12579   return CheckGravityMovement(player);
12580
12581   if (player->gravity && !player->programmed_action)
12582   {
12583     int jx = player->jx, jy = player->jy;
12584     boolean field_under_player_is_free =
12585       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12586     boolean player_is_standing_on_valid_field =
12587       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12588        (IS_WALKABLE(Tile[jx][jy]) &&
12589         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12590
12591     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12592       player->programmed_action = MV_DOWN;
12593   }
12594 }
12595
12596 /*
12597   MovePlayerOneStep()
12598   -----------------------------------------------------------------------------
12599   dx, dy:               direction (non-diagonal) to try to move the player to
12600   real_dx, real_dy:     direction as read from input device (can be diagonal)
12601 */
12602
12603 boolean MovePlayerOneStep(struct PlayerInfo *player,
12604                           int dx, int dy, int real_dx, int real_dy)
12605 {
12606   int jx = player->jx, jy = player->jy;
12607   int new_jx = jx + dx, new_jy = jy + dy;
12608   int can_move;
12609   boolean player_can_move = !player->cannot_move;
12610
12611   if (!player->active || (!dx && !dy))
12612     return MP_NO_ACTION;
12613
12614   player->MovDir = (dx < 0 ? MV_LEFT :
12615                     dx > 0 ? MV_RIGHT :
12616                     dy < 0 ? MV_UP :
12617                     dy > 0 ? MV_DOWN :  MV_NONE);
12618
12619   if (!IN_LEV_FIELD(new_jx, new_jy))
12620     return MP_NO_ACTION;
12621
12622   if (!player_can_move)
12623   {
12624     if (player->MovPos == 0)
12625     {
12626       player->is_moving = FALSE;
12627       player->is_digging = FALSE;
12628       player->is_collecting = FALSE;
12629       player->is_snapping = FALSE;
12630       player->is_pushing = FALSE;
12631     }
12632   }
12633
12634   if (!network.enabled && game.centered_player_nr == -1 &&
12635       !AllPlayersInSight(player, new_jx, new_jy))
12636     return MP_NO_ACTION;
12637
12638   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12639   if (can_move != MP_MOVING)
12640     return can_move;
12641
12642   // check if DigField() has caused relocation of the player
12643   if (player->jx != jx || player->jy != jy)
12644     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12645
12646   StorePlayer[jx][jy] = 0;
12647   player->last_jx = jx;
12648   player->last_jy = jy;
12649   player->jx = new_jx;
12650   player->jy = new_jy;
12651   StorePlayer[new_jx][new_jy] = player->element_nr;
12652
12653   if (player->move_delay_value_next != -1)
12654   {
12655     player->move_delay_value = player->move_delay_value_next;
12656     player->move_delay_value_next = -1;
12657   }
12658
12659   player->MovPos =
12660     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12661
12662   player->step_counter++;
12663
12664   PlayerVisit[jx][jy] = FrameCounter;
12665
12666   player->is_moving = TRUE;
12667
12668 #if 1
12669   // should better be called in MovePlayer(), but this breaks some tapes
12670   ScrollPlayer(player, SCROLL_INIT);
12671 #endif
12672
12673   return MP_MOVING;
12674 }
12675
12676 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12677 {
12678   int jx = player->jx, jy = player->jy;
12679   int old_jx = jx, old_jy = jy;
12680   int moved = MP_NO_ACTION;
12681
12682   if (!player->active)
12683     return FALSE;
12684
12685   if (!dx && !dy)
12686   {
12687     if (player->MovPos == 0)
12688     {
12689       player->is_moving = FALSE;
12690       player->is_digging = FALSE;
12691       player->is_collecting = FALSE;
12692       player->is_snapping = FALSE;
12693       player->is_pushing = FALSE;
12694     }
12695
12696     return FALSE;
12697   }
12698
12699   if (player->move_delay > 0)
12700     return FALSE;
12701
12702   player->move_delay = -1;              // set to "uninitialized" value
12703
12704   // store if player is automatically moved to next field
12705   player->is_auto_moving = (player->programmed_action != MV_NONE);
12706
12707   // remove the last programmed player action
12708   player->programmed_action = 0;
12709
12710   if (player->MovPos)
12711   {
12712     // should only happen if pre-1.2 tape recordings are played
12713     // this is only for backward compatibility
12714
12715     int original_move_delay_value = player->move_delay_value;
12716
12717 #if DEBUG
12718     Debug("game:playing:MovePlayer",
12719           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12720           tape.counter);
12721 #endif
12722
12723     // scroll remaining steps with finest movement resolution
12724     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12725
12726     while (player->MovPos)
12727     {
12728       ScrollPlayer(player, SCROLL_GO_ON);
12729       ScrollScreen(NULL, SCROLL_GO_ON);
12730
12731       AdvanceFrameAndPlayerCounters(player->index_nr);
12732
12733       DrawAllPlayers();
12734       BackToFront_WithFrameDelay(0);
12735     }
12736
12737     player->move_delay_value = original_move_delay_value;
12738   }
12739
12740   player->is_active = FALSE;
12741
12742   if (player->last_move_dir & MV_HORIZONTAL)
12743   {
12744     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12745       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12746   }
12747   else
12748   {
12749     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12750       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12751   }
12752
12753   if (!moved && !player->is_active)
12754   {
12755     player->is_moving = FALSE;
12756     player->is_digging = FALSE;
12757     player->is_collecting = FALSE;
12758     player->is_snapping = FALSE;
12759     player->is_pushing = FALSE;
12760   }
12761
12762   jx = player->jx;
12763   jy = player->jy;
12764
12765   if (moved & MP_MOVING && !ScreenMovPos &&
12766       (player->index_nr == game.centered_player_nr ||
12767        game.centered_player_nr == -1))
12768   {
12769     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12770
12771     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12772     {
12773       // actual player has left the screen -- scroll in that direction
12774       if (jx != old_jx)         // player has moved horizontally
12775         scroll_x += (jx - old_jx);
12776       else                      // player has moved vertically
12777         scroll_y += (jy - old_jy);
12778     }
12779     else
12780     {
12781       int offset_raw = game.scroll_delay_value;
12782
12783       if (jx != old_jx)         // player has moved horizontally
12784       {
12785         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12786         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12787         int new_scroll_x = jx - MIDPOSX + offset_x;
12788
12789         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12790             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12791           scroll_x = new_scroll_x;
12792
12793         // don't scroll over playfield boundaries
12794         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12795
12796         // don't scroll more than one field at a time
12797         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12798
12799         // don't scroll against the player's moving direction
12800         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12801             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12802           scroll_x = old_scroll_x;
12803       }
12804       else                      // player has moved vertically
12805       {
12806         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12807         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12808         int new_scroll_y = jy - MIDPOSY + offset_y;
12809
12810         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12811             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12812           scroll_y = new_scroll_y;
12813
12814         // don't scroll over playfield boundaries
12815         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12816
12817         // don't scroll more than one field at a time
12818         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12819
12820         // don't scroll against the player's moving direction
12821         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12822             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12823           scroll_y = old_scroll_y;
12824       }
12825     }
12826
12827     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12828     {
12829       if (!network.enabled && game.centered_player_nr == -1 &&
12830           !AllPlayersInVisibleScreen())
12831       {
12832         scroll_x = old_scroll_x;
12833         scroll_y = old_scroll_y;
12834       }
12835       else
12836       {
12837         ScrollScreen(player, SCROLL_INIT);
12838         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12839       }
12840     }
12841   }
12842
12843   player->StepFrame = 0;
12844
12845   if (moved & MP_MOVING)
12846   {
12847     if (old_jx != jx && old_jy == jy)
12848       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12849     else if (old_jx == jx && old_jy != jy)
12850       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12851
12852     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12853
12854     player->last_move_dir = player->MovDir;
12855     player->is_moving = TRUE;
12856     player->is_snapping = FALSE;
12857     player->is_switching = FALSE;
12858     player->is_dropping = FALSE;
12859     player->is_dropping_pressed = FALSE;
12860     player->drop_pressed_delay = 0;
12861
12862 #if 0
12863     // should better be called here than above, but this breaks some tapes
12864     ScrollPlayer(player, SCROLL_INIT);
12865 #endif
12866   }
12867   else
12868   {
12869     CheckGravityMovementWhenNotMoving(player);
12870
12871     player->is_moving = FALSE;
12872
12873     /* at this point, the player is allowed to move, but cannot move right now
12874        (e.g. because of something blocking the way) -- ensure that the player
12875        is also allowed to move in the next frame (in old versions before 3.1.1,
12876        the player was forced to wait again for eight frames before next try) */
12877
12878     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12879       player->move_delay = 0;   // allow direct movement in the next frame
12880   }
12881
12882   if (player->move_delay == -1)         // not yet initialized by DigField()
12883     player->move_delay = player->move_delay_value;
12884
12885   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12886   {
12887     TestIfPlayerTouchesBadThing(jx, jy);
12888     TestIfPlayerTouchesCustomElement(jx, jy);
12889   }
12890
12891   if (!player->active)
12892     RemovePlayer(player);
12893
12894   return moved;
12895 }
12896
12897 void ScrollPlayer(struct PlayerInfo *player, int mode)
12898 {
12899   int jx = player->jx, jy = player->jy;
12900   int last_jx = player->last_jx, last_jy = player->last_jy;
12901   int move_stepsize = TILEX / player->move_delay_value;
12902
12903   if (!player->active)
12904     return;
12905
12906   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12907     return;
12908
12909   if (mode == SCROLL_INIT)
12910   {
12911     player->actual_frame_counter = FrameCounter;
12912     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12913
12914     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12915         Tile[last_jx][last_jy] == EL_EMPTY)
12916     {
12917       int last_field_block_delay = 0;   // start with no blocking at all
12918       int block_delay_adjustment = player->block_delay_adjustment;
12919
12920       // if player blocks last field, add delay for exactly one move
12921       if (player->block_last_field)
12922       {
12923         last_field_block_delay += player->move_delay_value;
12924
12925         // when blocking enabled, prevent moving up despite gravity
12926         if (player->gravity && player->MovDir == MV_UP)
12927           block_delay_adjustment = -1;
12928       }
12929
12930       // add block delay adjustment (also possible when not blocking)
12931       last_field_block_delay += block_delay_adjustment;
12932
12933       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12934       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12935     }
12936
12937     if (player->MovPos != 0)    // player has not yet reached destination
12938       return;
12939   }
12940   else if (!FrameReached(&player->actual_frame_counter, 1))
12941     return;
12942
12943   if (player->MovPos != 0)
12944   {
12945     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12946     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12947
12948     // before DrawPlayer() to draw correct player graphic for this case
12949     if (player->MovPos == 0)
12950       CheckGravityMovement(player);
12951   }
12952
12953   if (player->MovPos == 0)      // player reached destination field
12954   {
12955     if (player->move_delay_reset_counter > 0)
12956     {
12957       player->move_delay_reset_counter--;
12958
12959       if (player->move_delay_reset_counter == 0)
12960       {
12961         // continue with normal speed after quickly moving through gate
12962         HALVE_PLAYER_SPEED(player);
12963
12964         // be able to make the next move without delay
12965         player->move_delay = 0;
12966       }
12967     }
12968
12969     player->last_jx = jx;
12970     player->last_jy = jy;
12971
12972     if (Tile[jx][jy] == EL_EXIT_OPEN ||
12973         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
12974         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
12975         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
12976         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12977         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12978         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
12979         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12980     {
12981       ExitPlayer(player);
12982
12983       if (game.players_still_needed == 0 &&
12984           (game.friends_still_needed == 0 ||
12985            IS_SP_ELEMENT(Tile[jx][jy])))
12986         LevelSolved();
12987     }
12988
12989     // this breaks one level: "machine", level 000
12990     {
12991       int move_direction = player->MovDir;
12992       int enter_side = MV_DIR_OPPOSITE(move_direction);
12993       int leave_side = move_direction;
12994       int old_jx = last_jx;
12995       int old_jy = last_jy;
12996       int old_element = Tile[old_jx][old_jy];
12997       int new_element = Tile[jx][jy];
12998
12999       if (IS_CUSTOM_ELEMENT(old_element))
13000         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13001                                    CE_LEFT_BY_PLAYER,
13002                                    player->index_bit, leave_side);
13003
13004       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13005                                           CE_PLAYER_LEAVES_X,
13006                                           player->index_bit, leave_side);
13007
13008       if (IS_CUSTOM_ELEMENT(new_element))
13009         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13010                                    player->index_bit, enter_side);
13011
13012       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13013                                           CE_PLAYER_ENTERS_X,
13014                                           player->index_bit, enter_side);
13015
13016       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13017                                         CE_MOVE_OF_X, move_direction);
13018     }
13019
13020     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13021     {
13022       TestIfPlayerTouchesBadThing(jx, jy);
13023       TestIfPlayerTouchesCustomElement(jx, jy);
13024
13025       /* needed because pushed element has not yet reached its destination,
13026          so it would trigger a change event at its previous field location */
13027       if (!player->is_pushing)
13028         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13029
13030       if (!player->active)
13031         RemovePlayer(player);
13032     }
13033
13034     if (!game.LevelSolved && level.use_step_counter)
13035     {
13036       int i;
13037
13038       TimePlayed++;
13039
13040       if (TimeLeft > 0)
13041       {
13042         TimeLeft--;
13043
13044         if (TimeLeft <= 10 && setup.time_limit)
13045           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13046
13047         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13048
13049         DisplayGameControlValues();
13050
13051         if (!TimeLeft && setup.time_limit)
13052           for (i = 0; i < MAX_PLAYERS; i++)
13053             KillPlayer(&stored_player[i]);
13054       }
13055       else if (game.no_time_limit && !game.all_players_gone)
13056       {
13057         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13058
13059         DisplayGameControlValues();
13060       }
13061     }
13062
13063     if (tape.single_step && tape.recording && !tape.pausing &&
13064         !player->programmed_action)
13065       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13066
13067     if (!player->programmed_action)
13068       CheckSaveEngineSnapshot(player);
13069   }
13070 }
13071
13072 void ScrollScreen(struct PlayerInfo *player, int mode)
13073 {
13074   static unsigned int screen_frame_counter = 0;
13075
13076   if (mode == SCROLL_INIT)
13077   {
13078     // set scrolling step size according to actual player's moving speed
13079     ScrollStepSize = TILEX / player->move_delay_value;
13080
13081     screen_frame_counter = FrameCounter;
13082     ScreenMovDir = player->MovDir;
13083     ScreenMovPos = player->MovPos;
13084     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13085     return;
13086   }
13087   else if (!FrameReached(&screen_frame_counter, 1))
13088     return;
13089
13090   if (ScreenMovPos)
13091   {
13092     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13093     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13094     redraw_mask |= REDRAW_FIELD;
13095   }
13096   else
13097     ScreenMovDir = MV_NONE;
13098 }
13099
13100 void TestIfPlayerTouchesCustomElement(int x, int y)
13101 {
13102   static int xy[4][2] =
13103   {
13104     { 0, -1 },
13105     { -1, 0 },
13106     { +1, 0 },
13107     { 0, +1 }
13108   };
13109   static int trigger_sides[4][2] =
13110   {
13111     // center side       border side
13112     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13113     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13114     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13115     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13116   };
13117   static int touch_dir[4] =
13118   {
13119     MV_LEFT | MV_RIGHT,
13120     MV_UP   | MV_DOWN,
13121     MV_UP   | MV_DOWN,
13122     MV_LEFT | MV_RIGHT
13123   };
13124   int center_element = Tile[x][y];      // should always be non-moving!
13125   int i;
13126
13127   for (i = 0; i < NUM_DIRECTIONS; i++)
13128   {
13129     int xx = x + xy[i][0];
13130     int yy = y + xy[i][1];
13131     int center_side = trigger_sides[i][0];
13132     int border_side = trigger_sides[i][1];
13133     int border_element;
13134
13135     if (!IN_LEV_FIELD(xx, yy))
13136       continue;
13137
13138     if (IS_PLAYER(x, y))                // player found at center element
13139     {
13140       struct PlayerInfo *player = PLAYERINFO(x, y);
13141
13142       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13143         border_element = Tile[xx][yy];          // may be moving!
13144       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13145         border_element = Tile[xx][yy];
13146       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13147         border_element = MovingOrBlocked2Element(xx, yy);
13148       else
13149         continue;               // center and border element do not touch
13150
13151       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13152                                  player->index_bit, border_side);
13153       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13154                                           CE_PLAYER_TOUCHES_X,
13155                                           player->index_bit, border_side);
13156
13157       {
13158         /* use player element that is initially defined in the level playfield,
13159            not the player element that corresponds to the runtime player number
13160            (example: a level that contains EL_PLAYER_3 as the only player would
13161            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13162         int player_element = PLAYERINFO(x, y)->initial_element;
13163
13164         CheckElementChangeBySide(xx, yy, border_element, player_element,
13165                                  CE_TOUCHING_X, border_side);
13166       }
13167     }
13168     else if (IS_PLAYER(xx, yy))         // player found at border element
13169     {
13170       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13171
13172       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13173       {
13174         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13175           continue;             // center and border element do not touch
13176       }
13177
13178       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13179                                  player->index_bit, center_side);
13180       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13181                                           CE_PLAYER_TOUCHES_X,
13182                                           player->index_bit, center_side);
13183
13184       {
13185         /* use player element that is initially defined in the level playfield,
13186            not the player element that corresponds to the runtime player number
13187            (example: a level that contains EL_PLAYER_3 as the only player would
13188            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13189         int player_element = PLAYERINFO(xx, yy)->initial_element;
13190
13191         CheckElementChangeBySide(x, y, center_element, player_element,
13192                                  CE_TOUCHING_X, center_side);
13193       }
13194
13195       break;
13196     }
13197   }
13198 }
13199
13200 void TestIfElementTouchesCustomElement(int x, int y)
13201 {
13202   static int xy[4][2] =
13203   {
13204     { 0, -1 },
13205     { -1, 0 },
13206     { +1, 0 },
13207     { 0, +1 }
13208   };
13209   static int trigger_sides[4][2] =
13210   {
13211     // center side      border side
13212     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13213     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13214     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13215     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13216   };
13217   static int touch_dir[4] =
13218   {
13219     MV_LEFT | MV_RIGHT,
13220     MV_UP   | MV_DOWN,
13221     MV_UP   | MV_DOWN,
13222     MV_LEFT | MV_RIGHT
13223   };
13224   boolean change_center_element = FALSE;
13225   int center_element = Tile[x][y];      // should always be non-moving!
13226   int border_element_old[NUM_DIRECTIONS];
13227   int i;
13228
13229   for (i = 0; i < NUM_DIRECTIONS; i++)
13230   {
13231     int xx = x + xy[i][0];
13232     int yy = y + xy[i][1];
13233     int border_element;
13234
13235     border_element_old[i] = -1;
13236
13237     if (!IN_LEV_FIELD(xx, yy))
13238       continue;
13239
13240     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13241       border_element = Tile[xx][yy];    // may be moving!
13242     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13243       border_element = Tile[xx][yy];
13244     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13245       border_element = MovingOrBlocked2Element(xx, yy);
13246     else
13247       continue;                 // center and border element do not touch
13248
13249     border_element_old[i] = border_element;
13250   }
13251
13252   for (i = 0; i < NUM_DIRECTIONS; i++)
13253   {
13254     int xx = x + xy[i][0];
13255     int yy = y + xy[i][1];
13256     int center_side = trigger_sides[i][0];
13257     int border_element = border_element_old[i];
13258
13259     if (border_element == -1)
13260       continue;
13261
13262     // check for change of border element
13263     CheckElementChangeBySide(xx, yy, border_element, center_element,
13264                              CE_TOUCHING_X, center_side);
13265
13266     // (center element cannot be player, so we dont have to check this here)
13267   }
13268
13269   for (i = 0; i < NUM_DIRECTIONS; i++)
13270   {
13271     int xx = x + xy[i][0];
13272     int yy = y + xy[i][1];
13273     int border_side = trigger_sides[i][1];
13274     int border_element = border_element_old[i];
13275
13276     if (border_element == -1)
13277       continue;
13278
13279     // check for change of center element (but change it only once)
13280     if (!change_center_element)
13281       change_center_element =
13282         CheckElementChangeBySide(x, y, center_element, border_element,
13283                                  CE_TOUCHING_X, border_side);
13284
13285     if (IS_PLAYER(xx, yy))
13286     {
13287       /* use player element that is initially defined in the level playfield,
13288          not the player element that corresponds to the runtime player number
13289          (example: a level that contains EL_PLAYER_3 as the only player would
13290          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13291       int player_element = PLAYERINFO(xx, yy)->initial_element;
13292
13293       CheckElementChangeBySide(x, y, center_element, player_element,
13294                                CE_TOUCHING_X, border_side);
13295     }
13296   }
13297 }
13298
13299 void TestIfElementHitsCustomElement(int x, int y, int direction)
13300 {
13301   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13302   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13303   int hitx = x + dx, hity = y + dy;
13304   int hitting_element = Tile[x][y];
13305   int touched_element;
13306
13307   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13308     return;
13309
13310   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13311                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13312
13313   if (IN_LEV_FIELD(hitx, hity))
13314   {
13315     int opposite_direction = MV_DIR_OPPOSITE(direction);
13316     int hitting_side = direction;
13317     int touched_side = opposite_direction;
13318     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13319                           MovDir[hitx][hity] != direction ||
13320                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13321
13322     object_hit = TRUE;
13323
13324     if (object_hit)
13325     {
13326       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13327                                CE_HITTING_X, touched_side);
13328
13329       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13330                                CE_HIT_BY_X, hitting_side);
13331
13332       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13333                                CE_HIT_BY_SOMETHING, opposite_direction);
13334
13335       if (IS_PLAYER(hitx, hity))
13336       {
13337         /* use player element that is initially defined in the level playfield,
13338            not the player element that corresponds to the runtime player number
13339            (example: a level that contains EL_PLAYER_3 as the only player would
13340            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13341         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13342
13343         CheckElementChangeBySide(x, y, hitting_element, player_element,
13344                                  CE_HITTING_X, touched_side);
13345       }
13346     }
13347   }
13348
13349   // "hitting something" is also true when hitting the playfield border
13350   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13351                            CE_HITTING_SOMETHING, direction);
13352 }
13353
13354 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13355 {
13356   int i, kill_x = -1, kill_y = -1;
13357
13358   int bad_element = -1;
13359   static int test_xy[4][2] =
13360   {
13361     { 0, -1 },
13362     { -1, 0 },
13363     { +1, 0 },
13364     { 0, +1 }
13365   };
13366   static int test_dir[4] =
13367   {
13368     MV_UP,
13369     MV_LEFT,
13370     MV_RIGHT,
13371     MV_DOWN
13372   };
13373
13374   for (i = 0; i < NUM_DIRECTIONS; i++)
13375   {
13376     int test_x, test_y, test_move_dir, test_element;
13377
13378     test_x = good_x + test_xy[i][0];
13379     test_y = good_y + test_xy[i][1];
13380
13381     if (!IN_LEV_FIELD(test_x, test_y))
13382       continue;
13383
13384     test_move_dir =
13385       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13386
13387     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13388
13389     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13390        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13391     */
13392     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13393         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13394     {
13395       kill_x = test_x;
13396       kill_y = test_y;
13397       bad_element = test_element;
13398
13399       break;
13400     }
13401   }
13402
13403   if (kill_x != -1 || kill_y != -1)
13404   {
13405     if (IS_PLAYER(good_x, good_y))
13406     {
13407       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13408
13409       if (player->shield_deadly_time_left > 0 &&
13410           !IS_INDESTRUCTIBLE(bad_element))
13411         Bang(kill_x, kill_y);
13412       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13413         KillPlayer(player);
13414     }
13415     else
13416       Bang(good_x, good_y);
13417   }
13418 }
13419
13420 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13421 {
13422   int i, kill_x = -1, kill_y = -1;
13423   int bad_element = Tile[bad_x][bad_y];
13424   static int test_xy[4][2] =
13425   {
13426     { 0, -1 },
13427     { -1, 0 },
13428     { +1, 0 },
13429     { 0, +1 }
13430   };
13431   static int touch_dir[4] =
13432   {
13433     MV_LEFT | MV_RIGHT,
13434     MV_UP   | MV_DOWN,
13435     MV_UP   | MV_DOWN,
13436     MV_LEFT | MV_RIGHT
13437   };
13438   static int test_dir[4] =
13439   {
13440     MV_UP,
13441     MV_LEFT,
13442     MV_RIGHT,
13443     MV_DOWN
13444   };
13445
13446   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13447     return;
13448
13449   for (i = 0; i < NUM_DIRECTIONS; i++)
13450   {
13451     int test_x, test_y, test_move_dir, test_element;
13452
13453     test_x = bad_x + test_xy[i][0];
13454     test_y = bad_y + test_xy[i][1];
13455
13456     if (!IN_LEV_FIELD(test_x, test_y))
13457       continue;
13458
13459     test_move_dir =
13460       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13461
13462     test_element = Tile[test_x][test_y];
13463
13464     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13465        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13466     */
13467     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13468         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13469     {
13470       // good thing is player or penguin that does not move away
13471       if (IS_PLAYER(test_x, test_y))
13472       {
13473         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13474
13475         if (bad_element == EL_ROBOT && player->is_moving)
13476           continue;     // robot does not kill player if he is moving
13477
13478         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13479         {
13480           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13481             continue;           // center and border element do not touch
13482         }
13483
13484         kill_x = test_x;
13485         kill_y = test_y;
13486
13487         break;
13488       }
13489       else if (test_element == EL_PENGUIN)
13490       {
13491         kill_x = test_x;
13492         kill_y = test_y;
13493
13494         break;
13495       }
13496     }
13497   }
13498
13499   if (kill_x != -1 || kill_y != -1)
13500   {
13501     if (IS_PLAYER(kill_x, kill_y))
13502     {
13503       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13504
13505       if (player->shield_deadly_time_left > 0 &&
13506           !IS_INDESTRUCTIBLE(bad_element))
13507         Bang(bad_x, bad_y);
13508       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13509         KillPlayer(player);
13510     }
13511     else
13512       Bang(kill_x, kill_y);
13513   }
13514 }
13515
13516 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13517 {
13518   int bad_element = Tile[bad_x][bad_y];
13519   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13520   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13521   int test_x = bad_x + dx, test_y = bad_y + dy;
13522   int test_move_dir, test_element;
13523   int kill_x = -1, kill_y = -1;
13524
13525   if (!IN_LEV_FIELD(test_x, test_y))
13526     return;
13527
13528   test_move_dir =
13529     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13530
13531   test_element = Tile[test_x][test_y];
13532
13533   if (test_move_dir != bad_move_dir)
13534   {
13535     // good thing can be player or penguin that does not move away
13536     if (IS_PLAYER(test_x, test_y))
13537     {
13538       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13539
13540       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13541          player as being hit when he is moving towards the bad thing, because
13542          the "get hit by" condition would be lost after the player stops) */
13543       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13544         return;         // player moves away from bad thing
13545
13546       kill_x = test_x;
13547       kill_y = test_y;
13548     }
13549     else if (test_element == EL_PENGUIN)
13550     {
13551       kill_x = test_x;
13552       kill_y = test_y;
13553     }
13554   }
13555
13556   if (kill_x != -1 || kill_y != -1)
13557   {
13558     if (IS_PLAYER(kill_x, kill_y))
13559     {
13560       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13561
13562       if (player->shield_deadly_time_left > 0 &&
13563           !IS_INDESTRUCTIBLE(bad_element))
13564         Bang(bad_x, bad_y);
13565       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13566         KillPlayer(player);
13567     }
13568     else
13569       Bang(kill_x, kill_y);
13570   }
13571 }
13572
13573 void TestIfPlayerTouchesBadThing(int x, int y)
13574 {
13575   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13576 }
13577
13578 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13579 {
13580   TestIfGoodThingHitsBadThing(x, y, move_dir);
13581 }
13582
13583 void TestIfBadThingTouchesPlayer(int x, int y)
13584 {
13585   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13586 }
13587
13588 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13589 {
13590   TestIfBadThingHitsGoodThing(x, y, move_dir);
13591 }
13592
13593 void TestIfFriendTouchesBadThing(int x, int y)
13594 {
13595   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13596 }
13597
13598 void TestIfBadThingTouchesFriend(int x, int y)
13599 {
13600   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13601 }
13602
13603 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13604 {
13605   int i, kill_x = bad_x, kill_y = bad_y;
13606   static int xy[4][2] =
13607   {
13608     { 0, -1 },
13609     { -1, 0 },
13610     { +1, 0 },
13611     { 0, +1 }
13612   };
13613
13614   for (i = 0; i < NUM_DIRECTIONS; i++)
13615   {
13616     int x, y, element;
13617
13618     x = bad_x + xy[i][0];
13619     y = bad_y + xy[i][1];
13620     if (!IN_LEV_FIELD(x, y))
13621       continue;
13622
13623     element = Tile[x][y];
13624     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13625         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13626     {
13627       kill_x = x;
13628       kill_y = y;
13629       break;
13630     }
13631   }
13632
13633   if (kill_x != bad_x || kill_y != bad_y)
13634     Bang(bad_x, bad_y);
13635 }
13636
13637 void KillPlayer(struct PlayerInfo *player)
13638 {
13639   int jx = player->jx, jy = player->jy;
13640
13641   if (!player->active)
13642     return;
13643
13644 #if 0
13645   Debug("game:playing:KillPlayer",
13646         "0: killed == %d, active == %d, reanimated == %d",
13647         player->killed, player->active, player->reanimated);
13648 #endif
13649
13650   /* the following code was introduced to prevent an infinite loop when calling
13651      -> Bang()
13652      -> CheckTriggeredElementChangeExt()
13653      -> ExecuteCustomElementAction()
13654      -> KillPlayer()
13655      -> (infinitely repeating the above sequence of function calls)
13656      which occurs when killing the player while having a CE with the setting
13657      "kill player X when explosion of <player X>"; the solution using a new
13658      field "player->killed" was chosen for backwards compatibility, although
13659      clever use of the fields "player->active" etc. would probably also work */
13660 #if 1
13661   if (player->killed)
13662     return;
13663 #endif
13664
13665   player->killed = TRUE;
13666
13667   // remove accessible field at the player's position
13668   Tile[jx][jy] = EL_EMPTY;
13669
13670   // deactivate shield (else Bang()/Explode() would not work right)
13671   player->shield_normal_time_left = 0;
13672   player->shield_deadly_time_left = 0;
13673
13674 #if 0
13675   Debug("game:playing:KillPlayer",
13676         "1: killed == %d, active == %d, reanimated == %d",
13677         player->killed, player->active, player->reanimated);
13678 #endif
13679
13680   Bang(jx, jy);
13681
13682 #if 0
13683   Debug("game:playing:KillPlayer",
13684         "2: killed == %d, active == %d, reanimated == %d",
13685         player->killed, player->active, player->reanimated);
13686 #endif
13687
13688   if (player->reanimated)       // killed player may have been reanimated
13689     player->killed = player->reanimated = FALSE;
13690   else
13691     BuryPlayer(player);
13692 }
13693
13694 static void KillPlayerUnlessEnemyProtected(int x, int y)
13695 {
13696   if (!PLAYER_ENEMY_PROTECTED(x, y))
13697     KillPlayer(PLAYERINFO(x, y));
13698 }
13699
13700 static void KillPlayerUnlessExplosionProtected(int x, int y)
13701 {
13702   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13703     KillPlayer(PLAYERINFO(x, y));
13704 }
13705
13706 void BuryPlayer(struct PlayerInfo *player)
13707 {
13708   int jx = player->jx, jy = player->jy;
13709
13710   if (!player->active)
13711     return;
13712
13713   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13714   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13715
13716   RemovePlayer(player);
13717
13718   player->buried = TRUE;
13719
13720   if (game.all_players_gone)
13721     game.GameOver = TRUE;
13722 }
13723
13724 void RemovePlayer(struct PlayerInfo *player)
13725 {
13726   int jx = player->jx, jy = player->jy;
13727   int i, found = FALSE;
13728
13729   player->present = FALSE;
13730   player->active = FALSE;
13731
13732   // required for some CE actions (even if the player is not active anymore)
13733   player->MovPos = 0;
13734
13735   if (!ExplodeField[jx][jy])
13736     StorePlayer[jx][jy] = 0;
13737
13738   if (player->is_moving)
13739     TEST_DrawLevelField(player->last_jx, player->last_jy);
13740
13741   for (i = 0; i < MAX_PLAYERS; i++)
13742     if (stored_player[i].active)
13743       found = TRUE;
13744
13745   if (!found)
13746   {
13747     game.all_players_gone = TRUE;
13748     game.GameOver = TRUE;
13749   }
13750
13751   game.exit_x = game.robot_wheel_x = jx;
13752   game.exit_y = game.robot_wheel_y = jy;
13753 }
13754
13755 void ExitPlayer(struct PlayerInfo *player)
13756 {
13757   DrawPlayer(player);   // needed here only to cleanup last field
13758   RemovePlayer(player);
13759
13760   if (game.players_still_needed > 0)
13761     game.players_still_needed--;
13762 }
13763
13764 static void setFieldForSnapping(int x, int y, int element, int direction)
13765 {
13766   struct ElementInfo *ei = &element_info[element];
13767   int direction_bit = MV_DIR_TO_BIT(direction);
13768   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13769   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13770                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13771
13772   Tile[x][y] = EL_ELEMENT_SNAPPING;
13773   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13774
13775   ResetGfxAnimation(x, y);
13776
13777   GfxElement[x][y] = element;
13778   GfxAction[x][y] = action;
13779   GfxDir[x][y] = direction;
13780   GfxFrame[x][y] = -1;
13781 }
13782
13783 /*
13784   =============================================================================
13785   checkDiagonalPushing()
13786   -----------------------------------------------------------------------------
13787   check if diagonal input device direction results in pushing of object
13788   (by checking if the alternative direction is walkable, diggable, ...)
13789   =============================================================================
13790 */
13791
13792 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13793                                     int x, int y, int real_dx, int real_dy)
13794 {
13795   int jx, jy, dx, dy, xx, yy;
13796
13797   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13798     return TRUE;
13799
13800   // diagonal direction: check alternative direction
13801   jx = player->jx;
13802   jy = player->jy;
13803   dx = x - jx;
13804   dy = y - jy;
13805   xx = jx + (dx == 0 ? real_dx : 0);
13806   yy = jy + (dy == 0 ? real_dy : 0);
13807
13808   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13809 }
13810
13811 /*
13812   =============================================================================
13813   DigField()
13814   -----------------------------------------------------------------------------
13815   x, y:                 field next to player (non-diagonal) to try to dig to
13816   real_dx, real_dy:     direction as read from input device (can be diagonal)
13817   =============================================================================
13818 */
13819
13820 static int DigField(struct PlayerInfo *player,
13821                     int oldx, int oldy, int x, int y,
13822                     int real_dx, int real_dy, int mode)
13823 {
13824   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13825   boolean player_was_pushing = player->is_pushing;
13826   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13827   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13828   int jx = oldx, jy = oldy;
13829   int dx = x - jx, dy = y - jy;
13830   int nextx = x + dx, nexty = y + dy;
13831   int move_direction = (dx == -1 ? MV_LEFT  :
13832                         dx == +1 ? MV_RIGHT :
13833                         dy == -1 ? MV_UP    :
13834                         dy == +1 ? MV_DOWN  : MV_NONE);
13835   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13836   int dig_side = MV_DIR_OPPOSITE(move_direction);
13837   int old_element = Tile[jx][jy];
13838   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13839   int collect_count;
13840
13841   if (is_player)                // function can also be called by EL_PENGUIN
13842   {
13843     if (player->MovPos == 0)
13844     {
13845       player->is_digging = FALSE;
13846       player->is_collecting = FALSE;
13847     }
13848
13849     if (player->MovPos == 0)    // last pushing move finished
13850       player->is_pushing = FALSE;
13851
13852     if (mode == DF_NO_PUSH)     // player just stopped pushing
13853     {
13854       player->is_switching = FALSE;
13855       player->push_delay = -1;
13856
13857       return MP_NO_ACTION;
13858     }
13859   }
13860
13861   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13862     old_element = Back[jx][jy];
13863
13864   // in case of element dropped at player position, check background
13865   else if (Back[jx][jy] != EL_EMPTY &&
13866            game.engine_version >= VERSION_IDENT(2,2,0,0))
13867     old_element = Back[jx][jy];
13868
13869   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13870     return MP_NO_ACTION;        // field has no opening in this direction
13871
13872   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13873     return MP_NO_ACTION;        // field has no opening in this direction
13874
13875   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13876   {
13877     SplashAcid(x, y);
13878
13879     Tile[jx][jy] = player->artwork_element;
13880     InitMovingField(jx, jy, MV_DOWN);
13881     Store[jx][jy] = EL_ACID;
13882     ContinueMoving(jx, jy);
13883     BuryPlayer(player);
13884
13885     return MP_DONT_RUN_INTO;
13886   }
13887
13888   if (player_can_move && DONT_RUN_INTO(element))
13889   {
13890     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13891
13892     return MP_DONT_RUN_INTO;
13893   }
13894
13895   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13896     return MP_NO_ACTION;
13897
13898   collect_count = element_info[element].collect_count_initial;
13899
13900   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13901     return MP_NO_ACTION;
13902
13903   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13904     player_can_move = player_can_move_or_snap;
13905
13906   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13907       game.engine_version >= VERSION_IDENT(2,2,0,0))
13908   {
13909     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13910                                player->index_bit, dig_side);
13911     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13912                                         player->index_bit, dig_side);
13913
13914     if (element == EL_DC_LANDMINE)
13915       Bang(x, y);
13916
13917     if (Tile[x][y] != element)          // field changed by snapping
13918       return MP_ACTION;
13919
13920     return MP_NO_ACTION;
13921   }
13922
13923   if (player->gravity && is_player && !player->is_auto_moving &&
13924       canFallDown(player) && move_direction != MV_DOWN &&
13925       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13926     return MP_NO_ACTION;        // player cannot walk here due to gravity
13927
13928   if (player_can_move &&
13929       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13930   {
13931     int sound_element = SND_ELEMENT(element);
13932     int sound_action = ACTION_WALKING;
13933
13934     if (IS_RND_GATE(element))
13935     {
13936       if (!player->key[RND_GATE_NR(element)])
13937         return MP_NO_ACTION;
13938     }
13939     else if (IS_RND_GATE_GRAY(element))
13940     {
13941       if (!player->key[RND_GATE_GRAY_NR(element)])
13942         return MP_NO_ACTION;
13943     }
13944     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13945     {
13946       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13947         return MP_NO_ACTION;
13948     }
13949     else if (element == EL_EXIT_OPEN ||
13950              element == EL_EM_EXIT_OPEN ||
13951              element == EL_EM_EXIT_OPENING ||
13952              element == EL_STEEL_EXIT_OPEN ||
13953              element == EL_EM_STEEL_EXIT_OPEN ||
13954              element == EL_EM_STEEL_EXIT_OPENING ||
13955              element == EL_SP_EXIT_OPEN ||
13956              element == EL_SP_EXIT_OPENING)
13957     {
13958       sound_action = ACTION_PASSING;    // player is passing exit
13959     }
13960     else if (element == EL_EMPTY)
13961     {
13962       sound_action = ACTION_MOVING;             // nothing to walk on
13963     }
13964
13965     // play sound from background or player, whatever is available
13966     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13967       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13968     else
13969       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13970   }
13971   else if (player_can_move &&
13972            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13973   {
13974     if (!ACCESS_FROM(element, opposite_direction))
13975       return MP_NO_ACTION;      // field not accessible from this direction
13976
13977     if (CAN_MOVE(element))      // only fixed elements can be passed!
13978       return MP_NO_ACTION;
13979
13980     if (IS_EM_GATE(element))
13981     {
13982       if (!player->key[EM_GATE_NR(element)])
13983         return MP_NO_ACTION;
13984     }
13985     else if (IS_EM_GATE_GRAY(element))
13986     {
13987       if (!player->key[EM_GATE_GRAY_NR(element)])
13988         return MP_NO_ACTION;
13989     }
13990     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13991     {
13992       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13993         return MP_NO_ACTION;
13994     }
13995     else if (IS_EMC_GATE(element))
13996     {
13997       if (!player->key[EMC_GATE_NR(element)])
13998         return MP_NO_ACTION;
13999     }
14000     else if (IS_EMC_GATE_GRAY(element))
14001     {
14002       if (!player->key[EMC_GATE_GRAY_NR(element)])
14003         return MP_NO_ACTION;
14004     }
14005     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14006     {
14007       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14008         return MP_NO_ACTION;
14009     }
14010     else if (element == EL_DC_GATE_WHITE ||
14011              element == EL_DC_GATE_WHITE_GRAY ||
14012              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14013     {
14014       if (player->num_white_keys == 0)
14015         return MP_NO_ACTION;
14016
14017       player->num_white_keys--;
14018     }
14019     else if (IS_SP_PORT(element))
14020     {
14021       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14022           element == EL_SP_GRAVITY_PORT_RIGHT ||
14023           element == EL_SP_GRAVITY_PORT_UP ||
14024           element == EL_SP_GRAVITY_PORT_DOWN)
14025         player->gravity = !player->gravity;
14026       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14027                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14028                element == EL_SP_GRAVITY_ON_PORT_UP ||
14029                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14030         player->gravity = TRUE;
14031       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14032                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14033                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14034                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14035         player->gravity = FALSE;
14036     }
14037
14038     // automatically move to the next field with double speed
14039     player->programmed_action = move_direction;
14040
14041     if (player->move_delay_reset_counter == 0)
14042     {
14043       player->move_delay_reset_counter = 2;     // two double speed steps
14044
14045       DOUBLE_PLAYER_SPEED(player);
14046     }
14047
14048     PlayLevelSoundAction(x, y, ACTION_PASSING);
14049   }
14050   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14051   {
14052     RemoveField(x, y);
14053
14054     if (mode != DF_SNAP)
14055     {
14056       GfxElement[x][y] = GFX_ELEMENT(element);
14057       player->is_digging = TRUE;
14058     }
14059
14060     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14061
14062     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14063                                         player->index_bit, dig_side);
14064
14065     // if digging triggered player relocation, finish digging tile
14066     if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14067       setFieldForSnapping(x, y, element, move_direction);
14068
14069     if (mode == DF_SNAP)
14070     {
14071       if (level.block_snap_field)
14072         setFieldForSnapping(x, y, element, move_direction);
14073       else
14074         TestIfElementTouchesCustomElement(x, y);        // for empty space
14075
14076       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14077                                           player->index_bit, dig_side);
14078     }
14079   }
14080   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14081   {
14082     RemoveField(x, y);
14083
14084     if (is_player && mode != DF_SNAP)
14085     {
14086       GfxElement[x][y] = element;
14087       player->is_collecting = TRUE;
14088     }
14089
14090     if (element == EL_SPEED_PILL)
14091     {
14092       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14093     }
14094     else if (element == EL_EXTRA_TIME && level.time > 0)
14095     {
14096       TimeLeft += level.extra_time;
14097
14098       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14099
14100       DisplayGameControlValues();
14101     }
14102     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14103     {
14104       player->shield_normal_time_left += level.shield_normal_time;
14105       if (element == EL_SHIELD_DEADLY)
14106         player->shield_deadly_time_left += level.shield_deadly_time;
14107     }
14108     else if (element == EL_DYNAMITE ||
14109              element == EL_EM_DYNAMITE ||
14110              element == EL_SP_DISK_RED)
14111     {
14112       if (player->inventory_size < MAX_INVENTORY_SIZE)
14113         player->inventory_element[player->inventory_size++] = element;
14114
14115       DrawGameDoorValues();
14116     }
14117     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14118     {
14119       player->dynabomb_count++;
14120       player->dynabombs_left++;
14121     }
14122     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14123     {
14124       player->dynabomb_size++;
14125     }
14126     else if (element == EL_DYNABOMB_INCREASE_POWER)
14127     {
14128       player->dynabomb_xl = TRUE;
14129     }
14130     else if (IS_KEY(element))
14131     {
14132       player->key[KEY_NR(element)] = TRUE;
14133
14134       DrawGameDoorValues();
14135     }
14136     else if (element == EL_DC_KEY_WHITE)
14137     {
14138       player->num_white_keys++;
14139
14140       // display white keys?
14141       // DrawGameDoorValues();
14142     }
14143     else if (IS_ENVELOPE(element))
14144     {
14145       player->show_envelope = element;
14146     }
14147     else if (element == EL_EMC_LENSES)
14148     {
14149       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14150
14151       RedrawAllInvisibleElementsForLenses();
14152     }
14153     else if (element == EL_EMC_MAGNIFIER)
14154     {
14155       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14156
14157       RedrawAllInvisibleElementsForMagnifier();
14158     }
14159     else if (IS_DROPPABLE(element) ||
14160              IS_THROWABLE(element))     // can be collected and dropped
14161     {
14162       int i;
14163
14164       if (collect_count == 0)
14165         player->inventory_infinite_element = element;
14166       else
14167         for (i = 0; i < collect_count; i++)
14168           if (player->inventory_size < MAX_INVENTORY_SIZE)
14169             player->inventory_element[player->inventory_size++] = element;
14170
14171       DrawGameDoorValues();
14172     }
14173     else if (collect_count > 0)
14174     {
14175       game.gems_still_needed -= collect_count;
14176       if (game.gems_still_needed < 0)
14177         game.gems_still_needed = 0;
14178
14179       game.snapshot.collected_item = TRUE;
14180
14181       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14182
14183       DisplayGameControlValues();
14184     }
14185
14186     RaiseScoreElement(element);
14187     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14188
14189     if (is_player)
14190     {
14191       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14192                                           player->index_bit, dig_side);
14193
14194       // if collecting triggered player relocation, finish collecting tile
14195       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14196         setFieldForSnapping(x, y, element, move_direction);
14197     }
14198
14199     if (mode == DF_SNAP)
14200     {
14201       if (level.block_snap_field)
14202         setFieldForSnapping(x, y, element, move_direction);
14203       else
14204         TestIfElementTouchesCustomElement(x, y);        // for empty space
14205
14206       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14207                                           player->index_bit, dig_side);
14208     }
14209   }
14210   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14211   {
14212     if (mode == DF_SNAP && element != EL_BD_ROCK)
14213       return MP_NO_ACTION;
14214
14215     if (CAN_FALL(element) && dy)
14216       return MP_NO_ACTION;
14217
14218     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14219         !(element == EL_SPRING && level.use_spring_bug))
14220       return MP_NO_ACTION;
14221
14222     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14223         ((move_direction & MV_VERTICAL &&
14224           ((element_info[element].move_pattern & MV_LEFT &&
14225             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14226            (element_info[element].move_pattern & MV_RIGHT &&
14227             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14228          (move_direction & MV_HORIZONTAL &&
14229           ((element_info[element].move_pattern & MV_UP &&
14230             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14231            (element_info[element].move_pattern & MV_DOWN &&
14232             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14233       return MP_NO_ACTION;
14234
14235     // do not push elements already moving away faster than player
14236     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14237         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14238       return MP_NO_ACTION;
14239
14240     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14241     {
14242       if (player->push_delay_value == -1 || !player_was_pushing)
14243         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14244     }
14245     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14246     {
14247       if (player->push_delay_value == -1)
14248         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14249     }
14250     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14251     {
14252       if (!player->is_pushing)
14253         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14254     }
14255
14256     player->is_pushing = TRUE;
14257     player->is_active = TRUE;
14258
14259     if (!(IN_LEV_FIELD(nextx, nexty) &&
14260           (IS_FREE(nextx, nexty) ||
14261            (IS_SB_ELEMENT(element) &&
14262             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14263            (IS_CUSTOM_ELEMENT(element) &&
14264             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14265       return MP_NO_ACTION;
14266
14267     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14268       return MP_NO_ACTION;
14269
14270     if (player->push_delay == -1)       // new pushing; restart delay
14271       player->push_delay = 0;
14272
14273     if (player->push_delay < player->push_delay_value &&
14274         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14275         element != EL_SPRING && element != EL_BALLOON)
14276     {
14277       // make sure that there is no move delay before next try to push
14278       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14279         player->move_delay = 0;
14280
14281       return MP_NO_ACTION;
14282     }
14283
14284     if (IS_CUSTOM_ELEMENT(element) &&
14285         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14286     {
14287       if (!DigFieldByCE(nextx, nexty, element))
14288         return MP_NO_ACTION;
14289     }
14290
14291     if (IS_SB_ELEMENT(element))
14292     {
14293       boolean sokoban_task_solved = FALSE;
14294
14295       if (element == EL_SOKOBAN_FIELD_FULL)
14296       {
14297         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14298
14299         IncrementSokobanFieldsNeeded();
14300         IncrementSokobanObjectsNeeded();
14301       }
14302
14303       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14304       {
14305         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14306
14307         DecrementSokobanFieldsNeeded();
14308         DecrementSokobanObjectsNeeded();
14309
14310         // sokoban object was pushed from empty field to sokoban field
14311         if (Back[x][y] == EL_EMPTY)
14312           sokoban_task_solved = TRUE;
14313       }
14314
14315       Tile[x][y] = EL_SOKOBAN_OBJECT;
14316
14317       if (Back[x][y] == Back[nextx][nexty])
14318         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14319       else if (Back[x][y] != 0)
14320         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14321                                     ACTION_EMPTYING);
14322       else
14323         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14324                                     ACTION_FILLING);
14325
14326       if (sokoban_task_solved &&
14327           game.sokoban_fields_still_needed == 0 &&
14328           game.sokoban_objects_still_needed == 0 &&
14329           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14330       {
14331         game.players_still_needed = 0;
14332
14333         LevelSolved();
14334
14335         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14336       }
14337     }
14338     else
14339       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14340
14341     InitMovingField(x, y, move_direction);
14342     GfxAction[x][y] = ACTION_PUSHING;
14343
14344     if (mode == DF_SNAP)
14345       ContinueMoving(x, y);
14346     else
14347       MovPos[x][y] = (dx != 0 ? dx : dy);
14348
14349     Pushed[x][y] = TRUE;
14350     Pushed[nextx][nexty] = TRUE;
14351
14352     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14353       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14354     else
14355       player->push_delay_value = -1;    // get new value later
14356
14357     // check for element change _after_ element has been pushed
14358     if (game.use_change_when_pushing_bug)
14359     {
14360       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14361                                  player->index_bit, dig_side);
14362       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14363                                           player->index_bit, dig_side);
14364     }
14365   }
14366   else if (IS_SWITCHABLE(element))
14367   {
14368     if (PLAYER_SWITCHING(player, x, y))
14369     {
14370       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14371                                           player->index_bit, dig_side);
14372
14373       return MP_ACTION;
14374     }
14375
14376     player->is_switching = TRUE;
14377     player->switch_x = x;
14378     player->switch_y = y;
14379
14380     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14381
14382     if (element == EL_ROBOT_WHEEL)
14383     {
14384       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14385
14386       game.robot_wheel_x = x;
14387       game.robot_wheel_y = y;
14388       game.robot_wheel_active = TRUE;
14389
14390       TEST_DrawLevelField(x, y);
14391     }
14392     else if (element == EL_SP_TERMINAL)
14393     {
14394       int xx, yy;
14395
14396       SCAN_PLAYFIELD(xx, yy)
14397       {
14398         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14399         {
14400           Bang(xx, yy);
14401         }
14402         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14403         {
14404           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14405
14406           ResetGfxAnimation(xx, yy);
14407           TEST_DrawLevelField(xx, yy);
14408         }
14409       }
14410     }
14411     else if (IS_BELT_SWITCH(element))
14412     {
14413       ToggleBeltSwitch(x, y);
14414     }
14415     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14416              element == EL_SWITCHGATE_SWITCH_DOWN ||
14417              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14418              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14419     {
14420       ToggleSwitchgateSwitch(x, y);
14421     }
14422     else if (element == EL_LIGHT_SWITCH ||
14423              element == EL_LIGHT_SWITCH_ACTIVE)
14424     {
14425       ToggleLightSwitch(x, y);
14426     }
14427     else if (element == EL_TIMEGATE_SWITCH ||
14428              element == EL_DC_TIMEGATE_SWITCH)
14429     {
14430       ActivateTimegateSwitch(x, y);
14431     }
14432     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14433              element == EL_BALLOON_SWITCH_RIGHT ||
14434              element == EL_BALLOON_SWITCH_UP    ||
14435              element == EL_BALLOON_SWITCH_DOWN  ||
14436              element == EL_BALLOON_SWITCH_NONE  ||
14437              element == EL_BALLOON_SWITCH_ANY)
14438     {
14439       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14440                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14441                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14442                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14443                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14444                              move_direction);
14445     }
14446     else if (element == EL_LAMP)
14447     {
14448       Tile[x][y] = EL_LAMP_ACTIVE;
14449       game.lights_still_needed--;
14450
14451       ResetGfxAnimation(x, y);
14452       TEST_DrawLevelField(x, y);
14453     }
14454     else if (element == EL_TIME_ORB_FULL)
14455     {
14456       Tile[x][y] = EL_TIME_ORB_EMPTY;
14457
14458       if (level.time > 0 || level.use_time_orb_bug)
14459       {
14460         TimeLeft += level.time_orb_time;
14461         game.no_time_limit = FALSE;
14462
14463         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14464
14465         DisplayGameControlValues();
14466       }
14467
14468       ResetGfxAnimation(x, y);
14469       TEST_DrawLevelField(x, y);
14470     }
14471     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14472              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14473     {
14474       int xx, yy;
14475
14476       game.ball_active = !game.ball_active;
14477
14478       SCAN_PLAYFIELD(xx, yy)
14479       {
14480         int e = Tile[xx][yy];
14481
14482         if (game.ball_active)
14483         {
14484           if (e == EL_EMC_MAGIC_BALL)
14485             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14486           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14487             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14488         }
14489         else
14490         {
14491           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14492             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14493           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14494             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14495         }
14496       }
14497     }
14498
14499     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14500                                         player->index_bit, dig_side);
14501
14502     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14503                                         player->index_bit, dig_side);
14504
14505     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14506                                         player->index_bit, dig_side);
14507
14508     return MP_ACTION;
14509   }
14510   else
14511   {
14512     if (!PLAYER_SWITCHING(player, x, y))
14513     {
14514       player->is_switching = TRUE;
14515       player->switch_x = x;
14516       player->switch_y = y;
14517
14518       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14519                                  player->index_bit, dig_side);
14520       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14521                                           player->index_bit, dig_side);
14522
14523       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14524                                  player->index_bit, dig_side);
14525       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14526                                           player->index_bit, dig_side);
14527     }
14528
14529     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14530                                player->index_bit, dig_side);
14531     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14532                                         player->index_bit, dig_side);
14533
14534     return MP_NO_ACTION;
14535   }
14536
14537   player->push_delay = -1;
14538
14539   if (is_player)                // function can also be called by EL_PENGUIN
14540   {
14541     if (Tile[x][y] != element)          // really digged/collected something
14542     {
14543       player->is_collecting = !player->is_digging;
14544       player->is_active = TRUE;
14545     }
14546   }
14547
14548   return MP_MOVING;
14549 }
14550
14551 static boolean DigFieldByCE(int x, int y, int digging_element)
14552 {
14553   int element = Tile[x][y];
14554
14555   if (!IS_FREE(x, y))
14556   {
14557     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14558                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14559                   ACTION_BREAKING);
14560
14561     // no element can dig solid indestructible elements
14562     if (IS_INDESTRUCTIBLE(element) &&
14563         !IS_DIGGABLE(element) &&
14564         !IS_COLLECTIBLE(element))
14565       return FALSE;
14566
14567     if (AmoebaNr[x][y] &&
14568         (element == EL_AMOEBA_FULL ||
14569          element == EL_BD_AMOEBA ||
14570          element == EL_AMOEBA_GROWING))
14571     {
14572       AmoebaCnt[AmoebaNr[x][y]]--;
14573       AmoebaCnt2[AmoebaNr[x][y]]--;
14574     }
14575
14576     if (IS_MOVING(x, y))
14577       RemoveMovingField(x, y);
14578     else
14579     {
14580       RemoveField(x, y);
14581       TEST_DrawLevelField(x, y);
14582     }
14583
14584     // if digged element was about to explode, prevent the explosion
14585     ExplodeField[x][y] = EX_TYPE_NONE;
14586
14587     PlayLevelSoundAction(x, y, action);
14588   }
14589
14590   Store[x][y] = EL_EMPTY;
14591
14592   // this makes it possible to leave the removed element again
14593   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14594     Store[x][y] = element;
14595
14596   return TRUE;
14597 }
14598
14599 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14600 {
14601   int jx = player->jx, jy = player->jy;
14602   int x = jx + dx, y = jy + dy;
14603   int snap_direction = (dx == -1 ? MV_LEFT  :
14604                         dx == +1 ? MV_RIGHT :
14605                         dy == -1 ? MV_UP    :
14606                         dy == +1 ? MV_DOWN  : MV_NONE);
14607   boolean can_continue_snapping = (level.continuous_snapping &&
14608                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14609
14610   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14611     return FALSE;
14612
14613   if (!player->active || !IN_LEV_FIELD(x, y))
14614     return FALSE;
14615
14616   if (dx && dy)
14617     return FALSE;
14618
14619   if (!dx && !dy)
14620   {
14621     if (player->MovPos == 0)
14622       player->is_pushing = FALSE;
14623
14624     player->is_snapping = FALSE;
14625
14626     if (player->MovPos == 0)
14627     {
14628       player->is_moving = FALSE;
14629       player->is_digging = FALSE;
14630       player->is_collecting = FALSE;
14631     }
14632
14633     return FALSE;
14634   }
14635
14636   // prevent snapping with already pressed snap key when not allowed
14637   if (player->is_snapping && !can_continue_snapping)
14638     return FALSE;
14639
14640   player->MovDir = snap_direction;
14641
14642   if (player->MovPos == 0)
14643   {
14644     player->is_moving = FALSE;
14645     player->is_digging = FALSE;
14646     player->is_collecting = FALSE;
14647   }
14648
14649   player->is_dropping = FALSE;
14650   player->is_dropping_pressed = FALSE;
14651   player->drop_pressed_delay = 0;
14652
14653   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14654     return FALSE;
14655
14656   player->is_snapping = TRUE;
14657   player->is_active = TRUE;
14658
14659   if (player->MovPos == 0)
14660   {
14661     player->is_moving = FALSE;
14662     player->is_digging = FALSE;
14663     player->is_collecting = FALSE;
14664   }
14665
14666   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14667     TEST_DrawLevelField(player->last_jx, player->last_jy);
14668
14669   TEST_DrawLevelField(x, y);
14670
14671   return TRUE;
14672 }
14673
14674 static boolean DropElement(struct PlayerInfo *player)
14675 {
14676   int old_element, new_element;
14677   int dropx = player->jx, dropy = player->jy;
14678   int drop_direction = player->MovDir;
14679   int drop_side = drop_direction;
14680   int drop_element = get_next_dropped_element(player);
14681
14682   /* do not drop an element on top of another element; when holding drop key
14683      pressed without moving, dropped element must move away before the next
14684      element can be dropped (this is especially important if the next element
14685      is dynamite, which can be placed on background for historical reasons) */
14686   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14687     return MP_ACTION;
14688
14689   if (IS_THROWABLE(drop_element))
14690   {
14691     dropx += GET_DX_FROM_DIR(drop_direction);
14692     dropy += GET_DY_FROM_DIR(drop_direction);
14693
14694     if (!IN_LEV_FIELD(dropx, dropy))
14695       return FALSE;
14696   }
14697
14698   old_element = Tile[dropx][dropy];     // old element at dropping position
14699   new_element = drop_element;           // default: no change when dropping
14700
14701   // check if player is active, not moving and ready to drop
14702   if (!player->active || player->MovPos || player->drop_delay > 0)
14703     return FALSE;
14704
14705   // check if player has anything that can be dropped
14706   if (new_element == EL_UNDEFINED)
14707     return FALSE;
14708
14709   // only set if player has anything that can be dropped
14710   player->is_dropping_pressed = TRUE;
14711
14712   // check if drop key was pressed long enough for EM style dynamite
14713   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14714     return FALSE;
14715
14716   // check if anything can be dropped at the current position
14717   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14718     return FALSE;
14719
14720   // collected custom elements can only be dropped on empty fields
14721   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14722     return FALSE;
14723
14724   if (old_element != EL_EMPTY)
14725     Back[dropx][dropy] = old_element;   // store old element on this field
14726
14727   ResetGfxAnimation(dropx, dropy);
14728   ResetRandomAnimationValue(dropx, dropy);
14729
14730   if (player->inventory_size > 0 ||
14731       player->inventory_infinite_element != EL_UNDEFINED)
14732   {
14733     if (player->inventory_size > 0)
14734     {
14735       player->inventory_size--;
14736
14737       DrawGameDoorValues();
14738
14739       if (new_element == EL_DYNAMITE)
14740         new_element = EL_DYNAMITE_ACTIVE;
14741       else if (new_element == EL_EM_DYNAMITE)
14742         new_element = EL_EM_DYNAMITE_ACTIVE;
14743       else if (new_element == EL_SP_DISK_RED)
14744         new_element = EL_SP_DISK_RED_ACTIVE;
14745     }
14746
14747     Tile[dropx][dropy] = new_element;
14748
14749     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14750       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14751                           el2img(Tile[dropx][dropy]), 0);
14752
14753     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14754
14755     // needed if previous element just changed to "empty" in the last frame
14756     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14757
14758     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14759                                player->index_bit, drop_side);
14760     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14761                                         CE_PLAYER_DROPS_X,
14762                                         player->index_bit, drop_side);
14763
14764     TestIfElementTouchesCustomElement(dropx, dropy);
14765   }
14766   else          // player is dropping a dyna bomb
14767   {
14768     player->dynabombs_left--;
14769
14770     Tile[dropx][dropy] = new_element;
14771
14772     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14773       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14774                           el2img(Tile[dropx][dropy]), 0);
14775
14776     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14777   }
14778
14779   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14780     InitField_WithBug1(dropx, dropy, FALSE);
14781
14782   new_element = Tile[dropx][dropy];     // element might have changed
14783
14784   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14785       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14786   {
14787     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14788       MovDir[dropx][dropy] = drop_direction;
14789
14790     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14791
14792     // do not cause impact style collision by dropping elements that can fall
14793     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14794   }
14795
14796   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14797   player->is_dropping = TRUE;
14798
14799   player->drop_pressed_delay = 0;
14800   player->is_dropping_pressed = FALSE;
14801
14802   player->drop_x = dropx;
14803   player->drop_y = dropy;
14804
14805   return TRUE;
14806 }
14807
14808 // ----------------------------------------------------------------------------
14809 // game sound playing functions
14810 // ----------------------------------------------------------------------------
14811
14812 static int *loop_sound_frame = NULL;
14813 static int *loop_sound_volume = NULL;
14814
14815 void InitPlayLevelSound(void)
14816 {
14817   int num_sounds = getSoundListSize();
14818
14819   checked_free(loop_sound_frame);
14820   checked_free(loop_sound_volume);
14821
14822   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14823   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14824 }
14825
14826 static void PlayLevelSound(int x, int y, int nr)
14827 {
14828   int sx = SCREENX(x), sy = SCREENY(y);
14829   int volume, stereo_position;
14830   int max_distance = 8;
14831   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14832
14833   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14834       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14835     return;
14836
14837   if (!IN_LEV_FIELD(x, y) ||
14838       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14839       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14840     return;
14841
14842   volume = SOUND_MAX_VOLUME;
14843
14844   if (!IN_SCR_FIELD(sx, sy))
14845   {
14846     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14847     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14848
14849     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14850   }
14851
14852   stereo_position = (SOUND_MAX_LEFT +
14853                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14854                      (SCR_FIELDX + 2 * max_distance));
14855
14856   if (IS_LOOP_SOUND(nr))
14857   {
14858     /* This assures that quieter loop sounds do not overwrite louder ones,
14859        while restarting sound volume comparison with each new game frame. */
14860
14861     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14862       return;
14863
14864     loop_sound_volume[nr] = volume;
14865     loop_sound_frame[nr] = FrameCounter;
14866   }
14867
14868   PlaySoundExt(nr, volume, stereo_position, type);
14869 }
14870
14871 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14872 {
14873   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14874                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14875                  y < LEVELY(BY1) ? LEVELY(BY1) :
14876                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14877                  sound_action);
14878 }
14879
14880 static void PlayLevelSoundAction(int x, int y, int action)
14881 {
14882   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14883 }
14884
14885 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14886 {
14887   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14888
14889   if (sound_effect != SND_UNDEFINED)
14890     PlayLevelSound(x, y, sound_effect);
14891 }
14892
14893 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14894                                               int action)
14895 {
14896   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14897
14898   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14899     PlayLevelSound(x, y, sound_effect);
14900 }
14901
14902 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14903 {
14904   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14905
14906   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14907     PlayLevelSound(x, y, sound_effect);
14908 }
14909
14910 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14911 {
14912   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
14913
14914   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14915     StopSound(sound_effect);
14916 }
14917
14918 static int getLevelMusicNr(void)
14919 {
14920   if (levelset.music[level_nr] != MUS_UNDEFINED)
14921     return levelset.music[level_nr];            // from config file
14922   else
14923     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14924 }
14925
14926 static void FadeLevelSounds(void)
14927 {
14928   FadeSounds();
14929 }
14930
14931 static void FadeLevelMusic(void)
14932 {
14933   int music_nr = getLevelMusicNr();
14934   char *curr_music = getCurrentlyPlayingMusicFilename();
14935   char *next_music = getMusicInfoEntryFilename(music_nr);
14936
14937   if (!strEqual(curr_music, next_music))
14938     FadeMusic();
14939 }
14940
14941 void FadeLevelSoundsAndMusic(void)
14942 {
14943   FadeLevelSounds();
14944   FadeLevelMusic();
14945 }
14946
14947 static void PlayLevelMusic(void)
14948 {
14949   int music_nr = getLevelMusicNr();
14950   char *curr_music = getCurrentlyPlayingMusicFilename();
14951   char *next_music = getMusicInfoEntryFilename(music_nr);
14952
14953   if (!strEqual(curr_music, next_music))
14954     PlayMusicLoop(music_nr);
14955 }
14956
14957 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14958 {
14959   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14960   int offset = 0;
14961   int x = xx - offset;
14962   int y = yy - offset;
14963
14964   switch (sample)
14965   {
14966     case SOUND_blank:
14967       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14968       break;
14969
14970     case SOUND_roll:
14971       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14972       break;
14973
14974     case SOUND_stone:
14975       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14976       break;
14977
14978     case SOUND_nut:
14979       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14980       break;
14981
14982     case SOUND_crack:
14983       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14984       break;
14985
14986     case SOUND_bug:
14987       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14988       break;
14989
14990     case SOUND_tank:
14991       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14992       break;
14993
14994     case SOUND_android_clone:
14995       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14996       break;
14997
14998     case SOUND_android_move:
14999       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15000       break;
15001
15002     case SOUND_spring:
15003       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15004       break;
15005
15006     case SOUND_slurp:
15007       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15008       break;
15009
15010     case SOUND_eater:
15011       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15012       break;
15013
15014     case SOUND_eater_eat:
15015       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15016       break;
15017
15018     case SOUND_alien:
15019       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15020       break;
15021
15022     case SOUND_collect:
15023       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15024       break;
15025
15026     case SOUND_diamond:
15027       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15028       break;
15029
15030     case SOUND_squash:
15031       // !!! CHECK THIS !!!
15032 #if 1
15033       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15034 #else
15035       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15036 #endif
15037       break;
15038
15039     case SOUND_wonderfall:
15040       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15041       break;
15042
15043     case SOUND_drip:
15044       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15045       break;
15046
15047     case SOUND_push:
15048       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15049       break;
15050
15051     case SOUND_dirt:
15052       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15053       break;
15054
15055     case SOUND_acid:
15056       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15057       break;
15058
15059     case SOUND_ball:
15060       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15061       break;
15062
15063     case SOUND_slide:
15064       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15065       break;
15066
15067     case SOUND_wonder:
15068       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15069       break;
15070
15071     case SOUND_door:
15072       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15073       break;
15074
15075     case SOUND_exit_open:
15076       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15077       break;
15078
15079     case SOUND_exit_leave:
15080       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15081       break;
15082
15083     case SOUND_dynamite:
15084       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15085       break;
15086
15087     case SOUND_tick:
15088       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15089       break;
15090
15091     case SOUND_press:
15092       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15093       break;
15094
15095     case SOUND_wheel:
15096       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15097       break;
15098
15099     case SOUND_boom:
15100       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15101       break;
15102
15103     case SOUND_die:
15104       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15105       break;
15106
15107     case SOUND_time:
15108       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15109       break;
15110
15111     default:
15112       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15113       break;
15114   }
15115 }
15116
15117 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15118 {
15119   int element = map_element_SP_to_RND(element_sp);
15120   int action = map_action_SP_to_RND(action_sp);
15121   int offset = (setup.sp_show_border_elements ? 0 : 1);
15122   int x = xx - offset;
15123   int y = yy - offset;
15124
15125   PlayLevelSoundElementAction(x, y, element, action);
15126 }
15127
15128 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15129 {
15130   int element = map_element_MM_to_RND(element_mm);
15131   int action = map_action_MM_to_RND(action_mm);
15132   int offset = 0;
15133   int x = xx - offset;
15134   int y = yy - offset;
15135
15136   if (!IS_MM_ELEMENT(element))
15137     element = EL_MM_DEFAULT;
15138
15139   PlayLevelSoundElementAction(x, y, element, action);
15140 }
15141
15142 void PlaySound_MM(int sound_mm)
15143 {
15144   int sound = map_sound_MM_to_RND(sound_mm);
15145
15146   if (sound == SND_UNDEFINED)
15147     return;
15148
15149   PlaySound(sound);
15150 }
15151
15152 void PlaySoundLoop_MM(int sound_mm)
15153 {
15154   int sound = map_sound_MM_to_RND(sound_mm);
15155
15156   if (sound == SND_UNDEFINED)
15157     return;
15158
15159   PlaySoundLoop(sound);
15160 }
15161
15162 void StopSound_MM(int sound_mm)
15163 {
15164   int sound = map_sound_MM_to_RND(sound_mm);
15165
15166   if (sound == SND_UNDEFINED)
15167     return;
15168
15169   StopSound(sound);
15170 }
15171
15172 void RaiseScore(int value)
15173 {
15174   game.score += value;
15175
15176   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15177
15178   DisplayGameControlValues();
15179 }
15180
15181 void RaiseScoreElement(int element)
15182 {
15183   switch (element)
15184   {
15185     case EL_EMERALD:
15186     case EL_BD_DIAMOND:
15187     case EL_EMERALD_YELLOW:
15188     case EL_EMERALD_RED:
15189     case EL_EMERALD_PURPLE:
15190     case EL_SP_INFOTRON:
15191       RaiseScore(level.score[SC_EMERALD]);
15192       break;
15193     case EL_DIAMOND:
15194       RaiseScore(level.score[SC_DIAMOND]);
15195       break;
15196     case EL_CRYSTAL:
15197       RaiseScore(level.score[SC_CRYSTAL]);
15198       break;
15199     case EL_PEARL:
15200       RaiseScore(level.score[SC_PEARL]);
15201       break;
15202     case EL_BUG:
15203     case EL_BD_BUTTERFLY:
15204     case EL_SP_ELECTRON:
15205       RaiseScore(level.score[SC_BUG]);
15206       break;
15207     case EL_SPACESHIP:
15208     case EL_BD_FIREFLY:
15209     case EL_SP_SNIKSNAK:
15210       RaiseScore(level.score[SC_SPACESHIP]);
15211       break;
15212     case EL_YAMYAM:
15213     case EL_DARK_YAMYAM:
15214       RaiseScore(level.score[SC_YAMYAM]);
15215       break;
15216     case EL_ROBOT:
15217       RaiseScore(level.score[SC_ROBOT]);
15218       break;
15219     case EL_PACMAN:
15220       RaiseScore(level.score[SC_PACMAN]);
15221       break;
15222     case EL_NUT:
15223       RaiseScore(level.score[SC_NUT]);
15224       break;
15225     case EL_DYNAMITE:
15226     case EL_EM_DYNAMITE:
15227     case EL_SP_DISK_RED:
15228     case EL_DYNABOMB_INCREASE_NUMBER:
15229     case EL_DYNABOMB_INCREASE_SIZE:
15230     case EL_DYNABOMB_INCREASE_POWER:
15231       RaiseScore(level.score[SC_DYNAMITE]);
15232       break;
15233     case EL_SHIELD_NORMAL:
15234     case EL_SHIELD_DEADLY:
15235       RaiseScore(level.score[SC_SHIELD]);
15236       break;
15237     case EL_EXTRA_TIME:
15238       RaiseScore(level.extra_time_score);
15239       break;
15240     case EL_KEY_1:
15241     case EL_KEY_2:
15242     case EL_KEY_3:
15243     case EL_KEY_4:
15244     case EL_EM_KEY_1:
15245     case EL_EM_KEY_2:
15246     case EL_EM_KEY_3:
15247     case EL_EM_KEY_4:
15248     case EL_EMC_KEY_5:
15249     case EL_EMC_KEY_6:
15250     case EL_EMC_KEY_7:
15251     case EL_EMC_KEY_8:
15252     case EL_DC_KEY_WHITE:
15253       RaiseScore(level.score[SC_KEY]);
15254       break;
15255     default:
15256       RaiseScore(element_info[element].collect_score);
15257       break;
15258   }
15259 }
15260
15261 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15262 {
15263   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15264   {
15265     // closing door required in case of envelope style request dialogs
15266     if (!skip_request)
15267     {
15268       // prevent short reactivation of overlay buttons while closing door
15269       SetOverlayActive(FALSE);
15270
15271       CloseDoor(DOOR_CLOSE_1);
15272     }
15273
15274     if (network.enabled)
15275       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15276     else
15277     {
15278       if (quick_quit)
15279         FadeSkipNextFadeIn();
15280
15281       SetGameStatus(GAME_MODE_MAIN);
15282
15283       DrawMainMenu();
15284     }
15285   }
15286   else          // continue playing the game
15287   {
15288     if (tape.playing && tape.deactivate_display)
15289       TapeDeactivateDisplayOff(TRUE);
15290
15291     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15292
15293     if (tape.playing && tape.deactivate_display)
15294       TapeDeactivateDisplayOn();
15295   }
15296 }
15297
15298 void RequestQuitGame(boolean ask_if_really_quit)
15299 {
15300   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15301   boolean skip_request = game.all_players_gone || quick_quit;
15302
15303   RequestQuitGameExt(skip_request, quick_quit,
15304                      "Do you really want to quit the game?");
15305 }
15306
15307 void RequestRestartGame(char *message)
15308 {
15309   game.restart_game_message = NULL;
15310
15311   boolean has_started_game = hasStartedNetworkGame();
15312   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15313
15314   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15315   {
15316     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15317   }
15318   else
15319   {
15320     // needed in case of envelope request to close game panel
15321     CloseDoor(DOOR_CLOSE_1);
15322
15323     SetGameStatus(GAME_MODE_MAIN);
15324
15325     DrawMainMenu();
15326   }
15327 }
15328
15329 void CheckGameOver(void)
15330 {
15331   static boolean last_game_over = FALSE;
15332   static int game_over_delay = 0;
15333   int game_over_delay_value = 50;
15334   boolean game_over = checkGameFailed();
15335
15336   // do not handle game over if request dialog is already active
15337   if (game.request_active)
15338     return;
15339
15340   // do not ask to play again if game was never actually played
15341   if (!game.GamePlayed)
15342     return;
15343
15344   if (!game_over)
15345   {
15346     last_game_over = FALSE;
15347     game_over_delay = game_over_delay_value;
15348
15349     return;
15350   }
15351
15352   if (game_over_delay > 0)
15353   {
15354     game_over_delay--;
15355
15356     return;
15357   }
15358
15359   if (last_game_over != game_over)
15360     game.restart_game_message = (hasStartedNetworkGame() ?
15361                                  "Game over! Play it again?" :
15362                                  "Game over!");
15363
15364   last_game_over = game_over;
15365 }
15366
15367 boolean checkGameSolved(void)
15368 {
15369   // set for all game engines if level was solved
15370   return game.LevelSolved_GameEnd;
15371 }
15372
15373 boolean checkGameFailed(void)
15374 {
15375   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15376     return (game_em.game_over && !game_em.level_solved);
15377   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15378     return (game_sp.game_over && !game_sp.level_solved);
15379   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15380     return (game_mm.game_over && !game_mm.level_solved);
15381   else                          // GAME_ENGINE_TYPE_RND
15382     return (game.GameOver && !game.LevelSolved);
15383 }
15384
15385 boolean checkGameEnded(void)
15386 {
15387   return (checkGameSolved() || checkGameFailed());
15388 }
15389
15390
15391 // ----------------------------------------------------------------------------
15392 // random generator functions
15393 // ----------------------------------------------------------------------------
15394
15395 unsigned int InitEngineRandom_RND(int seed)
15396 {
15397   game.num_random_calls = 0;
15398
15399   return InitEngineRandom(seed);
15400 }
15401
15402 unsigned int RND(int max)
15403 {
15404   if (max > 0)
15405   {
15406     game.num_random_calls++;
15407
15408     return GetEngineRandom(max);
15409   }
15410
15411   return 0;
15412 }
15413
15414
15415 // ----------------------------------------------------------------------------
15416 // game engine snapshot handling functions
15417 // ----------------------------------------------------------------------------
15418
15419 struct EngineSnapshotInfo
15420 {
15421   // runtime values for custom element collect score
15422   int collect_score[NUM_CUSTOM_ELEMENTS];
15423
15424   // runtime values for group element choice position
15425   int choice_pos[NUM_GROUP_ELEMENTS];
15426
15427   // runtime values for belt position animations
15428   int belt_graphic[4][NUM_BELT_PARTS];
15429   int belt_anim_mode[4][NUM_BELT_PARTS];
15430 };
15431
15432 static struct EngineSnapshotInfo engine_snapshot_rnd;
15433 static char *snapshot_level_identifier = NULL;
15434 static int snapshot_level_nr = -1;
15435
15436 static void SaveEngineSnapshotValues_RND(void)
15437 {
15438   static int belt_base_active_element[4] =
15439   {
15440     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15441     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15442     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15443     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15444   };
15445   int i, j;
15446
15447   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15448   {
15449     int element = EL_CUSTOM_START + i;
15450
15451     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15452   }
15453
15454   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15455   {
15456     int element = EL_GROUP_START + i;
15457
15458     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15459   }
15460
15461   for (i = 0; i < 4; i++)
15462   {
15463     for (j = 0; j < NUM_BELT_PARTS; j++)
15464     {
15465       int element = belt_base_active_element[i] + j;
15466       int graphic = el2img(element);
15467       int anim_mode = graphic_info[graphic].anim_mode;
15468
15469       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15470       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15471     }
15472   }
15473 }
15474
15475 static void LoadEngineSnapshotValues_RND(void)
15476 {
15477   unsigned int num_random_calls = game.num_random_calls;
15478   int i, j;
15479
15480   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15481   {
15482     int element = EL_CUSTOM_START + i;
15483
15484     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15485   }
15486
15487   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15488   {
15489     int element = EL_GROUP_START + i;
15490
15491     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15492   }
15493
15494   for (i = 0; i < 4; i++)
15495   {
15496     for (j = 0; j < NUM_BELT_PARTS; j++)
15497     {
15498       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15499       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15500
15501       graphic_info[graphic].anim_mode = anim_mode;
15502     }
15503   }
15504
15505   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15506   {
15507     InitRND(tape.random_seed);
15508     for (i = 0; i < num_random_calls; i++)
15509       RND(1);
15510   }
15511
15512   if (game.num_random_calls != num_random_calls)
15513   {
15514     Error("number of random calls out of sync");
15515     Error("number of random calls should be %d", num_random_calls);
15516     Error("number of random calls is %d", game.num_random_calls);
15517
15518     Fail("this should not happen -- please debug");
15519   }
15520 }
15521
15522 void FreeEngineSnapshotSingle(void)
15523 {
15524   FreeSnapshotSingle();
15525
15526   setString(&snapshot_level_identifier, NULL);
15527   snapshot_level_nr = -1;
15528 }
15529
15530 void FreeEngineSnapshotList(void)
15531 {
15532   FreeSnapshotList();
15533 }
15534
15535 static ListNode *SaveEngineSnapshotBuffers(void)
15536 {
15537   ListNode *buffers = NULL;
15538
15539   // copy some special values to a structure better suited for the snapshot
15540
15541   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15542     SaveEngineSnapshotValues_RND();
15543   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15544     SaveEngineSnapshotValues_EM();
15545   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15546     SaveEngineSnapshotValues_SP(&buffers);
15547   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15548     SaveEngineSnapshotValues_MM(&buffers);
15549
15550   // save values stored in special snapshot structure
15551
15552   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15553     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15554   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15555     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15556   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15557     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15558   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15559     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15560
15561   // save further RND engine values
15562
15563   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15564   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15565   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15566
15567   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15568   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15569   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15570   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15571   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15572
15573   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15574   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15575   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15576
15577   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15578
15579   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15580   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15581
15582   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15583   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15584   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15585   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15586   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15587   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15588   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15589   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15590   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15591   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15592   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15593   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15594   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15595   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15596   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15597   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15598   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15599   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15600
15601   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15602   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15603
15604   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15605   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15606   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15607
15608   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15609   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15610
15611   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15612   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15613   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15614   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15615   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15616
15617   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15618   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15619
15620 #if 0
15621   ListNode *node = engine_snapshot_list_rnd;
15622   int num_bytes = 0;
15623
15624   while (node != NULL)
15625   {
15626     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15627
15628     node = node->next;
15629   }
15630
15631   Debug("game:playing:SaveEngineSnapshotBuffers",
15632         "size of engine snapshot: %d bytes", num_bytes);
15633 #endif
15634
15635   return buffers;
15636 }
15637
15638 void SaveEngineSnapshotSingle(void)
15639 {
15640   ListNode *buffers = SaveEngineSnapshotBuffers();
15641
15642   // finally save all snapshot buffers to single snapshot
15643   SaveSnapshotSingle(buffers);
15644
15645   // save level identification information
15646   setString(&snapshot_level_identifier, leveldir_current->identifier);
15647   snapshot_level_nr = level_nr;
15648 }
15649
15650 boolean CheckSaveEngineSnapshotToList(void)
15651 {
15652   boolean save_snapshot =
15653     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15654      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15655       game.snapshot.changed_action) ||
15656      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15657       game.snapshot.collected_item));
15658
15659   game.snapshot.changed_action = FALSE;
15660   game.snapshot.collected_item = FALSE;
15661   game.snapshot.save_snapshot = save_snapshot;
15662
15663   return save_snapshot;
15664 }
15665
15666 void SaveEngineSnapshotToList(void)
15667 {
15668   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15669       tape.quick_resume)
15670     return;
15671
15672   ListNode *buffers = SaveEngineSnapshotBuffers();
15673
15674   // finally save all snapshot buffers to snapshot list
15675   SaveSnapshotToList(buffers);
15676 }
15677
15678 void SaveEngineSnapshotToListInitial(void)
15679 {
15680   FreeEngineSnapshotList();
15681
15682   SaveEngineSnapshotToList();
15683 }
15684
15685 static void LoadEngineSnapshotValues(void)
15686 {
15687   // restore special values from snapshot structure
15688
15689   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15690     LoadEngineSnapshotValues_RND();
15691   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15692     LoadEngineSnapshotValues_EM();
15693   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15694     LoadEngineSnapshotValues_SP();
15695   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15696     LoadEngineSnapshotValues_MM();
15697 }
15698
15699 void LoadEngineSnapshotSingle(void)
15700 {
15701   LoadSnapshotSingle();
15702
15703   LoadEngineSnapshotValues();
15704 }
15705
15706 static void LoadEngineSnapshot_Undo(int steps)
15707 {
15708   LoadSnapshotFromList_Older(steps);
15709
15710   LoadEngineSnapshotValues();
15711 }
15712
15713 static void LoadEngineSnapshot_Redo(int steps)
15714 {
15715   LoadSnapshotFromList_Newer(steps);
15716
15717   LoadEngineSnapshotValues();
15718 }
15719
15720 boolean CheckEngineSnapshotSingle(void)
15721 {
15722   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15723           snapshot_level_nr == level_nr);
15724 }
15725
15726 boolean CheckEngineSnapshotList(void)
15727 {
15728   return CheckSnapshotList();
15729 }
15730
15731
15732 // ---------- new game button stuff -------------------------------------------
15733
15734 static struct
15735 {
15736   int graphic;
15737   struct XY *pos;
15738   int gadget_id;
15739   boolean *setup_value;
15740   boolean allowed_on_tape;
15741   boolean is_touch_button;
15742   char *infotext;
15743 } gamebutton_info[NUM_GAME_BUTTONS] =
15744 {
15745   {
15746     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15747     GAME_CTRL_ID_STOP,                          NULL,
15748     TRUE, FALSE,                                "stop game"
15749   },
15750   {
15751     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15752     GAME_CTRL_ID_PAUSE,                         NULL,
15753     TRUE, FALSE,                                "pause game"
15754   },
15755   {
15756     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15757     GAME_CTRL_ID_PLAY,                          NULL,
15758     TRUE, FALSE,                                "play game"
15759   },
15760   {
15761     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15762     GAME_CTRL_ID_UNDO,                          NULL,
15763     TRUE, FALSE,                                "undo step"
15764   },
15765   {
15766     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15767     GAME_CTRL_ID_REDO,                          NULL,
15768     TRUE, FALSE,                                "redo step"
15769   },
15770   {
15771     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15772     GAME_CTRL_ID_SAVE,                          NULL,
15773     TRUE, FALSE,                                "save game"
15774   },
15775   {
15776     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15777     GAME_CTRL_ID_PAUSE2,                        NULL,
15778     TRUE, FALSE,                                "pause game"
15779   },
15780   {
15781     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15782     GAME_CTRL_ID_LOAD,                          NULL,
15783     TRUE, FALSE,                                "load game"
15784   },
15785   {
15786     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15787     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15788     FALSE, FALSE,                               "stop game"
15789   },
15790   {
15791     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15792     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15793     FALSE, FALSE,                               "pause game"
15794   },
15795   {
15796     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15797     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15798     FALSE, FALSE,                               "play game"
15799   },
15800   {
15801     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15802     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15803     FALSE, TRUE,                                "stop game"
15804   },
15805   {
15806     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15807     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15808     FALSE, TRUE,                                "pause game"
15809   },
15810   {
15811     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15812     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15813     TRUE, FALSE,                                "background music on/off"
15814   },
15815   {
15816     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15817     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15818     TRUE, FALSE,                                "sound loops on/off"
15819   },
15820   {
15821     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15822     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15823     TRUE, FALSE,                                "normal sounds on/off"
15824   },
15825   {
15826     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15827     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15828     FALSE, FALSE,                               "background music on/off"
15829   },
15830   {
15831     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15832     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15833     FALSE, FALSE,                               "sound loops on/off"
15834   },
15835   {
15836     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15837     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15838     FALSE, FALSE,                               "normal sounds on/off"
15839   }
15840 };
15841
15842 void CreateGameButtons(void)
15843 {
15844   int i;
15845
15846   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15847   {
15848     int graphic = gamebutton_info[i].graphic;
15849     struct GraphicInfo *gfx = &graphic_info[graphic];
15850     struct XY *pos = gamebutton_info[i].pos;
15851     struct GadgetInfo *gi;
15852     int button_type;
15853     boolean checked;
15854     unsigned int event_mask;
15855     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15856     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15857     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15858     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15859     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15860     int gd_x   = gfx->src_x;
15861     int gd_y   = gfx->src_y;
15862     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15863     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15864     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15865     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15866     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15867     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15868     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15869     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15870     int id = i;
15871
15872     if (gfx->bitmap == NULL)
15873     {
15874       game_gadget[id] = NULL;
15875
15876       continue;
15877     }
15878
15879     if (id == GAME_CTRL_ID_STOP ||
15880         id == GAME_CTRL_ID_PANEL_STOP ||
15881         id == GAME_CTRL_ID_TOUCH_STOP ||
15882         id == GAME_CTRL_ID_PLAY ||
15883         id == GAME_CTRL_ID_PANEL_PLAY ||
15884         id == GAME_CTRL_ID_SAVE ||
15885         id == GAME_CTRL_ID_LOAD)
15886     {
15887       button_type = GD_TYPE_NORMAL_BUTTON;
15888       checked = FALSE;
15889       event_mask = GD_EVENT_RELEASED;
15890     }
15891     else if (id == GAME_CTRL_ID_UNDO ||
15892              id == GAME_CTRL_ID_REDO)
15893     {
15894       button_type = GD_TYPE_NORMAL_BUTTON;
15895       checked = FALSE;
15896       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15897     }
15898     else
15899     {
15900       button_type = GD_TYPE_CHECK_BUTTON;
15901       checked = (gamebutton_info[i].setup_value != NULL ?
15902                  *gamebutton_info[i].setup_value : FALSE);
15903       event_mask = GD_EVENT_PRESSED;
15904     }
15905
15906     gi = CreateGadget(GDI_CUSTOM_ID, id,
15907                       GDI_IMAGE_ID, graphic,
15908                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15909                       GDI_X, base_x + x,
15910                       GDI_Y, base_y + y,
15911                       GDI_WIDTH, gfx->width,
15912                       GDI_HEIGHT, gfx->height,
15913                       GDI_TYPE, button_type,
15914                       GDI_STATE, GD_BUTTON_UNPRESSED,
15915                       GDI_CHECKED, checked,
15916                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15917                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15918                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15919                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15920                       GDI_DIRECT_DRAW, FALSE,
15921                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15922                       GDI_EVENT_MASK, event_mask,
15923                       GDI_CALLBACK_ACTION, HandleGameButtons,
15924                       GDI_END);
15925
15926     if (gi == NULL)
15927       Fail("cannot create gadget");
15928
15929     game_gadget[id] = gi;
15930   }
15931 }
15932
15933 void FreeGameButtons(void)
15934 {
15935   int i;
15936
15937   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15938     FreeGadget(game_gadget[i]);
15939 }
15940
15941 static void UnmapGameButtonsAtSamePosition(int id)
15942 {
15943   int i;
15944
15945   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15946     if (i != id &&
15947         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15948         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15949       UnmapGadget(game_gadget[i]);
15950 }
15951
15952 static void UnmapGameButtonsAtSamePosition_All(void)
15953 {
15954   if (setup.show_snapshot_buttons)
15955   {
15956     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15957     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15958     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15959   }
15960   else
15961   {
15962     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15963     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15964     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15965
15966     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15967     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15968     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15969   }
15970 }
15971
15972 static void MapGameButtonsAtSamePosition(int id)
15973 {
15974   int i;
15975
15976   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15977     if (i != id &&
15978         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15979         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15980       MapGadget(game_gadget[i]);
15981
15982   UnmapGameButtonsAtSamePosition_All();
15983 }
15984
15985 void MapUndoRedoButtons(void)
15986 {
15987   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15988   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15989
15990   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15991   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15992 }
15993
15994 void UnmapUndoRedoButtons(void)
15995 {
15996   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15997   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15998
15999   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16000   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16001 }
16002
16003 void ModifyPauseButtons(void)
16004 {
16005   static int ids[] =
16006   {
16007     GAME_CTRL_ID_PAUSE,
16008     GAME_CTRL_ID_PAUSE2,
16009     GAME_CTRL_ID_PANEL_PAUSE,
16010     GAME_CTRL_ID_TOUCH_PAUSE,
16011     -1
16012   };
16013   int i;
16014
16015   for (i = 0; ids[i] > -1; i++)
16016     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16017 }
16018
16019 static void MapGameButtonsExt(boolean on_tape)
16020 {
16021   int i;
16022
16023   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16024     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16025         i != GAME_CTRL_ID_UNDO &&
16026         i != GAME_CTRL_ID_REDO)
16027       MapGadget(game_gadget[i]);
16028
16029   UnmapGameButtonsAtSamePosition_All();
16030
16031   RedrawGameButtons();
16032 }
16033
16034 static void UnmapGameButtonsExt(boolean on_tape)
16035 {
16036   int i;
16037
16038   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16039     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16040       UnmapGadget(game_gadget[i]);
16041 }
16042
16043 static void RedrawGameButtonsExt(boolean on_tape)
16044 {
16045   int i;
16046
16047   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16048     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16049       RedrawGadget(game_gadget[i]);
16050 }
16051
16052 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16053 {
16054   if (gi == NULL)
16055     return;
16056
16057   gi->checked = state;
16058 }
16059
16060 static void RedrawSoundButtonGadget(int id)
16061 {
16062   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16063              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16064              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16065              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16066              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16067              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16068              id);
16069
16070   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16071   RedrawGadget(game_gadget[id2]);
16072 }
16073
16074 void MapGameButtons(void)
16075 {
16076   MapGameButtonsExt(FALSE);
16077 }
16078
16079 void UnmapGameButtons(void)
16080 {
16081   UnmapGameButtonsExt(FALSE);
16082 }
16083
16084 void RedrawGameButtons(void)
16085 {
16086   RedrawGameButtonsExt(FALSE);
16087 }
16088
16089 void MapGameButtonsOnTape(void)
16090 {
16091   MapGameButtonsExt(TRUE);
16092 }
16093
16094 void UnmapGameButtonsOnTape(void)
16095 {
16096   UnmapGameButtonsExt(TRUE);
16097 }
16098
16099 void RedrawGameButtonsOnTape(void)
16100 {
16101   RedrawGameButtonsExt(TRUE);
16102 }
16103
16104 static void GameUndoRedoExt(void)
16105 {
16106   ClearPlayerAction();
16107
16108   tape.pausing = TRUE;
16109
16110   RedrawPlayfield();
16111   UpdateAndDisplayGameControlValues();
16112
16113   DrawCompleteVideoDisplay();
16114   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16115   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16116   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16117
16118   BackToFront();
16119 }
16120
16121 static void GameUndo(int steps)
16122 {
16123   if (!CheckEngineSnapshotList())
16124     return;
16125
16126   LoadEngineSnapshot_Undo(steps);
16127
16128   GameUndoRedoExt();
16129 }
16130
16131 static void GameRedo(int steps)
16132 {
16133   if (!CheckEngineSnapshotList())
16134     return;
16135
16136   LoadEngineSnapshot_Redo(steps);
16137
16138   GameUndoRedoExt();
16139 }
16140
16141 static void HandleGameButtonsExt(int id, int button)
16142 {
16143   static boolean game_undo_executed = FALSE;
16144   int steps = BUTTON_STEPSIZE(button);
16145   boolean handle_game_buttons =
16146     (game_status == GAME_MODE_PLAYING ||
16147      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16148
16149   if (!handle_game_buttons)
16150     return;
16151
16152   switch (id)
16153   {
16154     case GAME_CTRL_ID_STOP:
16155     case GAME_CTRL_ID_PANEL_STOP:
16156     case GAME_CTRL_ID_TOUCH_STOP:
16157       if (game_status == GAME_MODE_MAIN)
16158         break;
16159
16160       if (tape.playing)
16161         TapeStop();
16162       else
16163         RequestQuitGame(TRUE);
16164
16165       break;
16166
16167     case GAME_CTRL_ID_PAUSE:
16168     case GAME_CTRL_ID_PAUSE2:
16169     case GAME_CTRL_ID_PANEL_PAUSE:
16170     case GAME_CTRL_ID_TOUCH_PAUSE:
16171       if (network.enabled && game_status == GAME_MODE_PLAYING)
16172       {
16173         if (tape.pausing)
16174           SendToServer_ContinuePlaying();
16175         else
16176           SendToServer_PausePlaying();
16177       }
16178       else
16179         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16180
16181       game_undo_executed = FALSE;
16182
16183       break;
16184
16185     case GAME_CTRL_ID_PLAY:
16186     case GAME_CTRL_ID_PANEL_PLAY:
16187       if (game_status == GAME_MODE_MAIN)
16188       {
16189         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16190       }
16191       else if (tape.pausing)
16192       {
16193         if (network.enabled)
16194           SendToServer_ContinuePlaying();
16195         else
16196           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16197       }
16198       break;
16199
16200     case GAME_CTRL_ID_UNDO:
16201       // Important: When using "save snapshot when collecting an item" mode,
16202       // load last (current) snapshot for first "undo" after pressing "pause"
16203       // (else the last-but-one snapshot would be loaded, because the snapshot
16204       // pointer already points to the last snapshot when pressing "pause",
16205       // which is fine for "every step/move" mode, but not for "every collect")
16206       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16207           !game_undo_executed)
16208         steps--;
16209
16210       game_undo_executed = TRUE;
16211
16212       GameUndo(steps);
16213       break;
16214
16215     case GAME_CTRL_ID_REDO:
16216       GameRedo(steps);
16217       break;
16218
16219     case GAME_CTRL_ID_SAVE:
16220       TapeQuickSave();
16221       break;
16222
16223     case GAME_CTRL_ID_LOAD:
16224       TapeQuickLoad();
16225       break;
16226
16227     case SOUND_CTRL_ID_MUSIC:
16228     case SOUND_CTRL_ID_PANEL_MUSIC:
16229       if (setup.sound_music)
16230       { 
16231         setup.sound_music = FALSE;
16232
16233         FadeMusic();
16234       }
16235       else if (audio.music_available)
16236       { 
16237         setup.sound = setup.sound_music = TRUE;
16238
16239         SetAudioMode(setup.sound);
16240
16241         if (game_status == GAME_MODE_PLAYING)
16242           PlayLevelMusic();
16243       }
16244
16245       RedrawSoundButtonGadget(id);
16246
16247       break;
16248
16249     case SOUND_CTRL_ID_LOOPS:
16250     case SOUND_CTRL_ID_PANEL_LOOPS:
16251       if (setup.sound_loops)
16252         setup.sound_loops = FALSE;
16253       else if (audio.loops_available)
16254       {
16255         setup.sound = setup.sound_loops = TRUE;
16256
16257         SetAudioMode(setup.sound);
16258       }
16259
16260       RedrawSoundButtonGadget(id);
16261
16262       break;
16263
16264     case SOUND_CTRL_ID_SIMPLE:
16265     case SOUND_CTRL_ID_PANEL_SIMPLE:
16266       if (setup.sound_simple)
16267         setup.sound_simple = FALSE;
16268       else if (audio.sound_available)
16269       {
16270         setup.sound = setup.sound_simple = TRUE;
16271
16272         SetAudioMode(setup.sound);
16273       }
16274
16275       RedrawSoundButtonGadget(id);
16276
16277       break;
16278
16279     default:
16280       break;
16281   }
16282 }
16283
16284 static void HandleGameButtons(struct GadgetInfo *gi)
16285 {
16286   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16287 }
16288
16289 void HandleSoundButtonKeys(Key key)
16290 {
16291   if (key == setup.shortcut.sound_simple)
16292     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16293   else if (key == setup.shortcut.sound_loops)
16294     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16295   else if (key == setup.shortcut.sound_music)
16296     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16297 }